80ca4b2d9d
- Next.js 16.2.2 + React 19 + TypeScript + Tailwind v4 - Better Auth with email/password and role system (student/curator/admin) - Prisma 7 schema: User, Session, Account, Verification + full LMS model - Role-based dashboards: student /dashboard, curator /curator/dashboard, admin /admin/dashboard - Auth pages: login, register, verify-email - Better Auth API route handler - Middleware for route protection - Docker Compose with PostgreSQL 16 - Seed script with test users (admin/curator/student) - CLAUDE.md and ROADMAP.md project documentation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
267 lines
7.9 KiB
Plaintext
267 lines
7.9 KiB
Plaintext
generator client {
|
|
provider = "prisma-client"
|
|
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[]
|
|
}
|
|
|
|
model Session {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
token String @unique
|
|
expiresAt DateTime
|
|
ipAddress String?
|
|
userAgent 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 Course {
|
|
id String @id @default(cuid())
|
|
slug String @unique
|
|
title String
|
|
description String?
|
|
coverImage String?
|
|
published Boolean @default(false)
|
|
order Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
modules Module[]
|
|
enrollments CourseEnrollment[]
|
|
}
|
|
|
|
model Module {
|
|
id String @id @default(cuid())
|
|
courseId String
|
|
title 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())
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
|
|
|
|
@@id([userId, courseId])
|
|
}
|
|
|
|
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?
|
|
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
|
|
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)
|
|
}
|