Assist_Design/apps/bff/prisma/schema.prisma
barsa d5e22f14f5 feat: add address reconciliation queue service for Salesforce integration
- Implement AddressReconcileQueueService to handle address reconciliation jobs between WHMCS and Salesforce.
- Define job data structure and queue configuration for retries and error handling.
- Add methods for enqueueing reconciliation jobs and retrieving queue health metrics.

feat: create loading components for various services in the portal

- Add loading skeletons for Internet, SIM, VPN, and public services configuration.
- Implement loading states for account-related views including account details, services, and verification settings.
- Introduce loading states for support case details and subscription actions.

feat: implement OTP input component for user verification

- Create OtpInput component to handle 6-digit OTP input with auto-focus and navigation.
- Add LoginOtpStep component for OTP verification during login, including countdown timer and error handling.

feat: define address domain constants for validation

- Establish constants for address field length limits to ensure compliance with WHMCS API constraints.
- Include maximum lengths for address fields and user input fields to maintain data integrity.
2026-02-03 11:48:49 +09:00

290 lines
9.7 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// =============================================================================
// Prisma Schema - Customer Portal BFF
// =============================================================================
// IMPORTANT: When building Docker images, the Prisma client must be regenerated
// from the production directory structure. See prisma/README.md for details.
// =============================================================================
generator client {
provider = "prisma-client-js"
// Only include engines we actually need:
// - native: for local development
// - linux-musl-openssl-3.0.x: for Alpine production
binaryTargets = ["native", "linux-musl-openssl-3.0.x"]
}
datasource db {
provider = "postgresql"
// Prisma 7+: URL is configured via prisma.config.ts for migrations
// and via --url flag for studio. Runtime uses the adapter in PrismaClient.
}
model User {
id String @id @default(uuid())
email String @unique
passwordHash String? @map("password_hash")
role UserRole @default(USER)
// Authentication state only - profile data comes from WHMCS
mfaSecret String? @map("mfa_secret")
emailVerified Boolean @default(false) @map("email_verified")
failedLoginAttempts Int @default(0) @map("failed_login_attempts")
lockedUntil DateTime? @map("locked_until")
lastLoginAt DateTime? @map("last_login_at")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
auditLogs AuditLog[]
idMapping IdMapping?
residenceCardSubmission ResidenceCardSubmission?
notifications Notification[]
@@map("users")
}
model IdMapping {
userId String @id @map("user_id")
whmcsClientId Int @unique @map("whmcs_client_id")
sfAccountId String @map("sf_account_id")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@map("id_mappings")
}
model AuditLog {
id String @id @default(uuid())
userId String? @map("user_id")
action AuditAction
resource String?
details Json?
ipAddress String? @map("ip_address")
userAgent String? @map("user_agent")
success Boolean @default(true)
error String?
createdAt DateTime @default(now()) @map("created_at")
user User? @relation(fields: [userId], references: [id])
@@index([userId, action])
@@index([action, createdAt])
@@index([createdAt])
@@map("audit_logs")
}
enum UserRole {
USER
ADMIN
}
enum AuditAction {
LOGIN_SUCCESS
LOGIN_FAILED
LOGIN_OTP_SENT
LOGIN_OTP_VERIFIED
LOGIN_OTP_FAILED
LOGIN_SESSION_INVALIDATED
LOGOUT
SIGNUP
PASSWORD_RESET
PASSWORD_CHANGE
ACCOUNT_LOCKED
ACCOUNT_UNLOCKED
PROFILE_UPDATE
MFA_ENABLED
MFA_DISABLED
API_ACCESS
SYSTEM_MAINTENANCE
// Address reconciliation (Salesforce dual-write)
ADDRESS_UPDATE
ADDRESS_UPDATE_PARTIAL
ADDRESS_RECONCILE_QUEUED
ADDRESS_RECONCILE_SUCCESS
ADDRESS_RECONCILE_FAILED
}
enum ResidenceCardStatus {
PENDING
VERIFIED
REJECTED
}
model ResidenceCardSubmission {
id String @id @default(uuid())
userId String @unique @map("user_id")
status ResidenceCardStatus @default(PENDING)
filename String
mimeType String @map("mime_type")
sizeBytes Int @map("size_bytes")
content Bytes @db.ByteA
submittedAt DateTime @default(now()) @map("submitted_at")
reviewedAt DateTime? @map("reviewed_at")
reviewerNotes String? @map("reviewer_notes")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@map("residence_card_submissions")
}
// Per-SIM daily usage snapshot used to build full-month charts
model SimUsageDaily {
id Int @id @default(autoincrement())
account String
date DateTime @db.Date
usageMb Float
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@unique([account, date])
@@index([account, date])
@@map("sim_usage_daily")
}
model SimVoiceOptions {
account String @id
voiceMailEnabled Boolean @default(false) @map("voice_mail_enabled")
callWaitingEnabled Boolean @default(false) @map("call_waiting_enabled")
internationalRoamingEnabled Boolean @default(false) @map("international_roaming_enabled")
networkType String @default("4G") @map("network_type")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("sim_voice_options")
}
// Call history from SFTP monthly imports (domestic calls)
model SimCallHistoryDomestic {
id String @id @default(uuid())
account String // Customer phone number (e.g., "08077052946")
callDate DateTime @db.Date @map("call_date") // Date the call was made
callTime String @map("call_time") // Start time of the call (HHMMSS)
calledTo String @map("called_to") // Phone number called
location String? // Location info
durationSec Int @map("duration_sec") // Duration in seconds (320 = 32.0 sec)
chargeYen Int @map("charge_yen") // Call charge in JPY
month String // YYYY-MM format for filtering
createdAt DateTime @default(now()) @map("created_at")
@@unique([account, callDate, callTime, calledTo])
@@index([account, month])
@@index([account, callDate])
@@map("sim_call_history_domestic")
}
// Call history from SFTP monthly imports (international calls)
model SimCallHistoryInternational {
id String @id @default(uuid())
account String // Customer phone number
callDate DateTime @db.Date @map("call_date") // Date the call was made
startTime String @map("start_time") // Start time of the call
stopTime String? @map("stop_time") // Stop time (if available)
country String? // Country/location for international calls
calledTo String @map("called_to") // Phone number called
durationSec Int @map("duration_sec") // Duration in seconds
chargeYen Int @map("charge_yen") // Call charge in JPY
month String // YYYY-MM format for filtering
createdAt DateTime @default(now()) @map("created_at")
@@unique([account, callDate, startTime, calledTo])
@@index([account, month])
@@index([account, callDate])
@@map("sim_call_history_international")
}
// SMS history from SFTP monthly imports
model SimSmsHistory {
id String @id @default(uuid())
account String // Customer phone number
smsDate DateTime @db.Date @map("sms_date") // Date the SMS was sent
smsTime String @map("sms_time") // Time the SMS was sent
sentTo String @map("sent_to") // Phone number SMS was sent to
smsType SmsType @default(DOMESTIC) @map("sms_type") // SMS or 国際SMS
month String // YYYY-MM format for filtering
createdAt DateTime @default(now()) @map("created_at")
@@unique([account, smsDate, smsTime, sentTo])
@@index([account, month])
@@index([account, smsDate])
@@map("sim_sms_history")
}
enum SmsType {
DOMESTIC //
INTERNATIONAL // 国際SMS
}
// Track which months have been imported
model SimHistoryImport {
id String @id @default(uuid())
month String @unique // YYYY-MM format
talkFile String? @map("talk_file") // Filename imported
smsFile String? @map("sms_file") // Filename imported
talkRecords Int @default(0) @map("talk_records") // Records imported
smsRecords Int @default(0) @map("sms_records") // Records imported
importedAt DateTime @default(now()) @map("imported_at")
status String @default("completed") // completed, failed, partial
@@map("sim_history_imports")
}
// =============================================================================
// Notifications - In-app notifications synced with Salesforce email triggers
// =============================================================================
model Notification {
id String @id @default(uuid())
userId String @map("user_id")
// Notification content
type NotificationType
title String
message String?
// Action (optional CTA button)
actionUrl String? @map("action_url")
actionLabel String? @map("action_label")
// Source tracking for deduplication
source NotificationSource @default(SALESFORCE)
sourceId String? @map("source_id") // SF Account ID, Order ID, etc.
// Status
read Boolean @default(false)
readAt DateTime? @map("read_at")
dismissed Boolean @default(false)
// Timestamps
createdAt DateTime @default(now()) @map("created_at")
expiresAt DateTime @map("expires_at") // 30 days from creation
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId, read, dismissed])
@@index([userId, createdAt])
@@index([expiresAt])
@@map("notifications")
}
enum NotificationType {
ELIGIBILITY_ELIGIBLE
ELIGIBILITY_INELIGIBLE
VERIFICATION_VERIFIED
VERIFICATION_REJECTED
ORDER_APPROVED
ORDER_ACTIVATED
ORDER_FAILED
CANCELLATION_SCHEDULED
CANCELLATION_COMPLETE
PAYMENT_METHOD_EXPIRING
INVOICE_DUE
SYSTEM_ANNOUNCEMENT
}
enum NotificationSource {
SALESFORCE
WHMCS
PORTAL
SYSTEM
}