Files
schwarmblog/apps/api/prisma/schema.prisma
Sebastian ab76f207ae feat: initial SchwarmbLog monorepo setup
- React + Vite + TanStack Router/Query frontend
- Hono + Prisma + MySQL backend
- Prisma schema mit allen Entitäten
- Docker + docker-compose Setup
- Tailwind mit Salm/Ozean Farbpalette
2026-03-29 15:19:47 +00:00

261 lines
8.6 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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")
}