f6fcd7ce54
Build & Push / Pipeline Tests (push) Failing after 1m8s
Build & Push / Build & Push Docker Image (push) Has been skipped
Test / Type Check (all packages) (push) Successful in 53s
Test / API Unit Tests (push) Failing after 52s
Test / Homepage Unit Tests (push) Successful in 46s
Test / Carplace Unit Tests (push) Successful in 43s
Test / Admin Unit Tests (push) Successful in 43s
Test / Dashboard Unit Tests (push) Successful in 46s
Test / API Integration Tests (push) Successful in 1m7s
106 lines
5.4 KiB
SQL
106 lines
5.4 KiB
SQL
-- Durable, recipient-safe notification model. The legacy notifications table is
|
|
-- intentionally retained for rollback and audit during the migration window.
|
|
|
|
CREATE TYPE "NotificationRecipientType" AS ENUM ('EMPLOYEE', 'RENTER');
|
|
CREATE TYPE "NotificationDeliveryStatus" AS ENUM ('PENDING', 'QUEUED', 'SENT', 'DELIVERED', 'FAILED', 'SKIPPED', 'DEAD_LETTER');
|
|
CREATE TYPE "NotificationOutboxStatus" AS ENUM ('PENDING', 'PUBLISHED', 'FAILED');
|
|
|
|
CREATE TABLE "notification_events" (
|
|
"id" TEXT NOT NULL,
|
|
"companyId" TEXT NOT NULL,
|
|
"type" "NotificationType" NOT NULL,
|
|
"templateKey" TEXT,
|
|
"locale" TEXT NOT NULL DEFAULT 'en',
|
|
"title" TEXT NOT NULL,
|
|
"body" TEXT NOT NULL,
|
|
"data" JSONB,
|
|
"sourceType" TEXT NOT NULL,
|
|
"sourceId" TEXT NOT NULL,
|
|
"idempotencyKey" TEXT NOT NULL,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "notification_events_pkey" PRIMARY KEY ("id"),
|
|
CONSTRAINT "notification_events_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "companies"("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
);
|
|
|
|
CREATE TABLE "notification_recipients" (
|
|
"id" TEXT NOT NULL,
|
|
"notificationEventId" TEXT NOT NULL,
|
|
"recipientType" "NotificationRecipientType" NOT NULL,
|
|
"employeeId" TEXT,
|
|
"renterId" TEXT,
|
|
"readAt" TIMESTAMP(3),
|
|
"archivedAt" TIMESTAMP(3),
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "notification_recipients_pkey" PRIMARY KEY ("id"),
|
|
CONSTRAINT "notification_recipients_notificationEventId_fkey" FOREIGN KEY ("notificationEventId") REFERENCES "notification_events"("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
|
CONSTRAINT "notification_recipients_employeeId_fkey" FOREIGN KEY ("employeeId") REFERENCES "employees"("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
|
CONSTRAINT "notification_recipients_renterId_fkey" FOREIGN KEY ("renterId") REFERENCES "renters"("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
|
CONSTRAINT "notification_recipients_exactly_one_identity_chk" CHECK (
|
|
(CASE WHEN "employeeId" IS NULL THEN 0 ELSE 1 END) +
|
|
(CASE WHEN "renterId" IS NULL THEN 0 ELSE 1 END) = 1
|
|
),
|
|
CONSTRAINT "notification_recipients_type_identity_chk" CHECK (
|
|
("recipientType" = 'EMPLOYEE' AND "employeeId" IS NOT NULL AND "renterId" IS NULL) OR
|
|
("recipientType" = 'RENTER' AND "renterId" IS NOT NULL AND "employeeId" IS NULL)
|
|
)
|
|
);
|
|
|
|
CREATE TABLE "notification_deliveries" (
|
|
"id" TEXT NOT NULL,
|
|
"notificationRecipientId" TEXT NOT NULL,
|
|
"channel" "NotificationChannel" NOT NULL,
|
|
"status" "NotificationDeliveryStatus" NOT NULL DEFAULT 'PENDING',
|
|
"attemptCount" INTEGER NOT NULL DEFAULT 0,
|
|
"lastAttemptAt" TIMESTAMP(3),
|
|
"nextAttemptAt" TIMESTAMP(3),
|
|
"provider" TEXT,
|
|
"providerMessageId" TEXT,
|
|
"failureCode" TEXT,
|
|
"failureReason" TEXT,
|
|
"preferenceDecision" TEXT,
|
|
"sentAt" TIMESTAMP(3),
|
|
"deliveredAt" TIMESTAMP(3),
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "notification_deliveries_pkey" PRIMARY KEY ("id"),
|
|
CONSTRAINT "notification_deliveries_notificationRecipientId_fkey" FOREIGN KEY ("notificationRecipientId") REFERENCES "notification_recipients"("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
);
|
|
|
|
CREATE TABLE "notification_outbox" (
|
|
"id" TEXT NOT NULL,
|
|
"notificationEventId" TEXT NOT NULL,
|
|
"status" "NotificationOutboxStatus" NOT NULL DEFAULT 'PENDING',
|
|
"payload" JSONB NOT NULL,
|
|
"publishedAt" TIMESTAMP(3),
|
|
"failureReason" TEXT,
|
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "notification_outbox_pkey" PRIMARY KEY ("id"),
|
|
CONSTRAINT "notification_outbox_notificationEventId_fkey" FOREIGN KEY ("notificationEventId") REFERENCES "notification_events"("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
);
|
|
|
|
CREATE TABLE "company_notification_preferences" (
|
|
"id" TEXT NOT NULL,
|
|
"companyId" TEXT NOT NULL,
|
|
"notificationType" "NotificationType" NOT NULL,
|
|
"channel" "NotificationChannel" NOT NULL,
|
|
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
|
|
|
CONSTRAINT "company_notification_preferences_pkey" PRIMARY KEY ("id"),
|
|
CONSTRAINT "company_notification_preferences_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "companies"("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
);
|
|
|
|
CREATE UNIQUE INDEX "notification_events_companyId_idempotencyKey_key" ON "notification_events"("companyId", "idempotencyKey");
|
|
CREATE INDEX "notification_events_companyId_type_createdAt_idx" ON "notification_events"("companyId", "type", "createdAt");
|
|
CREATE INDEX "notification_recipients_employeeId_readAt_createdAt_idx" ON "notification_recipients"("employeeId", "readAt", "createdAt");
|
|
CREATE INDEX "notification_recipients_renterId_readAt_createdAt_idx" ON "notification_recipients"("renterId", "readAt", "createdAt");
|
|
CREATE INDEX "notification_recipients_notificationEventId_idx" ON "notification_recipients"("notificationEventId");
|
|
CREATE UNIQUE INDEX "notification_deliveries_notificationRecipientId_channel_key" ON "notification_deliveries"("notificationRecipientId", "channel");
|
|
CREATE INDEX "notification_deliveries_channel_status_nextAttemptAt_idx" ON "notification_deliveries"("channel", "status", "nextAttemptAt");
|
|
CREATE INDEX "notification_outbox_status_createdAt_idx" ON "notification_outbox"("status", "createdAt");
|
|
CREATE UNIQUE INDEX "company_notification_preferences_companyId_notificationType_channel_key" ON "company_notification_preferences"("companyId", "notificationType", "channel");
|