f4e74b38d4
Without output directive, Prisma 7 generates to node_modules/@prisma/client in the Docker build context, causing the @/generated/prisma/client import to fail with "Module not found". Explicit output ensures generated TypeScript files are always placed at src/generated/prisma/. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
316 lines
9.7 KiB
Plaintext
316 lines
9.7 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
output = "../src/generated/prisma"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// Better Auth core tables
|
|
// ─────────────────────────────────────────────
|
|
|
|
model User {
|
|
id String @id @default(cuid())
|
|
name String
|
|
email String @unique
|
|
emailVerified Boolean @default(false)
|
|
image String?
|
|
role String @default("student") // student | curator | admin
|
|
banned Boolean? @default(false)
|
|
banReason String?
|
|
banExpires DateTime?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
sessions Session[]
|
|
accounts Account[]
|
|
enrollments CourseEnrollment[]
|
|
progress LessonProgress[]
|
|
submissions HomeworkSubmission[]
|
|
comments LessonComment[]
|
|
feedbacks HomeworkFeedback[]
|
|
accessLogs AccessLog[] @relation("AccessLogUser")
|
|
adminAccessLogs AccessLog[] @relation("AccessLogAdmin")
|
|
}
|
|
|
|
model Session {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
token String @unique
|
|
expiresAt DateTime
|
|
ipAddress String?
|
|
userAgent String?
|
|
impersonatedBy String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
model Account {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
accountId String
|
|
providerId String
|
|
accessToken String?
|
|
refreshToken String?
|
|
idToken String?
|
|
accessTokenExpiresAt DateTime?
|
|
refreshTokenExpiresAt DateTime?
|
|
scope String?
|
|
password String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
model Verification {
|
|
id String @id @default(cuid())
|
|
identifier String
|
|
value String
|
|
expiresAt DateTime
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// LMS core tables
|
|
// ─────────────────────────────────────────────
|
|
|
|
model Category {
|
|
id String @id @default(cuid())
|
|
title String
|
|
slug String @unique
|
|
order Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
|
|
courses Course[]
|
|
}
|
|
|
|
model Course {
|
|
id String @id @default(cuid())
|
|
slug String @unique
|
|
title String
|
|
description String?
|
|
coverImage String?
|
|
published Boolean @default(false)
|
|
allowAudio Boolean @default(false)
|
|
order Int @default(0)
|
|
categoryId String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
category Category? @relation(fields: [categoryId], references: [id], onDelete: SetNull)
|
|
modules Module[]
|
|
enrollments CourseEnrollment[]
|
|
accessLogs AccessLog[]
|
|
}
|
|
|
|
model Module {
|
|
id String @id @default(cuid())
|
|
courseId String
|
|
title String
|
|
description String?
|
|
order Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
|
|
lessons Lesson[]
|
|
}
|
|
|
|
model Lesson {
|
|
id String @id @default(cuid())
|
|
moduleId String
|
|
title String
|
|
content Json?
|
|
kinescopeId String?
|
|
order Int @default(0)
|
|
published Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
module Module @relation(fields: [moduleId], references: [id], onDelete: Cascade)
|
|
progress LessonProgress[]
|
|
quiz Quiz?
|
|
homework Homework?
|
|
comments LessonComment[]
|
|
files LessonFile[]
|
|
}
|
|
|
|
model LessonFile {
|
|
id String @id @default(cuid())
|
|
lessonId String
|
|
name String
|
|
url String
|
|
size Int
|
|
createdAt DateTime @default(now())
|
|
|
|
lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
model CourseEnrollment {
|
|
userId String
|
|
courseId String
|
|
enrolledAt DateTime @default(now())
|
|
expiresAt DateTime?
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
|
|
|
|
@@id([userId, courseId])
|
|
}
|
|
|
|
model AccessLog {
|
|
id String @id @default(cuid())
|
|
courseId String
|
|
userId String
|
|
action String // "granted" | "revoked"
|
|
method String @default("manual")
|
|
grantedById String?
|
|
note String?
|
|
createdAt DateTime @default(now())
|
|
|
|
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
|
|
user User @relation("AccessLogUser", fields: [userId], references: [id], onDelete: Cascade)
|
|
grantedBy User? @relation("AccessLogAdmin", fields: [grantedById], references: [id], onDelete: SetNull)
|
|
}
|
|
|
|
model LessonProgress {
|
|
userId String
|
|
lessonId String
|
|
completedAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade)
|
|
|
|
@@id([userId, lessonId])
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// Quizzes
|
|
// ─────────────────────────────────────────────
|
|
|
|
model Quiz {
|
|
id String @id @default(cuid())
|
|
lessonId String @unique
|
|
showAnswers Boolean @default(true)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade)
|
|
questions QuizQuestion[]
|
|
attempts QuizAttempt[]
|
|
}
|
|
|
|
model QuizQuestion {
|
|
id String @id @default(cuid())
|
|
quizId String
|
|
text String
|
|
type QuizQuestionType
|
|
order Int @default(0)
|
|
|
|
quiz Quiz @relation(fields: [quizId], references: [id], onDelete: Cascade)
|
|
options QuizOption[]
|
|
}
|
|
|
|
model QuizOption {
|
|
id String @id @default(cuid())
|
|
questionId String
|
|
text String
|
|
isCorrect Boolean @default(false)
|
|
order Int @default(0)
|
|
|
|
question QuizQuestion @relation(fields: [questionId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
model QuizAttempt {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
quizId String
|
|
score Int
|
|
answers Json
|
|
completedAt DateTime @default(now())
|
|
|
|
quiz Quiz @relation(fields: [quizId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
enum QuizQuestionType {
|
|
SINGLE
|
|
MULTIPLE
|
|
TEXT
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// Homework
|
|
// ─────────────────────────────────────────────
|
|
|
|
model Homework {
|
|
id String @id @default(cuid())
|
|
lessonId String @unique
|
|
description String
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade)
|
|
submissions HomeworkSubmission[]
|
|
}
|
|
|
|
model HomeworkSubmission {
|
|
id String @id @default(cuid())
|
|
homeworkId String
|
|
userId String
|
|
text String?
|
|
files Json?
|
|
status String @default("PENDING") // PENDING | REVIEWING | APPROVED | REJECTED
|
|
statusAt DateTime?
|
|
audioUrl String?
|
|
submittedAt DateTime @default(now())
|
|
|
|
homework Homework @relation(fields: [homeworkId], references: [id], onDelete: Cascade)
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
feedbacks HomeworkFeedback[]
|
|
}
|
|
|
|
model HomeworkFeedback {
|
|
id String @id @default(cuid())
|
|
submissionId String
|
|
curatorId String
|
|
text String
|
|
files Json? // [{name, url, size}]
|
|
audioUrl String?
|
|
createdAt DateTime @default(now())
|
|
|
|
submission HomeworkSubmission @relation(fields: [submissionId], references: [id], onDelete: Cascade)
|
|
curator User @relation(fields: [curatorId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// Comments
|
|
// ─────────────────────────────────────────────
|
|
|
|
model LessonComment {
|
|
id String @id @default(cuid())
|
|
lessonId String
|
|
userId String
|
|
text String
|
|
deleted Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
|
|
lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade)
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
}
|
|
|
|
// ─────────────────────────────────────────────
|
|
// Platform Settings (key-value store)
|
|
// ─────────────────────────────────────────────
|
|
|
|
model Settings {
|
|
key String @id
|
|
value String @db.Text
|
|
updatedAt DateTime @updatedAt
|
|
}
|