Initial project setup: monorepo structure with frontend and backend
- Set up pnpm workspace with frontend (React + Vite) and backend (Express + Prisma) - Configure TypeScript, ESLint, and Prettier - Add Prisma schema for database models (User, Course, Lesson, Progress, etc.) - Create basic frontend structure with Tailwind CSS and shadcn/ui - Add environment configuration files - Update README with project overview and setup instructions - Complete Phase 0: Project initialization
This commit is contained in:
232
packages/backend/prisma/schema.prisma
Normal file
232
packages/backend/prisma/schema.prisma
Normal file
@@ -0,0 +1,232 @@
|
||||
// prisma/schema.prisma
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
previewFeatures = ["fullTextIndex"]
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "mysql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// AUTHENTICATION & USERS
|
||||
// ============================================
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
email String @unique
|
||||
displayName String? @map("display_name")
|
||||
passwordHash String? @map("password_hash")
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user