// SchwarmbLog – Prisma Schema // Stack: Hono + Prisma + MySQL // Conventions: camelCase in Prisma, snake_case in DB, cuid() for IDs generator client { provider = "prisma-client-js" } datasource db { provider = "mysql" url = env("DATABASE_URL") } // ───────────────────────────────────────────── // USERS & AUTH // ───────────────────────────────────────────── model User { id String @id @default(cuid()) name String email String? @unique role Role @default(STUDENT) // Auth passwordHash String? qrToken String? @unique @map("qr_token") qrTokenCreated DateTime? @map("qr_token_created") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") // Relations classMemberships ClassMembership[] swarmMemberships SwarmMembership[] blog Blog? assignedTodos Todo[] @relation("AssignedTodos") reactions Reaction[] comments Comment[] @@map("users") } enum Role { ADMIN TEACHER STUDENT PARENT } // ───────────────────────────────────────────── // CLASSES & SWARMS // ───────────────────────────────────────────── model Class { id String @id @default(cuid()) name String // z.B. "7a" schoolYear String @map("school_year") // z.B. "2025/26" createdAt DateTime @default(now()) @map("created_at") memberships ClassMembership[] swarms Swarm[] blogs Blog[] @@map("classes") } model ClassMembership { id String @id @default(cuid()) user User @relation(fields: [userId], references: [id]) userId String @map("user_id") class Class @relation(fields: [classId], references: [id]) classId String @map("class_id") role ClassRole @default(STUDENT) @@unique([userId, classId]) @@map("class_memberships") } enum ClassRole { TEACHER STUDENT } // Schwarm = Sichtbarkeitsgruppe (Klasse oder ganzer Jahrgang) model Swarm { id String @id @default(cuid()) name String // z.B. "Schwarm 7a" oder "Schwarm Jahrgang 7" scope SwarmScope class Class? @relation(fields: [classId], references: [id]) classId String? @map("class_id") createdAt DateTime @default(now()) @map("created_at") memberships SwarmMembership[] @@map("swarms") } enum SwarmScope { CLASS // nur eine Klasse YEAR // ganzer Jahrgang } model SwarmMembership { id String @id @default(cuid()) swarm Swarm @relation(fields: [swarmId], references: [id]) swarmId String @map("swarm_id") user User @relation(fields: [userId], references: [id]) userId String @map("user_id") @@unique([swarmId, userId]) @@map("swarm_memberships") } // ───────────────────────────────────────────── // STATIONEN & TEMPLATES // ───────────────────────────────────────────── model Station { id String @id @default(cuid()) name String // z.B. "Lagos" country String latitude Float longitude Float bneTopic String @map("bne_topic") // z.B. "Elektroschrott" description String? @db.Text orderIndex Int @map("order_index") // optimale Reihenfolge createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") templates EntryTemplate[] blogEntries BlogEntry[] @@map("stations") } // Vorstrukturierter Eintrag (Lückentext, Bildblock, etc.) // blocks: JSON-Array von Block-Objekten // Beispiel: [{ type: "gap_text", content: "Ich bin in ___ angekommen..." }, { type: "image" }, { type: "free_text" }] model EntryTemplate { id String @id @default(cuid()) station Station @relation(fields: [stationId], references: [id]) stationId String @map("station_id") title String blocks Json // Block-Struktur des Templates isOptional Boolean @default(false) @map("is_optional") orderIndex Int @default(0) @map("order_index") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") todos Todo[] blogEntries BlogEntry[] @@map("entry_templates") } // ───────────────────────────────────────────── // BLOG & EINTRÄGE // ───────────────────────────────────────────── model Blog { id String @id @default(cuid()) student User @relation(fields: [studentId], references: [id]) studentId String @unique @map("student_id") class Class @relation(fields: [classId], references: [id]) classId String @map("class_id") title String @default("Mein Reiseblog") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") entries BlogEntry[] todos Todo[] @@map("blogs") } model BlogEntry { id String @id @default(cuid()) blog Blog @relation(fields: [blogId], references: [id]) blogId String @map("blog_id") station Station? @relation(fields: [stationId], references: [id]) stationId String? @map("station_id") template EntryTemplate? @relation(fields: [templateId], references: [id]) templateId String? @map("template_id") // null = freier Eintrag title String blocks Json // Ausgefüllte Block-Inhalte (Text, Bildpfad, etc.) status EntryStatus @default(DRAFT) visibility Visibility @default(PRIVATE) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") reactions Reaction[] comments Comment[] @@map("blog_entries") } enum EntryStatus { DRAFT PUBLISHED } enum Visibility { PRIVATE // nur Schüler + Lehrkraft CLASS // Klassen-Schwarm YEAR // Jahrgangs-Schwarm PARENTS // zusätzlich Eltern } // ───────────────────────────────────────────── // TODOS // ───────────────────────────────────────────── // Lehrkraft weist Schüler einen vorstrukturierten Eintrag zu model Todo { id String @id @default(cuid()) blog Blog @relation(fields: [blogId], references: [id]) blogId String @map("blog_id") template EntryTemplate @relation(fields: [templateId], references: [id]) templateId String @map("template_id") assignedBy User @relation("AssignedTodos", fields: [assignedById], references: [id]) assignedById String @map("assigned_by") dueDate DateTime? @map("due_date") completedAt DateTime? @map("completed_at") createdAt DateTime @default(now()) @map("created_at") @@map("todos") } // ───────────────────────────────────────────── // SOZIALES // ───────────────────────────────────────────── model Reaction { id String @id @default(cuid()) entry BlogEntry @relation(fields: [entryId], references: [id]) entryId String @map("entry_id") user User @relation(fields: [userId], references: [id]) userId String @map("user_id") type ReactionType @default(LIKE) @@unique([entryId, userId]) // pro Eintrag nur eine Reaktion pro User @@map("reactions") } enum ReactionType { LIKE } model Comment { id String @id @default(cuid()) entry BlogEntry @relation(fields: [entryId], references: [id]) entryId String @map("entry_id") user User @relation(fields: [userId], references: [id]) userId String @map("user_id") text String @db.Text createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@map("comments") }