3bb178b5-949c-4434-a4e8-093caab5d33b

This commit is contained in:
Z User committed 2026-10-29 23:59:48 +00:00
1 parent cd7591fd65
commit 72ec15c2ab
21 files changed
+1991 -130

No files matched your search

+37 -1
View File
@@ -19,9 +19,45 @@ model User {
id String @id @default(cuid())
email String @unique
name String
role String @default("owner") // owner|admin|sales|pm|developer|finance|readonly|client
role String @default("owner") // owner|admin|sales|pm|developer|finance|readonly|client|member
// --- Authentication ---
// scrypt-based password hash (format: "salt:hash", both hex). Null = seeded
// user that hasn't claimed their account via registration yet.
passwordHash String?
emailVerifiedAt DateTime?
lastLoginAt DateTime?
// --- Two-factor authentication (opt-in per user) ---
twoFactorEnabled Boolean @default(false)
// 'email' = OTP sent by mail | 'totp' = Google Authenticator / Authy / 1Password
twoFactorMethod String? // email|totp
// Base32 TOTP secret (stored plaintext for now; encrypt-at-rest later).
// Only set when method='totp'.
twoFactorSecret String?
// Backup codes (hashed, newline-separated) for recovery. Generated on enable.
twoFactorBackup String?
twoFactorEnabledAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
otpCodes OtpCode[]
}
// One-time passcodes (email OTP method). Each login attempt issues a code,
// stored hashed (never plaintext). Codes expire after 5 minutes and are
// single-use (consumedAt set on successful verify).
model OtpCode {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
// SHA-256 hash of the 6-digit code (we never store the code itself).
codeHash String
purpose String @default("login") // login|email-verify|password-reset
attempts Int @default(0)
consumedAt DateTime?
expiresAt DateTime
createdAt DateTime @default(now())
@@index([userId, purpose])
}
// Module registry — installed/enabled/disabled state of each domain module