- Multi-tenant client management with unique API keys - Semaphore SMS integration (per-client key + sender name + credit balance) - DB-based SMS queue with 3-attempt retry and exponential backoff - Failed SMS dashboard with manual retry button - Reports page with date/client filter and CSV export - Admin auth via NextAuth (email/password) - Docker Compose setup (app + PostgreSQL 16) - Prisma 5 schema with SmsQueue, SmsLog, Client, User models Tech: Next.js 14 App Router + TypeScript + Tailwind CSS + Prisma + PostgreSQL
91 lines
1.9 KiB
Plaintext
91 lines
1.9 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(cuid())
|
|
email String @unique
|
|
password String
|
|
name String?
|
|
role Role @default(ADMIN)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
model Client {
|
|
id String @id @default(cuid())
|
|
name String
|
|
slug String @unique
|
|
apiKey String @unique @default(cuid())
|
|
isActive Boolean @default(true)
|
|
semaphoreKey String?
|
|
senderName String? @default("NFC-HUB")
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
smsLogs SmsLog[]
|
|
smsQueue SmsQueue[]
|
|
}
|
|
|
|
model SmsQueue {
|
|
id String @id @default(cuid())
|
|
clientId String
|
|
client Client @relation(fields: [clientId], references: [id])
|
|
studentName String
|
|
studentId String
|
|
parentPhone String
|
|
event EventType
|
|
timestamp DateTime
|
|
message String?
|
|
status SmsStatus @default(PENDING)
|
|
attempts Int @default(0)
|
|
lastError String?
|
|
scheduledAt DateTime @default(now())
|
|
processedAt DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
smsLog SmsLog?
|
|
}
|
|
|
|
model SmsLog {
|
|
id String @id @default(cuid())
|
|
clientId String
|
|
client Client @relation(fields: [clientId], references: [id])
|
|
queueId String @unique
|
|
queue SmsQueue @relation(fields: [queueId], references: [id])
|
|
phone String
|
|
message String
|
|
status LogStatus
|
|
semaphoreId String?
|
|
errorReason String?
|
|
sentAt DateTime @default(now())
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
enum Role {
|
|
ADMIN
|
|
VIEWER
|
|
}
|
|
|
|
enum EventType {
|
|
time_in
|
|
time_out
|
|
}
|
|
|
|
enum SmsStatus {
|
|
PENDING
|
|
PROCESSING
|
|
SENT
|
|
FAILED
|
|
RETRYING
|
|
}
|
|
|
|
enum LogStatus {
|
|
SUCCESS
|
|
FAILED
|
|
}
|