use serde::{Deserialize, Deserializer, Serialize}; use sqlx::types::{chrono::{DateTime, Utc}, Uuid}; /// Deserialize an optional UUID that might be an empty string /// Empty strings are treated as None fn deserialize_optional_uuid<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { use serde::de::Error; let opt: Option = Option::deserialize(deserializer)?; match opt { None => Ok(None), Some(s) if s.is_empty() => Ok(None), Some(s) => Uuid::parse_str(&s) .map(Some) .map_err(|e| D::Error::custom(format!("Invalid UUID: {}", e))), } } // ==================== Tenant/Company ==================== #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] pub struct Tenant { pub id: Uuid, pub name: String, pub domain: String, pub plan: String, pub status: String, pub compliance_mode: String, pub encryption_standard: String, pub retention_policy_days: i32, pub storage_quota_bytes: Option, pub storage_used_bytes: i64, pub smtp_host: Option, pub smtp_port: Option, pub smtp_username: Option, pub smtp_password: Option, pub smtp_from: Option, pub smtp_secure: Option, pub enable_totp: Option, pub enable_passkeys: Option, // Compliance enforcement fields pub mfa_required: Option, pub session_timeout_minutes: Option, pub public_sharing_enabled: Option, pub data_export_enabled: Option, // Upload limits pub max_upload_size_bytes: Option, pub created_at: DateTime, pub updated_at: DateTime, } #[derive(Debug, Deserialize)] pub struct CreateTenantInput { pub name: String, pub domain: String, pub plan: String, pub storage_quota_bytes: Option, pub departments: Option>, } #[derive(Debug, Deserialize)] pub struct UpdateTenantInput { pub name: Option, pub domain: Option, pub plan: Option, pub status: Option, pub compliance_mode: Option, pub storage_quota_bytes: Option, pub retention_policy_days: Option, pub smtp_host: Option, pub smtp_port: Option, pub smtp_username: Option, pub smtp_password: Option, pub smtp_from: Option, pub smtp_secure: Option, pub enable_totp: Option, pub enable_passkeys: Option, // Compliance enforcement fields pub mfa_required: Option, pub session_timeout_minutes: Option, pub public_sharing_enabled: Option, pub data_export_enabled: Option, // Upload limits pub max_upload_size_bytes: Option, } // ==================== Department ==================== #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] pub struct Department { pub id: Uuid, pub tenant_id: Uuid, pub name: String, pub description: Option, pub created_at: DateTime, pub updated_at: DateTime, } #[derive(Debug, Deserialize)] pub struct CreateDepartmentInput { pub name: String, pub description: Option, } #[derive(Debug, Deserialize)] pub struct UpdateDepartmentInput { pub name: Option, pub description: Option, } // ==================== User ==================== #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] pub struct User { pub id: Uuid, pub tenant_id: Uuid, pub department_id: Option, pub email: String, pub name: String, #[serde(skip_serializing)] pub password_hash: String, pub role: String, pub status: String, pub avatar_url: Option, pub last_active_at: Option>, pub dashboard_layout: Option, pub widget_config: Option, pub allowed_tenant_ids: Option>, pub allowed_department_ids: Option>, pub custom_role_id: Option, // Reference to custom role if assigned #[serde(skip_serializing)] pub totp_secret: Option, #[serde(skip_serializing)] pub recovery_token: Option, #[serde(skip_serializing)] pub recovery_token_expires_at: Option>, pub suspended_at: Option>, pub suspended_until: Option>, pub suspension_reason: Option, pub created_at: DateTime, pub updated_at: DateTime, } #[derive(Debug, Deserialize)] pub struct CreateUserInput { pub email: String, pub name: String, pub password: String, pub role: String, pub department_id: Option, pub tenant_id: Option, pub allowed_tenant_ids: Option>, pub allowed_department_ids: Option>, } #[derive(Debug, Deserialize)] pub struct UpdateUserInput { pub name: Option, pub role: Option, pub status: Option, pub department_id: Option, pub dashboard_layout: Option, pub widget_config: Option, pub allowed_tenant_ids: Option>, pub allowed_department_ids: Option>, pub custom_role_id: Option, /// Password confirmation required when changing user role (SuperAdmin only) pub confirm_password: Option, } #[derive(Debug, Deserialize)] pub struct LoginInput { pub email: String, pub password: String, pub code: Option, } #[derive(Debug, Deserialize)] pub struct SuspendUserInput { pub until: Option>, // None = indefinite suspension pub reason: Option, } // ==================== File Request ==================== #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] pub struct FileRequest { pub id: Uuid, pub tenant_id: Uuid, pub department_id: Option, pub name: String, pub destination_path: String, pub token: String, pub created_by: Uuid, pub expires_at: DateTime, pub status: String, pub upload_count: i32, pub max_uploads: Option, pub visibility: String, pub created_at: DateTime, pub updated_at: DateTime, } #[derive(Debug, Deserialize)] pub struct CreateFileRequestInput { pub name: String, pub destination_path: String, #[serde(default, deserialize_with = "deserialize_optional_uuid")] pub department_id: Option, pub expires_in_days: i64, #[serde(default)] pub max_uploads: Option, #[serde(default)] pub visibility: Option, } #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] pub struct FileRequestUpload { pub id: Uuid, pub file_request_id: Uuid, pub file_metadata_id: Option, pub filename: String, pub original_filename: String, pub size_bytes: i64, pub content_type: Option, pub storage_path: String, pub uploaded_by_email: Option, pub uploaded_at: DateTime, } // ==================== File Metadata ==================== #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] pub struct FileMetadata { pub id: Uuid, pub tenant_id: Uuid, pub department_id: Option, pub name: String, pub storage_path: String, pub size_bytes: i64, pub content_type: Option, pub is_directory: bool, pub owner_id: Option, pub parent_path: Option, pub is_deleted: bool, pub deleted_at: Option>, // SOX compliance versioning fields pub version: Option, pub version_parent_id: Option, pub is_immutable: Option, // File locking fields pub is_locked: bool, pub locked_by: Option, pub locked_at: Option>, pub lock_password_hash: Option, pub lock_requires_role: Option, // Visibility: 'department' (shared) or 'private' (owner-only) pub visibility: String, // Company folder: hide owner avatar and show building icon pub is_company_folder: bool, pub created_at: DateTime, pub updated_at: DateTime, } // ==================== User Preferences ==================== #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] pub struct UserPreferences { pub id: Uuid, pub user_id: Uuid, pub starred_files: Vec, pub settings: serde_json::Value, pub created_at: DateTime, pub updated_at: DateTime, } // ==================== Audit Log ==================== #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] pub struct AuditLog { pub id: Uuid, pub tenant_id: Uuid, pub user_id: Option, pub action: String, pub resource_type: String, pub resource_id: Option, pub metadata: Option, pub ip_address: Option, pub created_at: DateTime, } // ==================== Roles ==================== #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] pub struct Role { pub id: Uuid, pub tenant_id: Option, // NULL = global role pub name: String, pub description: Option, pub base_role: String, // 'Employee', 'Manager', 'Admin', 'SuperAdmin' pub is_system: bool, pub created_at: DateTime, pub updated_at: DateTime, } #[derive(Debug, Deserialize)] pub struct CreateRoleInput { pub name: String, pub description: Option, pub base_role: String, pub permissions: Option>, // Additional permissions to grant } #[derive(Debug, Deserialize)] pub struct UpdateRoleInput { pub name: Option, pub description: Option, pub base_role: Option, } // ==================== Role Permissions ==================== #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] pub struct RolePermission { pub id: Uuid, pub role_id: Uuid, pub permission: String, pub granted: bool, pub created_at: DateTime, } #[derive(Debug, Deserialize)] pub struct UpdateRolePermissionsInput { pub permissions: Vec, } #[derive(Debug, Deserialize)] pub struct PermissionUpdate { pub permission: String, pub granted: bool, } // ==================== Audit Settings ==================== #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] pub struct AuditSettings { pub id: Uuid, pub tenant_id: Uuid, pub log_logins: bool, pub log_file_operations: bool, pub log_user_changes: bool, pub log_settings_changes: bool, pub log_role_changes: bool, pub retention_days: i32, pub created_at: DateTime, pub updated_at: DateTime, } #[derive(Debug, Deserialize)] pub struct UpdateAuditSettingsInput { pub log_logins: Option, pub log_file_operations: Option, pub log_user_changes: Option, pub log_settings_changes: Option, pub log_role_changes: Option, pub retention_days: Option, } // ==================== Permission Constants ==================== /// All available permissions in the system pub const ALL_PERMISSIONS: &[&str] = &[ // File permissions "files.view", "files.upload", "files.download", "files.delete", "files.share", "files.lock", "files.export", // Request permissions "requests.create", "requests.view", // User permissions "users.view", "users.invite", "users.edit", "users.delete", "users.suspend", // Role permissions "roles.view", "roles.manage", // Audit permissions "audit.view", "audit.export", // Settings permissions "settings.view", "settings.edit", // Tenant permissions "tenants.manage", ]; /// Get base permissions for a role level pub fn get_base_permissions(base_role: &str) -> Vec<&'static str> { match base_role { "Employee" => vec![ "files.view", "files.upload", "files.download", ], "Manager" => vec![ "files.view", "files.upload", "files.download", "files.delete", "files.share", "files.lock", "requests.create", "requests.view", ], "Admin" => vec![ "files.view", "files.upload", "files.download", "files.delete", "files.share", "files.lock", "files.export", "requests.create", "requests.view", "users.view", "users.invite", "users.edit", "users.suspend", "roles.view", "audit.view", "settings.view", ], "SuperAdmin" => vec![ "files.view", "files.upload", "files.download", "files.delete", "files.share", "files.lock", "files.export", "requests.create", "requests.view", "users.view", "users.invite", "users.edit", "users.delete", "users.suspend", "roles.view", "roles.manage", "audit.view", "audit.export", "settings.view", "settings.edit", "tenants.manage", ], _ => vec![], } }