feat(S2-a): ingest durable app MQTT points securely
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
-- App-path ingest idempotency + provenance (Sprint 2, S2-a; SRS-SYN-2/7, §3.4.2).
|
||||
-- Adds the UUIDv7 idempotency key and forward-compat provenance tags to locate_points.
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "PointOrigin" AS ENUM ('APP', 'LOCATOR', 'MAGLINK');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "PointUploadPath" AS ENUM ('APP_MQTT', 'DEVICE_MQTT', 'REST_BATCH');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "locate_points" ADD COLUMN "pointId" TEXT,
|
||||
ADD COLUMN "origin" "PointOrigin" NOT NULL DEFAULT 'LOCATOR',
|
||||
ADD COLUMN "uploadPath" "PointUploadPath",
|
||||
ADD COLUMN "originClientId" TEXT,
|
||||
ADD COLUMN "createdAt" TIMESTAMPTZ(6);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "locate_points_pointId_key" ON "locate_points"("pointId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "locate_points_jobId_createdAt_idx" ON "locate_points"("jobId", "createdAt");
|
||||
@@ -53,6 +53,22 @@ enum LocateMode {
|
||||
SONDE
|
||||
}
|
||||
|
||||
// Which producer a point originated from (telemetry-schema.md `origin`). Tagged now
|
||||
// for SYN-6 path arbitration; no arbitration LOGIC runs yet (deferred, sprint-2 scope).
|
||||
enum PointOrigin {
|
||||
APP
|
||||
LOCATOR
|
||||
MAGLINK
|
||||
}
|
||||
|
||||
// Which transport carried the point to the cloud (telemetry-schema.md `uploadPath`).
|
||||
// Same forward-compat tagging as PointOrigin.
|
||||
enum PointUploadPath {
|
||||
APP_MQTT
|
||||
DEVICE_MQTT
|
||||
REST_BATCH
|
||||
}
|
||||
|
||||
model Organization {
|
||||
id String @id @default(cuid())
|
||||
name String
|
||||
@@ -185,6 +201,25 @@ model LocatePoint {
|
||||
id BigInt @id @default(autoincrement())
|
||||
jobId String
|
||||
deviceId String?
|
||||
|
||||
// Client-generated UUIDv7, the idempotency key across every upload path
|
||||
// (app MQTT, device-direct MQTT, REST batch — SRS §3.4.2 / telemetry-schema.md).
|
||||
// A replayed UUID is ingested exactly once (unique constraint below). Nullable:
|
||||
// the legacy device-direct path (LogIngest/PointsIngest) predates it and does not
|
||||
// supply one yet; the app path (SYN-2) always does.
|
||||
pointId String? @unique
|
||||
|
||||
// Provenance tags (telemetry-schema.md). Set on the app path now; carried for
|
||||
// forward-compat with SYN-6 arbitration, which does not run yet.
|
||||
origin PointOrigin @default(LOCATOR)
|
||||
uploadPath PointUploadPath?
|
||||
originClientId String?
|
||||
|
||||
// Event time asserted by the producer (record creation, telemetry-schema.md
|
||||
// `createdAt`). Ingest orders and dedups by this, NOT by arrival (`receivedAt`),
|
||||
// so replays and out-of-order delivery converge to the same stored ordering.
|
||||
createdAt DateTime? @db.Timestamptz(6)
|
||||
|
||||
lat Decimal @db.Decimal(10, 8)
|
||||
lng Decimal @db.Decimal(11, 8)
|
||||
altitude Decimal? @db.Decimal(8, 3)
|
||||
@@ -217,7 +252,11 @@ model LocatePoint {
|
||||
job Job @relation(fields: [jobId], references: [id], onDelete: Cascade)
|
||||
device Device? @relation(fields: [deviceId], references: [id], onDelete: SetNull)
|
||||
|
||||
// (jobId, recordedAt) serves GPS-time reads; (jobId, createdAt) serves the
|
||||
// event-time ordering the app path and portal read use (SRS-SYN-2). pointId is
|
||||
// already uniquely indexed above — that index also backs the dedup lookup.
|
||||
@@index([jobId, recordedAt])
|
||||
@@index([jobId, createdAt])
|
||||
@@map("locate_points")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user