feat(notifications): add ZaloOaLinkController + migration + schema — TEC-3065
Include files missed from previous commit: - ZaloOaLinkController (GET /auth/zalo-oa/link, GET /auth/zalo-oa/callback, DELETE) - prisma/schema.prisma — ZaloAccountLink model + User.zaloAccountLink relation - prisma/migrations/20260421010000_add_zalo_account_links/migration.sql - Updated ZaloOaService, webhook controller, notifications module, and specs Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
-- [TEC-3065] Add zalo_account_links table for Zalo OA OAuth account linking.
|
||||
-- Stores per-user OA access/refresh tokens (AES-256-GCM encrypted at app layer)
|
||||
-- and the last interaction timestamp used for the 24-hour ZNS window check.
|
||||
|
||||
CREATE TABLE "zalo_account_links" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"zaloUserId" TEXT NOT NULL,
|
||||
"accessToken" TEXT NOT NULL,
|
||||
"refreshToken" TEXT NOT NULL,
|
||||
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||
"lastInteractAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "zalo_account_links_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- One link per platform user
|
||||
CREATE UNIQUE INDEX "zalo_account_links_userId_key" ON "zalo_account_links"("userId");
|
||||
-- One link per Zalo OA UID
|
||||
CREATE UNIQUE INDEX "zalo_account_links_zaloUserId_key" ON "zalo_account_links"("zaloUserId");
|
||||
|
||||
CREATE INDEX "zalo_account_links_zaloUserId_idx" ON "zalo_account_links"("zaloUserId");
|
||||
CREATE INDEX "zalo_account_links_expiresAt_idx" ON "zalo_account_links"("expiresAt");
|
||||
|
||||
ALTER TABLE "zalo_account_links"
|
||||
ADD CONSTRAINT "zalo_account_links_userId_fkey"
|
||||
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -81,6 +81,7 @@ model User {
|
||||
ownedProjects ProjectDevelopment[] @relation("ProjectOwner")
|
||||
/// KCN do user này vận hành (role=PARK_OPERATOR).
|
||||
ownedIndustrialParks IndustrialPark[] @relation("IndustrialParkOwner")
|
||||
zaloAccountLink ZaloAccountLink?
|
||||
|
||||
@@index([role])
|
||||
@@index([kycStatus])
|
||||
@@ -145,6 +146,30 @@ model OAuthAccount {
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
/// Zalo OA account link — stores the OA-scoped access/refresh tokens for sending
|
||||
/// template messages to a linked user via ZNS.
|
||||
/// Token fields are AES-256-GCM encrypted at the application layer.
|
||||
model ZaloAccountLink {
|
||||
id String @id @default(cuid())
|
||||
userId String @unique
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
/// Zalo user ID scoped to the Official Account (OA UID, not Social Graph UID)
|
||||
zaloUserId String @unique
|
||||
/// AES-256-GCM encrypted access token (base64url: iv.tag.ciphertext)
|
||||
accessToken String
|
||||
/// AES-256-GCM encrypted refresh token (base64url: iv.tag.ciphertext)
|
||||
refreshToken String
|
||||
expiresAt DateTime
|
||||
/// Unix epoch (seconds) of the last user→OA interaction; used for 24-hour window check
|
||||
lastInteractAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([zaloUserId])
|
||||
@@index([expiresAt])
|
||||
@@map("zalo_account_links")
|
||||
}
|
||||
|
||||
model Agent {
|
||||
id String @id @default(cuid())
|
||||
userId String @unique
|
||||
|
||||
Reference in New Issue
Block a user