Files
znakovni.hr/packages/backend/prisma/schema.prisma
johnny2211 3275bc4a4f Add authentication system and admin panel
- Implement JWT-based authentication with login/logout
- Add user management routes and middleware
- Create admin panel for managing words and categories
- Add authentication store and API client
- Update database schema with User model
- Configure CORS and authentication middleware
- Add login page and protected routes
2026-01-17 14:30:22 +01:00

241 lines
6.4 KiB
Plaintext

// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
previewFeatures = ["fullTextIndex"]
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
shadowDatabaseUrl = env("SHADOW_DATABASE_URL")
}
// ============================================
// AUTHENTICATION & USERS
// ============================================
enum UserRole {
ADMIN
USER
}
model User {
id String @id @default(uuid())
email String @unique
displayName String? @map("display_name")
passwordHash String? @map("password_hash")
role UserRole @default(USER)
isActive Boolean @default(true) @map("is_active")
authProvider String @default("local") @map("auth_provider") // local, google, microsoft
providerId String? @map("provider_id")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
documents Document[]
comments Comment[]
bugReports BugReport[]
@@map("users")
}
// ============================================
// DICTIONARY / TERMS
// ============================================
model Term {
id String @id @default(uuid())
wordText String @map("word_text") @db.VarChar(255)
normalizedText String @map("normalized_text") @db.VarChar(255)
language String @default("hr") @db.VarChar(10)
wordType WordType @map("word_type")
cefrLevel CefrLevel @map("cefr_level")
shortDescription String? @map("short_description") @db.Text
tags String? @db.Text // JSON array stored as text
iconAssetId String? @map("icon_asset_id")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
media TermMedia[]
examples TermExample[]
sentenceTokens SentenceToken[]
@@index([normalizedText])
@@fulltext([wordText, normalizedText])
@@map("terms")
}
model TermMedia {
id String @id @default(uuid())
termId String @map("term_id")
kind MediaKind
url String @db.VarChar(500)
durationMs Int? @map("duration_ms")
width Int?
height Int?
checksum String? @db.VarChar(64)
createdAt DateTime @default(now()) @map("created_at")
term Term @relation(fields: [termId], references: [id], onDelete: Cascade)
@@index([termId])
@@map("term_media")
}
model TermExample {
id String @id @default(uuid())
termId String @map("term_id")
exampleText String @map("example_text") @db.Text
notes String? @db.Text
createdAt DateTime @default(now()) @map("created_at")
term Term @relation(fields: [termId], references: [id], onDelete: Cascade)
@@index([termId])
@@map("term_examples")
}
// ============================================
// DOCUMENTS & SENTENCES (ZNAKOPIS)
// ============================================
model Document {
id String @id @default(uuid())
ownerUserId String? @map("owner_user_id")
title String @db.VarChar(255)
description String? @db.Text
visibility Visibility @default(PRIVATE)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
owner User? @relation(fields: [ownerUserId], references: [id], onDelete: SetNull)
pages DocumentPage[]
@@index([ownerUserId])
@@map("documents")
}
model DocumentPage {
id String @id @default(uuid())
documentId String @map("document_id")
pageIndex Int @map("page_index")
title String? @db.VarChar(255)
document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
sentences Sentence[]
@@unique([documentId, pageIndex])
@@index([documentId])
@@map("document_pages")
}
model Sentence {
id String @id @default(uuid())
documentPageId String @map("document_page_id")
sentenceIndex Int @map("sentence_index")
rawText String? @map("raw_text") @db.Text
documentPage DocumentPage @relation(fields: [documentPageId], references: [id], onDelete: Cascade)
tokens SentenceToken[]
@@unique([documentPageId, sentenceIndex])
@@index([documentPageId])
@@map("sentences")
}
model SentenceToken {
id String @id @default(uuid())
sentenceId String @map("sentence_id")
tokenIndex Int @map("token_index")
termId String? @map("term_id")
displayText String @map("display_text") @db.VarChar(255)
isPunctuation Boolean @default(false) @map("is_punctuation")
sentence Sentence @relation(fields: [sentenceId], references: [id], onDelete: Cascade)
term Term? @relation(fields: [termId], references: [id], onDelete: SetNull)
@@unique([sentenceId, tokenIndex])
@@index([sentenceId])
@@index([termId])
@@map("sentence_tokens")
}
// ============================================
// COMMUNITY & SUPPORT
// ============================================
model Comment {
id String @id @default(uuid())
userId String @map("user_id")
content String @db.Text
context String? @db.VarChar(255) // e.g., "term:uuid" or "general"
createdAt DateTime @default(now()) @map("created_at")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@map("comments")
}
model BugReport {
id String @id @default(uuid())
userId String? @map("user_id")
title String @db.VarChar(255)
description String @db.Text
status BugStatus @default(OPEN)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
@@index([userId])
@@index([status])
@@map("bug_reports")
}
// ============================================
// ENUMS
// ============================================
enum WordType {
NOUN
VERB
ADJECTIVE
ADVERB
PRONOUN
PREPOSITION
CONJUNCTION
INTERJECTION
PHRASE
OTHER
}
enum CefrLevel {
A1
A2
B1
B2
C1
C2
}
enum MediaKind {
VIDEO
IMAGE
ILLUSTRATION
}
enum Visibility {
PRIVATE
SHARED
PUBLIC
}
enum BugStatus {
OPEN
IN_PROGRESS
RESOLVED
CLOSED
}