generator client { provider = "prisma-client-js" binaryTargets = ["native", "debian-openssl-3.0.x", "debian-openssl-1.1.x"] } datasource db { provider = "postgresql" url = env("DATABASE_URL") } enum AppStatus { ACTIVE READY DISABLED } enum ComponentType { ELECTRON CONTAINER STATIC_WEB DATABASE MIGRATION } enum JobStatus { PENDING RUNNING SUCCEEDED FAILED CANCELED } enum AdminRole { OWNER ADMIN OPERATOR VIEWER } enum RuntimeAction { MIGRATION RESTART SCALE LOGS EVENTS ROLLBACK STOP } model AdminRoleOption { id String @id @default(uuid()) @db.Uuid code AdminRole @unique name String description String? sortOrder Int @default(0) @map("sort_order") isActive Boolean @default(true) @map("is_active") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) @@index([isActive, sortOrder]) @@map("admin_roles") } model Admin { id String @id @default(uuid()) @db.Uuid email String @unique passwordHash String @map("password_hash") name String role AdminRole @default(ADMIN) isActive Boolean @default(true) @map("is_active") lastLoginAt DateTime? @map("last_login_at") @db.Timestamptz(3) createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) sessions AdminSession[] auditLogs AdminAuditLog[] sshKeys AdminSshKey[] appPermissions AdminAppPermission[] @@index([isActive, role]) @@map("admins") } model AdminAppPermission { id String @id @default(uuid()) @db.Uuid adminId String @map("admin_id") @db.Uuid appId String @map("app_id") @db.Uuid createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) admin Admin @relation(fields: [adminId], references: [id], onDelete: Cascade) app App @relation(fields: [appId], references: [id], onDelete: Cascade) @@unique([adminId, appId]) @@index([appId]) @@map("admin_app_permissions") } model AdminSshKey { id String @id @default(uuid()) @db.Uuid adminId String @map("admin_id") @db.Uuid label String? keyType String @map("key_type") publicKey String @map("public_key") publicKeyHash String @unique @map("public_key_hash") fingerprint String giteaUsername String? @map("gitea_username") giteaKeyId Int? @map("gitea_key_id") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) admin Admin @relation(fields: [adminId], references: [id], onDelete: Cascade) @@index([adminId, createdAt]) @@index([giteaUsername]) @@map("admin_ssh_keys") } model AdminSession { id String @id @default(uuid()) @db.Uuid adminId String @map("admin_id") @db.Uuid tokenHash String @unique @map("token_hash") ipAddress String? @map("ip_address") userAgent String? @map("user_agent") expiresAt DateTime @map("expires_at") @db.Timestamptz(3) revokedAt DateTime? @map("revoked_at") @db.Timestamptz(3) createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) admin Admin @relation(fields: [adminId], references: [id], onDelete: Cascade) @@index([adminId, expiresAt]) @@map("admin_sessions") } model AdminAuditLog { id String @id @default(uuid()) @db.Uuid adminId String? @map("admin_id") @db.Uuid action String resource String resourceId String? @map("resource_id") metadata Json? ipAddress String? @map("ip_address") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) admin Admin? @relation(fields: [adminId], references: [id], onDelete: SetNull) @@index([resource, createdAt]) @@index([adminId, createdAt]) @@map("admin_audit_logs") } model App { id String @id @default(uuid()) @db.Uuid key String @unique name String description String? namespace String? status AppStatus @default(READY) createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) components AppComponent[] releases AppRelease[] deployments DeploymentJob[] runtimeProfiles RuntimeEnvProfile[] adminPermissions AdminAppPermission[] @@map("apps") } model AppComponent { id String @id @default(uuid()) @db.Uuid appId String @map("app_id") @db.Uuid name String type ComponentType imageSource String? @default("SOURCE") @map("image_source") externalImage String? @map("external_image") workloadType String? @default("Deployment") @map("workload_type") replicas Int? @default(1) serviceType String? @default("ClusterIP") @map("service_type") storageEnabled Boolean @default(false) @map("storage_enabled") storageSize String? @map("storage_size") storageMountPath String? @map("storage_mount_path") storageRetain Boolean @default(true) @map("storage_retain") repository String? branch String? buildTarget String? @map("build_target") port Int? targetPort Int? @map("target_port") ports Json? createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) app App @relation(fields: [appId], references: [id], onDelete: Cascade) buildJobs BuildJob[] buildVariables BuildEnvVariable[] runtimeVariables RuntimeEnvVariable[] ingressRules RuntimeIngressRule[] @@unique([appId, name]) @@map("app_components") } model BuildEnvVariable { id String @id @default(uuid()) @db.Uuid componentId String @map("component_id") @db.Uuid key String value String isSecret Boolean @default(false) @map("is_secret") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) component AppComponent @relation(fields: [componentId], references: [id], onDelete: Cascade) @@unique([componentId, key]) @@index([componentId]) @@index([componentId, isSecret]) @@map("build_env_variables") } model BuildJob { id String @id @default(uuid()) @db.Uuid componentId String @map("component_id") @db.Uuid branch String commitSha String? @map("commit_sha") artifact String? image String? status JobStatus @default(PENDING) requestedById String? @map("requested_by_id") @db.Uuid requestedByName String? @map("requested_by_name") requestedByEmail String? @map("requested_by_email") logs String? startedAt DateTime? @map("started_at") @db.Timestamptz(3) finishedAt DateTime? @map("finished_at") @db.Timestamptz(3) releaseCandidateHiddenAt DateTime? @map("release_candidate_hidden_at") @db.Timestamptz(3) createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) component AppComponent @relation(fields: [componentId], references: [id], onDelete: Cascade) @@index([componentId, createdAt]) @@index([releaseCandidateHiddenAt]) @@map("build_jobs") } model AppRelease { id String @id @default(uuid()) @db.Uuid appId String @map("app_id") @db.Uuid version String branch String commitSha String? @map("commit_sha") artifact String? imageTags Json? @map("image_tags") status JobStatus @default(PENDING) requestedById String? @map("requested_by_id") @db.Uuid requestedByName String? @map("requested_by_name") requestedByEmail String? @map("requested_by_email") logs String? createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) app App @relation(fields: [appId], references: [id], onDelete: Cascade) deployments DeploymentJob[] @@unique([appId, version]) @@map("app_releases") } model DeploymentJob { id String @id @default(uuid()) @db.Uuid appId String @map("app_id") @db.Uuid releaseId String? @map("release_id") @db.Uuid environment String namespace String status JobStatus @default(PENDING) requestedBy String? @map("requested_by") requestedById String? @map("requested_by_id") @db.Uuid requestedByName String? @map("requested_by_name") requestedByEmail String? @map("requested_by_email") logs String? startedAt DateTime? @map("started_at") @db.Timestamptz(3) finishedAt DateTime? @map("finished_at") @db.Timestamptz(3) createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) app App @relation(fields: [appId], references: [id], onDelete: Cascade) release AppRelease? @relation(fields: [releaseId], references: [id], onDelete: SetNull) @@index([appId, createdAt]) @@map("deployment_jobs") } model LauncherBuildJob { id String @id @default(uuid()) @db.Uuid target String @default("launcher-client") repository String? branch String @default("release") commitSha String? @map("commit_sha") version String artifact String uploadPath String @map("upload_path") downloadUrl String @map("download_url") status JobStatus @default(PENDING) requestedById String? @map("requested_by_id") @db.Uuid requestedByName String? @map("requested_by_name") requestedByEmail String? @map("requested_by_email") logs String? startedAt DateTime? @map("started_at") @db.Timestamptz(3) finishedAt DateTime? @map("finished_at") @db.Timestamptz(3) createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) @@index([status, createdAt]) @@map("launcher_build_jobs") } model RuntimeControlJob { id String @id @default(uuid()) @db.Uuid namespace String kind String name String action RuntimeAction status JobStatus @default(PENDING) requestedBy String? @map("requested_by") requestedById String? @map("requested_by_id") @db.Uuid requestedByName String? @map("requested_by_name") requestedByEmail String? @map("requested_by_email") request Json? result Json? logs String? startedAt DateTime? @map("started_at") @db.Timestamptz(3) finishedAt DateTime? @map("finished_at") @db.Timestamptz(3) createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) @@index([namespace, kind, name]) @@index([createdAt]) @@map("runtime_control_jobs") } model RuntimeEnvProfile { id String @id @default(uuid()) @db.Uuid appId String @map("app_id") @db.Uuid name String environment String namespace String isActive Boolean @default(true) @map("is_active") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) app App @relation(fields: [appId], references: [id], onDelete: Cascade) variables RuntimeEnvVariable[] ingressRules RuntimeIngressRule[] @@unique([appId, environment, name]) @@index([appId, environment]) @@map("runtime_env_profiles") } model RuntimeEnvVariable { id String @id @default(uuid()) @db.Uuid profileId String @map("profile_id") @db.Uuid componentId String? @map("component_id") @db.Uuid key String value String isSecret Boolean @default(false) @map("is_secret") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) profile RuntimeEnvProfile @relation(fields: [profileId], references: [id], onDelete: Cascade) component AppComponent? @relation(fields: [componentId], references: [id], onDelete: SetNull) @@unique([profileId, componentId, key]) @@index([profileId, isSecret]) @@index([componentId]) @@map("runtime_env_variables") } model RuntimeIngressRule { id String @id @default(uuid()) @db.Uuid profileId String @map("profile_id") @db.Uuid componentId String @map("component_id") @db.Uuid host String path String @default("/") servicePort Int @map("service_port") stripPrefix Boolean @default(false) @map("strip_prefix") tlsEnabled Boolean @default(false) @map("tls_enabled") tlsSecretName String? @map("tls_secret_name") createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) profile RuntimeEnvProfile @relation(fields: [profileId], references: [id], onDelete: Cascade) component AppComponent @relation(fields: [componentId], references: [id], onDelete: Cascade) @@unique([profileId, host, path]) @@index([profileId]) @@index([componentId]) @@map("runtime_ingress_rules") } model PlatformConnection { id String @id @default(uuid()) @db.Uuid name String @unique endpoint String role String status String lastSync DateTime? @map("last_sync") @db.Timestamptz(3) metadata Json? createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) @@index([status]) @@map("platform_connections") } model TailscaleNode { id String @id @default(uuid()) @db.Uuid name String @unique ip String role String? user String? routes String? status String lastSeen DateTime? @map("last_seen") @db.Timestamptz(3) metadata Json? createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) @@index([status, lastSeen]) @@map("tailscale_nodes") } model TailscaleSetting { id String @id @default(uuid()) @db.Uuid key String @unique name String value String description String? status String createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3) updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3) @@index([status]) @@map("tailscale_settings") }