feat: add GPS tracking with Traccar integration

- Add GPS module with Traccar client service for device management
- Add driver enrollment flow with QR code generation
- Add real-time location tracking on driver profiles
- Add GPS settings configuration in admin tools
- Add Auth0 OpenID Connect setup script for Traccar
- Add deployment configs for production server
- Update nginx configs for SSL on GPS port 5055
- Add timezone setting support
- Various UI improvements and bug fixes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-03 18:13:17 +01:00
parent 3814d175ff
commit 5ded039793
91 changed files with 4403 additions and 68 deletions

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "pdf_settings" ADD COLUMN "timezone" TEXT NOT NULL DEFAULT 'America/New_York';

View File

@@ -0,0 +1,71 @@
-- CreateTable
CREATE TABLE "gps_devices" (
"id" TEXT NOT NULL,
"driverId" TEXT NOT NULL,
"traccarDeviceId" INTEGER NOT NULL,
"deviceIdentifier" TEXT NOT NULL,
"enrolledAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"consentGiven" BOOLEAN NOT NULL DEFAULT false,
"consentGivenAt" TIMESTAMP(3),
"lastActive" TIMESTAMP(3),
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "gps_devices_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "gps_location_history" (
"id" TEXT NOT NULL,
"deviceId" TEXT NOT NULL,
"latitude" DOUBLE PRECISION NOT NULL,
"longitude" DOUBLE PRECISION NOT NULL,
"altitude" DOUBLE PRECISION,
"speed" DOUBLE PRECISION,
"course" DOUBLE PRECISION,
"accuracy" DOUBLE PRECISION,
"battery" DOUBLE PRECISION,
"timestamp" TIMESTAMP(3) NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "gps_location_history_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "gps_settings" (
"id" TEXT NOT NULL,
"updateIntervalSeconds" INTEGER NOT NULL DEFAULT 60,
"shiftStartHour" INTEGER NOT NULL DEFAULT 4,
"shiftStartMinute" INTEGER NOT NULL DEFAULT 0,
"shiftEndHour" INTEGER NOT NULL DEFAULT 1,
"shiftEndMinute" INTEGER NOT NULL DEFAULT 0,
"retentionDays" INTEGER NOT NULL DEFAULT 30,
"traccarAdminUser" TEXT NOT NULL DEFAULT 'admin',
"traccarAdminPassword" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "gps_settings_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "gps_devices_driverId_key" ON "gps_devices"("driverId");
-- CreateIndex
CREATE UNIQUE INDEX "gps_devices_traccarDeviceId_key" ON "gps_devices"("traccarDeviceId");
-- CreateIndex
CREATE UNIQUE INDEX "gps_devices_deviceIdentifier_key" ON "gps_devices"("deviceIdentifier");
-- CreateIndex
CREATE INDEX "gps_location_history_deviceId_timestamp_idx" ON "gps_location_history"("deviceId", "timestamp");
-- CreateIndex
CREATE INDEX "gps_location_history_timestamp_idx" ON "gps_location_history"("timestamp");
-- AddForeignKey
ALTER TABLE "gps_devices" ADD CONSTRAINT "gps_devices_driverId_fkey" FOREIGN KEY ("driverId") REFERENCES "drivers"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "gps_location_history" ADD CONSTRAINT "gps_location_history_deviceId_fkey" FOREIGN KEY ("deviceId") REFERENCES "gps_devices"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -115,6 +115,7 @@ model Driver {
events ScheduleEvent[]
assignedVehicle Vehicle? @relation("AssignedDriver")
messages SignalMessage[] // Signal chat messages
gpsDevice GpsDevice? // GPS tracking device
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -281,6 +282,9 @@ model PdfSettings {
showAppUrl Boolean @default(false)
pageSize PageSize @default(LETTER)
// Timezone for correspondence and display (IANA timezone format)
timezone String @default("America/New_York")
// Content Toggles
showFlightInfo Boolean @default(true)
showDriverNames Boolean @default(true)
@@ -303,3 +307,81 @@ enum PageSize {
A4
}
// ============================================
// GPS Tracking
// ============================================
model GpsDevice {
id String @id @default(uuid())
driverId String @unique
driver Driver @relation(fields: [driverId], references: [id], onDelete: Cascade)
// Traccar device information
traccarDeviceId Int @unique // Traccar's internal device ID
deviceIdentifier String @unique // Unique ID for Traccar Client app
// Privacy & Consent
enrolledAt DateTime @default(now())
consentGiven Boolean @default(false)
consentGivenAt DateTime?
lastActive DateTime? // Last location report timestamp
// Settings
isActive Boolean @default(true)
// Location history
locationHistory GpsLocationHistory[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("gps_devices")
}
model GpsLocationHistory {
id String @id @default(uuid())
deviceId String
device GpsDevice @relation(fields: [deviceId], references: [id], onDelete: Cascade)
latitude Float
longitude Float
altitude Float?
speed Float? // km/h
course Float? // Bearing in degrees
accuracy Float? // Meters
battery Float? // Battery percentage (0-100)
timestamp DateTime
createdAt DateTime @default(now())
@@map("gps_location_history")
@@index([deviceId, timestamp])
@@index([timestamp]) // For cleanup job
}
model GpsSettings {
id String @id @default(uuid())
// Update frequency (seconds)
updateIntervalSeconds Int @default(60)
// Shift-based tracking (4AM - 1AM next day)
shiftStartHour Int @default(4) // 4 AM
shiftStartMinute Int @default(0)
shiftEndHour Int @default(1) // 1 AM next day
shiftEndMinute Int @default(0)
// Data retention (days)
retentionDays Int @default(30)
// Traccar credentials
traccarAdminUser String @default("admin")
traccarAdminPassword String? // Encrypted or hashed
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("gps_settings")
}