Add public API keys, outbound webhooks, and bulk SMS campaigns with settings UI and org modules.

This commit is contained in:
2026-09-26 16:12:29 +03:30
parent ccddd009e3
commit ef600d149d
23 changed files with 2434 additions and 5 deletions
+98
View File
@@ -194,6 +194,9 @@ model Organization {
cheques Cheque[]
auditLogs AuditLog[]
savedViews SavedView[]
apiKeys OrganizationApiKey[]
webhookEndpoints WebhookEndpoint[]
smsCampaigns SmsCampaign[]
}
model User {
@@ -1597,3 +1600,98 @@ model SavedView {
@@index([organizationId, scope])
@@index([organizationId, ownerUserId])
}
/// کلید API عمومی برای یکپارچه‌سازی خارجی
model OrganizationApiKey {
id String @id @default(cuid())
organizationId String
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
name String
/// پیشوند قابل‌نمایش (مثلاً mk_ab12)
prefix String
/// SHA-256 هش کلید کامل
keyHash String
lastUsedAt DateTime?
revokedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([keyHash])
@@index([organizationId, revokedAt])
@@index([prefix])
}
/// وب‌هوک خروجی سازمان
model WebhookEndpoint {
id String @id @default(cuid())
organizationId String
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
name String
url String
secret String
events String[] @default([])
isActive Boolean @default(true)
deliveries WebhookDelivery[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([organizationId, isActive])
}
model WebhookDelivery {
id String @id @default(cuid())
organizationId String
endpointId String
endpoint WebhookEndpoint @relation(fields: [endpointId], references: [id], onDelete: Cascade)
event String
payload Json
status String @default("pending")
statusCode Int?
attempts Int @default(0)
lastError String?
nextAttemptAt DateTime @default(now())
deliveredAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([organizationId, createdAt])
@@index([status, nextAttemptAt])
@@index([endpointId, createdAt])
}
/// کمپین پیامک گروهی
model SmsCampaign {
id String @id @default(cuid())
organizationId String
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
name String
body String
/// draft | queued | sending | done | cancelled
status String @default("draft")
scheduledAt DateTime?
totalCount Int @default(0)
sentCount Int @default(0)
failedCount Int @default(0)
skippedCount Int @default(0)
createdById String?
recipients SmsCampaignRecipient[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([organizationId, status, scheduledAt])
}
model SmsCampaignRecipient {
id String @id @default(cuid())
campaignId String
campaign SmsCampaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)
personId String?
mobile String?
/// pending | sent | failed | skipped
status String @default("pending")
error String?
sentAt DateTime?
createdAt DateTime @default(now())
@@index([campaignId, status])
}