Add production-ready SuperAdmin API features for tenants, SMS, servers, and plugins.
Includes mobile auth, get_user/get_version, SMS wallet parity endpoints, appearance backfill, and related Prisma migrations. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+145
-12
@@ -26,9 +26,12 @@ enum SmsLedgerType {
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
email String @unique
|
||||
mobile String @unique
|
||||
firstName String @map("first_name")
|
||||
lastName String @map("last_name")
|
||||
/// Optional legacy/contact email — login uses mobile
|
||||
email String? @unique
|
||||
passwordHash String @map("password_hash")
|
||||
name String
|
||||
role UserRole @default(super_admin)
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
@@ -60,12 +63,16 @@ model Tenant {
|
||||
smsSyncedAt DateTime? @map("sms_synced_at")
|
||||
/// Full Khanegi UI JSON (theme, navigation, pages). Hamkar: unused for now.
|
||||
appUiConfig Json? @map("app_ui_config")
|
||||
/// Client cache-bust / force-reload signal for this sold version (GET /get_version).
|
||||
appVersion String @default("1") @map("app_version")
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
notes String?
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
auditLogs AuditLog[]
|
||||
smsAccount SmsAccount?
|
||||
serverAccount ServerAccount?
|
||||
pluginSubscriptions PluginSubscription[]
|
||||
|
||||
@@index([product])
|
||||
@@index([isActive])
|
||||
@@ -91,6 +98,11 @@ model SmsAccount {
|
||||
panelUsername String? @map("panel_username")
|
||||
panelPassword String? @map("panel_password")
|
||||
panelFrom String? @map("panel_from")
|
||||
/// Deposit instructions shown to tenant managers (parity with Hamkar sms-panel)
|
||||
chargeCard String? @map("charge_card")
|
||||
chargeIban String? @map("charge_iban")
|
||||
chargeHolder String? @map("charge_holder")
|
||||
chargeInstruction String? @map("charge_instruction")
|
||||
extraSettings Json? @map("extra_settings")
|
||||
todayMessages Int @default(0) @map("today_messages")
|
||||
todayParts Int @default(0) @map("today_parts")
|
||||
@@ -101,29 +113,54 @@ model SmsAccount {
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
||||
ledgers SmsLedger[]
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
||||
ledgers SmsLedger[]
|
||||
sendLogs SmsSendLog[]
|
||||
|
||||
@@map("sms_accounts")
|
||||
}
|
||||
|
||||
model SmsLedger {
|
||||
id String @id @default(cuid())
|
||||
smsAccountId String @map("sms_account_id")
|
||||
type SmsLedgerType
|
||||
amountRials Int @map("amount_rials")
|
||||
parts Int?
|
||||
note String?
|
||||
createdById String? @map("created_by_id")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
id String @id @default(cuid())
|
||||
smsAccountId String @map("sms_account_id")
|
||||
type SmsLedgerType
|
||||
amountRials Int @map("amount_rials")
|
||||
balanceAfterRials Int? @map("balance_after_rials")
|
||||
parts Int?
|
||||
note String?
|
||||
createdById String? @map("created_by_id")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
smsAccount SmsAccount @relation(fields: [smsAccountId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([smsAccountId])
|
||||
@@index([createdAt])
|
||||
@@index([type])
|
||||
@@map("sms_ledgers")
|
||||
}
|
||||
|
||||
/// Per-tenant SMS send history (filled when products hook consume; empty until then)
|
||||
model SmsSendLog {
|
||||
id String @id @default(cuid())
|
||||
smsAccountId String @map("sms_account_id")
|
||||
receiver String
|
||||
body String
|
||||
parts Int @default(1)
|
||||
pricePerPartRials Int @map("price_per_part_rials")
|
||||
totalAmountRials Int @map("total_amount_rials")
|
||||
provider String?
|
||||
source String?
|
||||
isAlert Boolean @default(false) @map("is_alert")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
smsAccount SmsAccount @relation(fields: [smsAccountId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([smsAccountId])
|
||||
@@index([createdAt])
|
||||
@@index([provider])
|
||||
@@map("sms_send_logs")
|
||||
}
|
||||
|
||||
model AuditLog {
|
||||
id String @id @default(cuid())
|
||||
action String
|
||||
@@ -144,3 +181,99 @@ model AuditLog {
|
||||
@@index([createdAt])
|
||||
@@map("audit_logs")
|
||||
}
|
||||
|
||||
/// Server hosting status for a sold version.
|
||||
/// isDedicated=true → customer-owned; SuperAdmin only tracks that flag.
|
||||
/// isDedicated=false → we manage charge/renewal/expiry alerts.
|
||||
model ServerAccount {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @unique @map("tenant_id")
|
||||
isDedicated Boolean @default(false) @map("is_dedicated")
|
||||
serverName String? @map("server_name")
|
||||
specs String?
|
||||
/// Price of the current/last plan period (rials)
|
||||
priceRials Int? @map("price_rials")
|
||||
/// Start of current paid period
|
||||
chargedAt DateTime? @map("charged_at")
|
||||
/// Plan length in months: 1 | 3 | 6 | 12
|
||||
planMonths Int? @map("plan_months")
|
||||
expiresAt DateTime? @map("expires_at")
|
||||
description String?
|
||||
/// Days before expiry to start SMS alerts (default 3)
|
||||
alertDaysBefore Int @default(3) @map("alert_days_before")
|
||||
/// Last calendar day an expiry alert was sent (avoid multi-send same day)
|
||||
lastAlertDate DateTime? @map("last_alert_date")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
||||
charges ServerCharge[]
|
||||
|
||||
@@index([expiresAt])
|
||||
@@index([isDedicated])
|
||||
@@map("server_accounts")
|
||||
}
|
||||
|
||||
model ServerCharge {
|
||||
id String @id @default(cuid())
|
||||
serverAccountId String @map("server_account_id")
|
||||
chargedAt DateTime @map("charged_at")
|
||||
planMonths Int @map("plan_months")
|
||||
priceRials Int @map("price_rials")
|
||||
expiresAt DateTime @map("expires_at")
|
||||
note String?
|
||||
createdById String? @map("created_by_id")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
serverAccount ServerAccount @relation(fields: [serverAccountId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([serverAccountId])
|
||||
@@index([createdAt])
|
||||
@@map("server_charges")
|
||||
}
|
||||
|
||||
/// Paid plugin/addon subscription for a sold version (0..N per tenant).
|
||||
model PluginSubscription {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
/// Plugin display name (required; each tenant can have many)
|
||||
name String
|
||||
specs String?
|
||||
priceRials Int? @map("price_rials")
|
||||
chargedAt DateTime? @map("charged_at")
|
||||
/// Plan length in months: 1 | 3 | 6 | 12
|
||||
planMonths Int? @map("plan_months")
|
||||
expiresAt DateTime? @map("expires_at")
|
||||
description String?
|
||||
alertDaysBefore Int @default(3) @map("alert_days_before")
|
||||
lastAlertDate DateTime? @map("last_alert_date")
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
||||
charges PluginCharge[]
|
||||
|
||||
@@index([tenantId])
|
||||
@@index([expiresAt])
|
||||
@@index([isActive])
|
||||
@@map("plugin_subscriptions")
|
||||
}
|
||||
|
||||
model PluginCharge {
|
||||
id String @id @default(cuid())
|
||||
pluginId String @map("plugin_id")
|
||||
chargedAt DateTime @map("charged_at")
|
||||
planMonths Int @map("plan_months")
|
||||
priceRials Int @map("price_rials")
|
||||
expiresAt DateTime @map("expires_at")
|
||||
note String?
|
||||
createdById String? @map("created_by_id")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
plugin PluginSubscription @relation(fields: [pluginId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([pluginId])
|
||||
@@index([createdAt])
|
||||
@@map("plugin_charges")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user