- Implement complete authentication system with JWT token validation - Add auth provider with persistent login state across page refreshes - Create multilingual login/register forms with Material-UI components - Fix token validation using raw SQL queries to bypass Prisma sync issues - Add comprehensive error handling for expired/invalid tokens - Create profile and settings pages with full i18n support - Add proper user role management (admin/user) with database sync - Implement secure middleware with CSRF protection and auth checks - Add debug endpoints for troubleshooting authentication issues - Fix Zustand store persistence for authentication state 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
217 lines
5.7 KiB
Plaintext
217 lines
5.7 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(uuid())
|
|
email String @unique
|
|
passwordHash String
|
|
name String?
|
|
role String @default("user") // "user", "admin", "moderator"
|
|
theme String @default("light")
|
|
fontSize String @default("medium")
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
lastLoginAt DateTime?
|
|
|
|
sessions Session[]
|
|
bookmarks Bookmark[]
|
|
notes Note[]
|
|
chatMessages ChatMessage[]
|
|
prayerRequests PrayerRequest[]
|
|
readingHistory ReadingHistory[]
|
|
preferences UserPreference[]
|
|
|
|
@@index([role])
|
|
}
|
|
|
|
model Session {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
token String @unique
|
|
expiresAt DateTime
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId])
|
|
@@index([token])
|
|
}
|
|
|
|
model BibleVersion {
|
|
id String @id @default(uuid())
|
|
name String // e.g., "King James Version", "Cornilescu"
|
|
abbreviation String // e.g., "KJV", "CORNILESCU", "NIV"
|
|
language String // e.g., "en", "ro", "es"
|
|
description String?
|
|
isDefault Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
books BibleBook[]
|
|
|
|
@@unique([abbreviation, language])
|
|
@@index([language])
|
|
@@index([isDefault])
|
|
}
|
|
|
|
model BibleBook {
|
|
id String @id @default(uuid())
|
|
versionId String
|
|
name String // Version-specific book name
|
|
testament String
|
|
orderNum Int
|
|
bookKey String // For cross-version matching (e.g., "genesis", "exodus")
|
|
chapters BibleChapter[]
|
|
|
|
version BibleVersion @relation(fields: [versionId], references: [id])
|
|
|
|
@@unique([versionId, orderNum])
|
|
@@unique([versionId, bookKey])
|
|
@@index([versionId])
|
|
@@index([testament])
|
|
}
|
|
|
|
model BibleChapter {
|
|
id String @id @default(uuid())
|
|
bookId String
|
|
chapterNum Int
|
|
verses BibleVerse[]
|
|
|
|
book BibleBook @relation(fields: [bookId], references: [id])
|
|
|
|
@@unique([bookId, chapterNum])
|
|
@@index([bookId])
|
|
}
|
|
|
|
model BibleVerse {
|
|
id String @id @default(uuid())
|
|
chapterId String
|
|
verseNum Int
|
|
text String @db.Text
|
|
|
|
chapter BibleChapter @relation(fields: [chapterId], references: [id])
|
|
bookmarks Bookmark[]
|
|
notes Note[]
|
|
|
|
@@unique([chapterId, verseNum])
|
|
@@index([chapterId])
|
|
}
|
|
|
|
model BiblePassage {
|
|
id String @id @default(uuid())
|
|
testament String // 'OT' or 'NT'
|
|
book String
|
|
chapter Int
|
|
verse Int
|
|
ref String // Generated field: "book chapter:verse"
|
|
lang String @default("ro")
|
|
translation String @default("FIDELA")
|
|
textRaw String @db.Text
|
|
textNorm String @db.Text // Normalized text for embedding
|
|
embedding String? // Will be changed to vector later when extension is available
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([translation, lang, book, chapter, verse])
|
|
@@index([book, chapter])
|
|
@@index([testament])
|
|
}
|
|
|
|
model ChatMessage {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
role String // 'user' or 'assistant'
|
|
content String @db.Text
|
|
metadata Json? // Store verse references, etc.
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId, createdAt])
|
|
}
|
|
|
|
model Bookmark {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
verseId String
|
|
note String?
|
|
color String @default("#FFD700")
|
|
createdAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
verse BibleVerse @relation(fields: [verseId], references: [id])
|
|
|
|
@@unique([userId, verseId])
|
|
@@index([userId])
|
|
}
|
|
|
|
model Note {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
verseId String
|
|
content String @db.Text
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
verse BibleVerse @relation(fields: [verseId], references: [id])
|
|
|
|
@@index([userId])
|
|
@@index([verseId])
|
|
}
|
|
|
|
model PrayerRequest {
|
|
id String @id @default(uuid())
|
|
userId String?
|
|
content String @db.Text
|
|
isAnonymous Boolean @default(true)
|
|
prayerCount Int @default(0)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
prayers Prayer[]
|
|
|
|
@@index([createdAt])
|
|
}
|
|
|
|
model Prayer {
|
|
id String @id @default(uuid())
|
|
requestId String
|
|
ipAddress String // For anonymous prayer counting
|
|
createdAt DateTime @default(now())
|
|
|
|
request PrayerRequest @relation(fields: [requestId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([requestId, ipAddress])
|
|
}
|
|
|
|
model ReadingHistory {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
bookId String
|
|
chapterNum Int
|
|
verseNum Int?
|
|
viewedAt DateTime @default(now())
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId, viewedAt])
|
|
}
|
|
|
|
model UserPreference {
|
|
id String @id @default(uuid())
|
|
userId String
|
|
key String
|
|
value String
|
|
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, key])
|
|
} |