generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = env("DATABASE_URL") } model User { id String @id @default(cuid()) email String @unique password String? displayName String avatarUrl String? authProvider String @default("local") // local | oauth role String @default("user") // admin | user refreshToken String? refreshTokenExpiresAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt groups UserGroup[] createdApps App[] boards Board[] @@index([email]) } model Group { id String @id @default(cuid()) name String @unique description String? isDefault Boolean @default(false) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt users UserGroup[] } model UserGroup { id String @id @default(cuid()) userId String groupId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) group Group @relation(fields: [groupId], references: [id], onDelete: Cascade) @@unique([userId, groupId]) @@index([userId]) @@index([groupId]) } model App { id String @id @default(cuid()) name String url String icon String? iconType String @default("lucide") // lucide | simple | url | emoji description String? category String? tags String @default("") // comma-separated healthcheckEnabled Boolean @default(false) healthcheckInterval Int @default(300) // seconds healthcheckMethod String @default("GET") healthcheckExpectedStatus Int @default(200) healthcheckTimeout Int @default(5000) // milliseconds createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull) statuses AppStatus[] widgets Widget[] @@index([name]) @@index([category]) @@index([createdById]) } model AppStatus { id String @id @default(cuid()) appId String status String @default("unknown") // online | offline | degraded | unknown responseTime Int? // milliseconds checkedAt DateTime @default(now()) app App @relation(fields: [appId], references: [id], onDelete: Cascade) @@index([appId]) @@index([checkedAt]) } model Board { id String @id @default(cuid()) name String icon String? description String? isDefault Boolean @default(false) isGuestAccessible Boolean @default(false) backgroundConfig String? // JSON stored as string for SQLite createdById String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt createdBy User? @relation(fields: [createdById], references: [id], onDelete: SetNull) sections Section[] @@index([createdById]) } model Section { id String @id @default(cuid()) boardId String title String icon String? order Int @default(0) isExpandedByDefault Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt board Board @relation(fields: [boardId], references: [id], onDelete: Cascade) widgets Widget[] @@index([boardId]) } model Widget { id String @id @default(cuid()) sectionId String type String // app | bookmark | note | embed | status order Int @default(0) config String @default("{}") // JSON stored as string for SQLite appId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt section Section @relation(fields: [sectionId], references: [id], onDelete: Cascade) app App? @relation(fields: [appId], references: [id], onDelete: SetNull) @@index([sectionId]) @@index([appId]) } model Permission { id String @id @default(cuid()) entityType String // board | app entityId String targetType String // user | group targetId String level String // view | edit | admin createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([entityType, entityId, targetType, targetId]) @@index([entityType, entityId]) @@index([targetType, targetId]) } model SystemSettings { id String @id @default("singleton") authMode String @default("local") // local | oauth | both registrationEnabled Boolean @default(true) oauthClientId String? oauthClientSecret String? oauthDiscoveryUrl String? defaultTheme String @default("dark") defaultPrimaryColor String @default("#6366f1") healthcheckDefaults String @default("{}") // JSON stored as string for SQLite createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }