feat: implement enterprise RBAC, profile identity, system management, and audit stabilization

This commit is contained in:
Austin A
2026-04-17 23:36:07 +01:00
parent 5def26e0df
commit 6279347e4b
28 changed files with 4521 additions and 326 deletions

View File

@@ -302,14 +302,23 @@ model User {
email String @unique
password_hash String
full_name String?
avatar_url String?
profile_metadata Json @default("{}")
role Role @default(VIEWER)
tenant_id String?
is_active Boolean @default(true)
must_change_password Boolean @default(false)
mfa_enabled Boolean @default(false)
mfa_secret String?
mfa_recovery_codes Json @default("[]")
password_changed_at DateTime?
last_login_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
tenant Tenant? @relation(fields: [tenant_id], references: [id], onDelete: SetNull)
auth_sessions AuthSession[]
password_reset_tokens PasswordResetToken[]
@@index([tenant_id])
}
@@ -319,6 +328,11 @@ model Tenant {
name String
slug String @unique
status TenantStatus @default(ACTIVE)
trial_starts_at DateTime?
trial_ends_at DateTime?
trial_grace_ends_at DateTime?
trial_days Int?
trial_locked Boolean @default(false)
plan String @default("starter")
owner_email String
member_emails Json @default("[]")
@@ -351,9 +365,42 @@ model Tenant {
monitoring_alert_events MonitoringAlertEvent[]
@@index([status])
@@index([trial_ends_at])
@@index([owner_email])
}
model AuthSession {
id String @id @default(cuid())
user_id String
refresh_token_hash String @unique
ip_address String?
user_agent String?
issued_at DateTime @default(now())
expires_at DateTime
last_used_at DateTime?
revoked_at DateTime?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
user User @relation(fields: [user_id], references: [id], onDelete: Cascade)
@@index([user_id, revoked_at])
@@index([expires_at])
}
model PasswordResetToken {
id String @id @default(cuid())
user_id String
token_hash String @unique
expires_at DateTime
used_at DateTime?
created_at DateTime @default(now())
user User @relation(fields: [user_id], references: [id], onDelete: Cascade)
@@index([user_id, expires_at])
}
model ProxmoxNode {
id String @id @default(cuid())
name String
@@ -1203,3 +1250,32 @@ model Setting {
created_at DateTime @default(now())
updated_at DateTime @updatedAt
}
model CmsPage {
id String @id @default(cuid())
slug String @unique
title String
section String @default("general")
content Json @default("{}")
is_published Boolean @default(false)
created_by String?
updated_by String?
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@index([section, is_published])
}
model SiteNavigationItem {
id String @id @default(cuid())
label String
href String
position String @default("header")
sort_order Int @default(100)
is_enabled Boolean @default(true)
metadata Json @default("{}")
created_at DateTime @default(now())
updated_at DateTime @updatedAt
@@index([position, sort_order])
}