-
Notifications
You must be signed in to change notification settings - Fork 0
Database
TrackHub utilizes PostgreSQL as its robust data backbone, enhanced with the PostGIS extension to efficiently process and analyze geographical data. This combination empowers the platform to perform spatial queries and complex geolocation tasks with precision and speed, making it an ideal solution for location-based services.
The platform uses two separate databases to enforce domain separation between identity/authorization concerns and business data:
| Database | Purpose | Extensions |
|---|---|---|
| TrackHubSecurity | Identity, users, roles, policies, OAuth clients | — |
| TrackHub | Accounts, assets, positions, geofences | PostGIS |
This database stores all business domain data: accounts, transporters, devices, operators, positions, groups, and geofences.
erDiagram
Account ||--o{ User : "has"
Account ||--o{ Operator : "has"
Account ||--o{ Transporter : "has"
Account ||--o{ Device : "has"
Account ||--o{ "Group" : "has"
Account ||--o| AccountSettings : "configures"
User ||--o{ UserGroup : "belongs to"
"Group" ||--o{ UserGroup : "contains"
"Group" ||--o{ TransporterGroup : "assigns"
Transporter ||--o{ TransporterGroup : "belongs to"
Transporter ||--o| TransporterPosition : "current position"
Transporter }o--|| TransporterType : "classified as"
Device }o--o| Transporter : "installed in"
Device }o--|| Operator : "managed by"
Operator ||--o{ Credential : "authenticates via"
Account {
uuid AccountId PK
string Name
string Description
short Type
bool Active
}
AccountSettings {
uuid AccountId PK
short Maps
int OnlineInterval
int StoringInterval
int RefreshMapInterval
bool GeofencingEnabled
bool TripManagementEnabled
}
User {
uuid UserId PK
string FirstName
string LastName
string Username
bool Active
uuid AccountId FK
}
Operator {
uuid OperatorId PK
string Name
string Description
short ProtocolTypeId
uuid AccountId FK
}
Credential {
uuid CredentialId PK
string Uri
string Username
string Password
string Token
datetime TokenExpiration
string RefreshToken
string Salt
string Key
string Key2
uuid OperatorId FK
}
Transporter {
uuid TransporterId PK
string Name
short TransporterTypeId FK
uuid AccountId FK
}
TransporterType {
short TransporterTypeId PK
string Name
string Description
}
Device {
uuid DeviceId PK
string Name
int Identifier
string Serial
short DeviceTypeId
string Description
uuid TransporterId FK
uuid OperatorId FK
uuid AccountId FK
}
TransporterPosition {
uuid TransporterId PK
double Latitude
double Longitude
double Altitude
double Speed
datetime EventDate
string GeocodeAddress
string DeviceTime
string ServerTime
int Attributes
}
"Group" {
uuid GroupId PK
string Name
string Description
bool Active
uuid AccountId FK
}
UserGroup {
uuid UserId PK
uuid GroupId PK
}
TransporterGroup {
uuid TransporterId PK
uuid GroupId PK
}
The geofencing tables reside in the same database under a dedicated schema, using PostGIS geometry types for spatial operations:
erDiagram
Geofence ||--o{ GeofenceEvent : "triggers"
Transporter ||--o{ GeofenceEvent : "enters/exits"
Geofence {
uuid GeofenceId PK
string Name
string Description
short GeofenceTypeId
geometry Geom
uuid AccountId FK
}
GeofenceEvent {
uuid GeofenceEventId PK
uuid TransporterId FK
uuid GeofenceId FK
datetime DatetimeIn
datetime DatetimeOut
double Latitude
double Longitude
}
The vw_transporter_position view provides user-scoped access to transporter positions by joining through the group membership chain:
transporter_position → transporters → transporter_group → user_group
This ensures each user only sees positions for transporters in their assigned groups.
This database manages authentication, authorization, and the permission model. It implements a flexible RBAC + Policy-based access control system.
erDiagram
User ||--o| Client : "OAuth client"
User ||--o{ UserRole : "assigned"
User ||--o{ UserPolicy : "granted"
Role ||--o{ UserRole : "assigned to"
Role ||--o{ ResourceActionRole : "grants"
Role }o--o| Role : "parent hierarchy"
Policy ||--o{ UserPolicy : "granted to"
Policy ||--o{ ResourceActionPolicy : "defines"
Resource ||--o{ ResourceAction : "has"
Action ||--o{ ResourceAction : "applied to"
ResourceAction ||--o{ ResourceActionPolicy : "controlled by"
ResourceAction ||--o{ ResourceActionRole : "restricted by"
User {
uuid UserId PK
string Username
string Password
bool Active
int LoginAttempts
bool Verified
uuid AccountId
}
Client {
uuid ClientId PK
uuid UserId FK
}
Role {
int RoleId PK
string Name
string Description
int ParentRoleId FK
bool Active
}
Policy {
int PolicyId PK
string Name
string Description
}
Resource {
int ResourceId PK
string ResourceName
string Description
}
Action {
int ActionId PK
string ActionName
string Description
}
ResourceAction {
int ResourceId PK
int ActionId PK
}
ResourceActionPolicy {
int ResourceActionPolicyId PK
int ResourceId FK
int ActionId FK
int PolicyId FK
}
ResourceActionRole {
int ResourceActionRoleId PK
int ResourceId FK
int ActionId FK
int RoleId FK
}
UserPolicy {
int PolicyId PK
uuid UserId PK
}
UserRole {
int RoleId PK
uuid UserId PK
}
When a user attempts an action on a resource, the authorization pipeline resolves permissions through two paths:
graph LR
subgraph Check["Authorization Check"]
req["Request:<br/>Resource + Action"]
end
subgraph PolicyPath["Policy Path"]
up["UserPolicy"]
rap["ResourceActionPolicy"]
end
subgraph RolePath["Role Path"]
ur["UserRole"]
rar["ResourceActionRole"]
end
req --> up --> rap --> grant["✅ Granted"]
req --> ur --> rar --> grant
req -->|Neither path matches| deny["❌ Denied"]
Access is granted if either the policy-based or role-based path resolves to a matching Resource + Action pair.
Tenant isolation is enforced at the database level through the AccountId foreign key:
| Entity | Isolation Column | Strategy |
|---|---|---|
| Users | AccountId |
Direct FK |
| Operators | AccountId |
Direct FK |
| Transporters | AccountId |
Direct FK |
| Devices | AccountId |
Direct FK |
| Groups | AccountId |
Direct FK |
| Geofences | AccountId |
Direct FK |
All queries filter by the authenticated user's AccountId, ensuring complete data isolation between tenants.