Files
biblical-guide.com/prisma/schema.prisma
Andrei 95070e5369 Add comprehensive page management system to admin dashboard
Features added:
- Database schema for pages and media files with content types (Rich Text, HTML, Markdown)
- Admin API routes for full page CRUD operations
- Image upload functionality with file management
- Rich text editor using TinyMCE with image insertion
- Admin interface for creating/editing pages with SEO options
- Dynamic navigation and footer integration
- Public page display routes with proper SEO metadata
- Support for featured images and content excerpts

Admin features:
- Create/edit/delete pages with rich content editor
- Upload and manage images through media library
- Configure pages to appear in navigation or footer
- Set page status (Draft, Published, Archived)
- SEO title and description management
- Real-time preview of content changes

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-24 07:26:25 +00:00

350 lines
9.5 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[]
chapterBookmarks ChapterBookmark[]
notes Note[]
chatMessages ChatMessage[]
chatConversations ChatConversation[]
prayerRequests PrayerRequest[]
userPrayers UserPrayer[]
readingHistory ReadingHistory[]
preferences UserPreference[]
createdPages Page[] @relation("PageCreator")
updatedPages Page[] @relation("PageUpdater")
uploadedFiles MediaFile[]
@@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?
country String?
englishTitle String?
flagImageUrl String?
zipFileUrl 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[]
chapterBookmarks ChapterBookmark[]
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 ChatConversation {
id String @id @default(uuid())
userId String? // Optional for anonymous users
title String // Auto-generated from first message
language String // 'ro' or 'en'
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
lastMessageAt DateTime @default(now())
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
messages ChatMessage[]
@@index([userId, language, lastMessageAt])
@@index([isActive, lastMessageAt])
}
model ChatMessage {
id String @id @default(uuid())
conversationId String
userId String? // Keep for backward compatibility
role ChatMessageRole
content String @db.Text
metadata Json? // Store verse references, etc.
timestamp DateTime @default(now())
conversation ChatConversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([conversationId, timestamp])
@@index([userId, timestamp])
}
enum ChatMessageRole {
USER
ASSISTANT
SYSTEM
}
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 ChapterBookmark {
id String @id @default(uuid())
userId String
bookId String
chapterNum Int
note String?
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
book BibleBook @relation(fields: [bookId], references: [id])
@@unique([userId, bookId, chapterNum])
@@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?
title String
description String @db.Text
category String // personal, family, health, work, ministry, world
author String // Display name (can be "Anonymous" or user's name)
isAnonymous Boolean @default(false)
prayerCount Int @default(0)
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
prayers Prayer[]
userPrayers UserPrayer[]
@@index([createdAt])
@@index([category])
@@index([isActive])
}
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 UserPrayer {
id String @id @default(uuid())
userId String
requestId String
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
request PrayerRequest @relation(fields: [requestId], references: [id], onDelete: Cascade)
@@unique([userId, requestId])
@@index([userId])
@@index([requestId])
}
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])
}
model Page {
id String @id @default(uuid())
title String
slug String @unique
content String @db.Text
contentType PageContentType @default(RICH_TEXT)
excerpt String? @db.Text
featuredImage String?
seoTitle String?
seoDescription String?
status PageStatus @default(DRAFT)
showInNavigation Boolean @default(false)
showInFooter Boolean @default(false)
navigationOrder Int?
footerOrder Int?
createdBy String
updatedBy String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
publishedAt DateTime?
creator User @relation("PageCreator", fields: [createdBy], references: [id])
updater User @relation("PageUpdater", fields: [updatedBy], references: [id])
@@index([slug])
@@index([status])
@@index([showInNavigation, navigationOrder])
@@index([showInFooter, footerOrder])
}
model MediaFile {
id String @id @default(uuid())
filename String
originalName String
mimeType String
size Int
path String
url String
alt String?
uploadedBy String
createdAt DateTime @default(now())
uploader User @relation(fields: [uploadedBy], references: [id])
@@index([uploadedBy])
@@index([mimeType])
}
enum PageContentType {
RICH_TEXT
HTML
MARKDOWN
}
enum PageStatus {
DRAFT
PUBLISHED
ARCHIVED
}