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:
dwindown
2025-10-11 14:06:55 +07:00
parent 249f3a9d7d
commit c3bc181063
11 changed files with 1052 additions and 37 deletions

View File

@@ -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;