feat: add admin dashboard schema and seeder
- Add Plan, Subscription, Payment, PaymentMethod, Coupon models - Add ApiKey, Webhook models for API access - Add AppConfig model for dynamic configuration - Add role, suspendedAt fields to User model - Create comprehensive seeder with: - Admin user (dwindi.ramadhana@gmail.com) - Default plans (Free, Pro Monthly, Pro Yearly) - Payment methods (BCA, Mandiri, GoPay) - App config (maintenance mode) - Zero data loss migration strategy
This commit is contained in:
@@ -0,0 +1,316 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "public"."User" ADD COLUMN "lastLoginAt" TIMESTAMP(3),
|
||||
ADD COLUMN "role" TEXT NOT NULL DEFAULT 'user',
|
||||
ADD COLUMN "suspendedAt" TIMESTAMP(3),
|
||||
ADD COLUMN "suspendedReason" TEXT;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."Plan" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"slug" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"price" DECIMAL(10,2) NOT NULL,
|
||||
"currency" TEXT NOT NULL DEFAULT 'IDR',
|
||||
"durationType" TEXT NOT NULL,
|
||||
"durationDays" INTEGER,
|
||||
"trialDays" INTEGER NOT NULL DEFAULT 7,
|
||||
"features" JSONB NOT NULL,
|
||||
"badge" TEXT,
|
||||
"badgeColor" TEXT,
|
||||
"highlightColor" TEXT,
|
||||
"sortOrder" INTEGER NOT NULL DEFAULT 0,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
"isVisible" BOOLEAN NOT NULL DEFAULT true,
|
||||
"isFeatured" BOOLEAN NOT NULL DEFAULT false,
|
||||
"maxWallets" INTEGER,
|
||||
"maxGoals" INTEGER,
|
||||
"maxTeamMembers" INTEGER,
|
||||
"apiEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"apiRateLimit" INTEGER,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Plan_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."Subscription" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"planId" TEXT NOT NULL,
|
||||
"status" TEXT NOT NULL,
|
||||
"startDate" TIMESTAMP(3) NOT NULL,
|
||||
"endDate" TIMESTAMP(3) NOT NULL,
|
||||
"isTrialUsed" BOOLEAN NOT NULL DEFAULT false,
|
||||
"trialEndDate" TIMESTAMP(3),
|
||||
"cancelledAt" TIMESTAMP(3),
|
||||
"cancellationReason" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Subscription_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."Payment" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"subscriptionId" TEXT,
|
||||
"invoiceNumber" TEXT NOT NULL,
|
||||
"amount" DECIMAL(10,2) NOT NULL,
|
||||
"currency" TEXT NOT NULL DEFAULT 'IDR',
|
||||
"method" TEXT NOT NULL,
|
||||
"tripayReference" TEXT,
|
||||
"tripayFee" DECIMAL(10,2),
|
||||
"totalAmount" DECIMAL(10,2) NOT NULL,
|
||||
"paymentChannel" TEXT,
|
||||
"paymentUrl" TEXT,
|
||||
"qrUrl" TEXT,
|
||||
"status" TEXT NOT NULL,
|
||||
"proofImageUrl" TEXT,
|
||||
"transferDate" TIMESTAMP(3),
|
||||
"verifiedBy" TEXT,
|
||||
"verifiedAt" TIMESTAMP(3),
|
||||
"rejectionReason" TEXT,
|
||||
"couponId" TEXT,
|
||||
"discountAmount" DECIMAL(10,2),
|
||||
"notes" TEXT,
|
||||
"expiresAt" TIMESTAMP(3),
|
||||
"paidAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Payment_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."PaymentMethod" (
|
||||
"id" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"provider" TEXT NOT NULL,
|
||||
"accountName" TEXT NOT NULL,
|
||||
"accountNumber" TEXT NOT NULL,
|
||||
"displayName" TEXT NOT NULL,
|
||||
"logoUrl" TEXT,
|
||||
"instructions" TEXT,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
"sortOrder" INTEGER NOT NULL DEFAULT 0,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "PaymentMethod_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."Coupon" (
|
||||
"id" TEXT NOT NULL,
|
||||
"code" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"discountType" TEXT NOT NULL,
|
||||
"discountValue" DECIMAL(10,2) NOT NULL,
|
||||
"maxDiscount" DECIMAL(10,2),
|
||||
"validFrom" TIMESTAMP(3) NOT NULL,
|
||||
"validUntil" TIMESTAMP(3) NOT NULL,
|
||||
"maxUses" INTEGER,
|
||||
"usedCount" INTEGER NOT NULL DEFAULT 0,
|
||||
"minPurchase" DECIMAL(10,2),
|
||||
"applicablePlans" TEXT[],
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Coupon_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."AppConfig" (
|
||||
"id" TEXT NOT NULL,
|
||||
"key" TEXT NOT NULL,
|
||||
"value" TEXT NOT NULL,
|
||||
"category" TEXT NOT NULL,
|
||||
"label" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"type" TEXT NOT NULL,
|
||||
"isSecret" BOOLEAN NOT NULL DEFAULT false,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
"updatedBy" TEXT,
|
||||
|
||||
CONSTRAINT "AppConfig_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."ApiKey" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"keyHash" TEXT NOT NULL,
|
||||
"prefix" TEXT NOT NULL,
|
||||
"scopes" TEXT[],
|
||||
"lastUsedAt" TIMESTAMP(3),
|
||||
"expiresAt" TIMESTAMP(3),
|
||||
"revokedAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "ApiKey_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."ApiKeyUsage" (
|
||||
"id" TEXT NOT NULL,
|
||||
"apiKeyId" TEXT NOT NULL,
|
||||
"endpoint" TEXT NOT NULL,
|
||||
"method" TEXT NOT NULL,
|
||||
"statusCode" INTEGER NOT NULL,
|
||||
"responseTime" INTEGER NOT NULL,
|
||||
"timestamp" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "ApiKeyUsage_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."Webhook" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"url" TEXT NOT NULL,
|
||||
"events" TEXT[],
|
||||
"secret" TEXT NOT NULL,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
"lastTriggeredAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Webhook_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "public"."WebhookDelivery" (
|
||||
"id" TEXT NOT NULL,
|
||||
"webhookId" TEXT NOT NULL,
|
||||
"event" TEXT NOT NULL,
|
||||
"payload" JSONB NOT NULL,
|
||||
"status" TEXT NOT NULL,
|
||||
"statusCode" INTEGER,
|
||||
"response" TEXT,
|
||||
"attempts" INTEGER NOT NULL DEFAULT 0,
|
||||
"nextRetryAt" TIMESTAMP(3),
|
||||
"deliveredAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "WebhookDelivery_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Plan_slug_key" ON "public"."Plan"("slug");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Plan_slug_idx" ON "public"."Plan"("slug");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Plan_isActive_idx" ON "public"."Plan"("isActive");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Plan_isVisible_idx" ON "public"."Plan"("isVisible");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Plan_sortOrder_idx" ON "public"."Plan"("sortOrder");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Subscription_userId_key" ON "public"."Subscription"("userId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Subscription_userId_idx" ON "public"."Subscription"("userId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Subscription_status_idx" ON "public"."Subscription"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Subscription_endDate_idx" ON "public"."Subscription"("endDate");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Payment_invoiceNumber_key" ON "public"."Payment"("invoiceNumber");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Payment_tripayReference_key" ON "public"."Payment"("tripayReference");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Payment_userId_idx" ON "public"."Payment"("userId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Payment_status_idx" ON "public"."Payment"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Payment_invoiceNumber_idx" ON "public"."Payment"("invoiceNumber");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Payment_tripayReference_idx" ON "public"."Payment"("tripayReference");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "PaymentMethod_isActive_idx" ON "public"."PaymentMethod"("isActive");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "PaymentMethod_sortOrder_idx" ON "public"."PaymentMethod"("sortOrder");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Coupon_code_key" ON "public"."Coupon"("code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Coupon_code_idx" ON "public"."Coupon"("code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Coupon_isActive_idx" ON "public"."Coupon"("isActive");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "AppConfig_key_key" ON "public"."AppConfig"("key");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "AppConfig_category_idx" ON "public"."AppConfig"("category");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "ApiKey_keyHash_key" ON "public"."ApiKey"("keyHash");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ApiKey_userId_idx" ON "public"."ApiKey"("userId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ApiKey_keyHash_idx" ON "public"."ApiKey"("keyHash");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ApiKeyUsage_apiKeyId_timestamp_idx" ON "public"."ApiKeyUsage"("apiKeyId", "timestamp");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Webhook_userId_idx" ON "public"."Webhook"("userId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "WebhookDelivery_webhookId_idx" ON "public"."WebhookDelivery"("webhookId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "WebhookDelivery_status_idx" ON "public"."WebhookDelivery"("status");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."Subscription" ADD CONSTRAINT "Subscription_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."Subscription" ADD CONSTRAINT "Subscription_planId_fkey" FOREIGN KEY ("planId") REFERENCES "public"."Plan"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."Payment" ADD CONSTRAINT "Payment_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."Payment" ADD CONSTRAINT "Payment_subscriptionId_fkey" FOREIGN KEY ("subscriptionId") REFERENCES "public"."Subscription"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."Payment" ADD CONSTRAINT "Payment_couponId_fkey" FOREIGN KEY ("couponId") REFERENCES "public"."Coupon"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."ApiKey" ADD CONSTRAINT "ApiKey_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."ApiKeyUsage" ADD CONSTRAINT "ApiKeyUsage_apiKeyId_fkey" FOREIGN KEY ("apiKeyId") REFERENCES "public"."ApiKey"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."Webhook" ADD CONSTRAINT "Webhook_userId_fkey" FOREIGN KEY ("userId") REFERENCES "public"."User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "public"."WebhookDelivery" ADD CONSTRAINT "WebhookDelivery_webhookId_fkey" FOREIGN KEY ("webhookId") REFERENCES "public"."Webhook"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -26,12 +26,22 @@ model User {
|
||||
otpWhatsappEnabled Boolean @default(false)
|
||||
otpTotpEnabled Boolean @default(false)
|
||||
otpTotpSecret String?
|
||||
// Admin fields
|
||||
role String @default("user") // "user" | "admin"
|
||||
suspendedAt DateTime?
|
||||
suspendedReason String?
|
||||
lastLoginAt DateTime?
|
||||
// Relations
|
||||
authAccounts AuthAccount[]
|
||||
categories Category[]
|
||||
Recurrence Recurrence[]
|
||||
sessions Session[]
|
||||
transactions Transaction[]
|
||||
wallets Wallet[]
|
||||
subscriptions Subscription[]
|
||||
payments Payment[]
|
||||
apiKeys ApiKey[]
|
||||
webhooks Webhook[]
|
||||
}
|
||||
|
||||
model AuthAccount {
|
||||
@@ -129,3 +139,224 @@ model CurrencyRate {
|
||||
@@unique([base, quote, at])
|
||||
@@index([base, quote])
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// SUBSCRIPTION & PAYMENT MODELS
|
||||
// ============================================
|
||||
|
||||
model Plan {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
slug String @unique
|
||||
description String?
|
||||
price Decimal @db.Decimal(10, 2)
|
||||
currency String @default("IDR")
|
||||
durationType String // "monthly" | "yearly" | "lifetime" | "custom"
|
||||
durationDays Int?
|
||||
trialDays Int @default(7)
|
||||
features Json
|
||||
badge String?
|
||||
badgeColor String?
|
||||
highlightColor String?
|
||||
sortOrder Int @default(0)
|
||||
isActive Boolean @default(true)
|
||||
isVisible Boolean @default(true)
|
||||
isFeatured Boolean @default(false)
|
||||
maxWallets Int?
|
||||
maxGoals Int?
|
||||
maxTeamMembers Int?
|
||||
apiEnabled Boolean @default(false)
|
||||
apiRateLimit Int?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
subscriptions Subscription[]
|
||||
|
||||
@@index([slug])
|
||||
@@index([isActive])
|
||||
@@index([isVisible])
|
||||
@@index([sortOrder])
|
||||
}
|
||||
|
||||
model Subscription {
|
||||
id String @id @default(uuid())
|
||||
userId String @unique
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
planId String
|
||||
plan Plan @relation(fields: [planId], references: [id])
|
||||
status String // "active" | "expired" | "cancelled" | "grace_period"
|
||||
startDate DateTime
|
||||
endDate DateTime
|
||||
isTrialUsed Boolean @default(false)
|
||||
trialEndDate DateTime?
|
||||
cancelledAt DateTime?
|
||||
cancellationReason String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
payments Payment[]
|
||||
|
||||
@@index([userId])
|
||||
@@index([status])
|
||||
@@index([endDate])
|
||||
}
|
||||
|
||||
model Payment {
|
||||
id String @id @default(uuid())
|
||||
userId String
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
subscriptionId String?
|
||||
subscription Subscription? @relation(fields: [subscriptionId], references: [id])
|
||||
invoiceNumber String @unique
|
||||
amount Decimal @db.Decimal(10, 2)
|
||||
currency String @default("IDR")
|
||||
method String
|
||||
tripayReference String? @unique
|
||||
tripayFee Decimal? @db.Decimal(10, 2)
|
||||
totalAmount Decimal @db.Decimal(10, 2)
|
||||
paymentChannel String?
|
||||
paymentUrl String?
|
||||
qrUrl String?
|
||||
status String
|
||||
proofImageUrl String?
|
||||
transferDate DateTime?
|
||||
verifiedBy String?
|
||||
verifiedAt DateTime?
|
||||
rejectionReason String?
|
||||
couponId String?
|
||||
coupon Coupon? @relation(fields: [couponId], references: [id])
|
||||
discountAmount Decimal? @db.Decimal(10, 2)
|
||||
notes String?
|
||||
expiresAt DateTime?
|
||||
paidAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([userId])
|
||||
@@index([status])
|
||||
@@index([invoiceNumber])
|
||||
@@index([tripayReference])
|
||||
}
|
||||
|
||||
model PaymentMethod {
|
||||
id String @id @default(uuid())
|
||||
type String
|
||||
provider String
|
||||
accountName String
|
||||
accountNumber String
|
||||
displayName String
|
||||
logoUrl String?
|
||||
instructions String?
|
||||
isActive Boolean @default(true)
|
||||
sortOrder Int @default(0)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([isActive])
|
||||
@@index([sortOrder])
|
||||
}
|
||||
|
||||
model Coupon {
|
||||
id String @id @default(uuid())
|
||||
code String @unique
|
||||
name String
|
||||
description String?
|
||||
discountType String
|
||||
discountValue Decimal @db.Decimal(10, 2)
|
||||
maxDiscount Decimal? @db.Decimal(10, 2)
|
||||
validFrom DateTime
|
||||
validUntil DateTime
|
||||
maxUses Int?
|
||||
usedCount Int @default(0)
|
||||
minPurchase Decimal? @db.Decimal(10, 2)
|
||||
applicablePlans String[]
|
||||
isActive Boolean @default(true)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
payments Payment[]
|
||||
|
||||
@@index([code])
|
||||
@@index([isActive])
|
||||
}
|
||||
|
||||
model AppConfig {
|
||||
id String @id @default(uuid())
|
||||
key String @unique
|
||||
value String
|
||||
category String
|
||||
label String
|
||||
description String?
|
||||
type String
|
||||
isSecret Boolean @default(false)
|
||||
updatedAt DateTime @updatedAt
|
||||
updatedBy String?
|
||||
|
||||
@@index([category])
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// API & WEBHOOK MODELS
|
||||
// ============================================
|
||||
|
||||
model ApiKey {
|
||||
id String @id @default(uuid())
|
||||
userId String
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
name String
|
||||
keyHash String @unique
|
||||
prefix String
|
||||
scopes String[]
|
||||
lastUsedAt DateTime?
|
||||
expiresAt DateTime?
|
||||
revokedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
usage ApiKeyUsage[]
|
||||
|
||||
@@index([userId])
|
||||
@@index([keyHash])
|
||||
}
|
||||
|
||||
model ApiKeyUsage {
|
||||
id String @id @default(uuid())
|
||||
apiKeyId String
|
||||
apiKey ApiKey @relation(fields: [apiKeyId], references: [id], onDelete: Cascade)
|
||||
endpoint String
|
||||
method String
|
||||
statusCode Int
|
||||
responseTime Int
|
||||
timestamp DateTime @default(now())
|
||||
|
||||
@@index([apiKeyId, timestamp])
|
||||
}
|
||||
|
||||
model Webhook {
|
||||
id String @id @default(uuid())
|
||||
userId String
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
url String
|
||||
events String[]
|
||||
secret String
|
||||
isActive Boolean @default(true)
|
||||
lastTriggeredAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
deliveries WebhookDelivery[]
|
||||
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
model WebhookDelivery {
|
||||
id String @id @default(uuid())
|
||||
webhookId String
|
||||
webhook Webhook @relation(fields: [webhookId], references: [id], onDelete: Cascade)
|
||||
event String
|
||||
payload Json
|
||||
status String
|
||||
statusCode Int?
|
||||
response String?
|
||||
attempts Int @default(0)
|
||||
nextRetryAt DateTime?
|
||||
deliveredAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([webhookId])
|
||||
@@index([status])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user