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