project context update

This commit is contained in:
gosangam
2026-05-10 01:12:10 +05:30
parent bf1ff47d3e
commit 4cbb146bc1
+62 -248
View File
@@ -15,10 +15,9 @@ Content is sourced from the pre-1980s Vedabase, extracted and indexed into Mongo
- **shadcn/ui** (base-nova style, lucide icons) — components: Button, Card, Badge, Tooltip, Input, Drawer - **shadcn/ui** (base-nova style, lucide icons) — components: Button, Card, Badge, Tooltip, Input, Drawer
- **next-themes** for dark/light/system mode - **next-themes** for dark/light/system mode
- **vaul** for mobile drawer navigation - **vaul** for mobile drawer navigation
- **Prisma v6** with MongoDB (provider `prisma-client-js`) - **Go BFF API** — all data fetched via `lib/bff.ts` HTTP client calling the Go BFF server (no direct database access)
- **MongoDB** native driver (`mongodb` package) for seeding (Prisma requires replica set for writes) - **framer-motion** for audio player animations (spring enter/exit, minimize/expand transitions, audio wave bars)
- **Python 3** with BeautifulSoup4 + `indic-transliteration` for HTML extraction scripts - **Python 3** with BeautifulSoup4 + `indic-transliteration` for HTML extraction scripts
- **Database**: MongoDB at `DATABASE_URL` env variable with `?authSource=admin`
--- ---
@@ -96,23 +95,21 @@ knowledgebase/
│ ├── logo.tsx → Theme-aware logo component │ ├── logo.tsx → Theme-aware logo component
│ └── theme-provider.tsx → next-themes wrapper │ └── theme-provider.tsx → next-themes wrapper
├── lib/ ├── lib/
│ ├── db.ts → Prisma client singleton │ ├── bff.ts → BFF API client (typed fetch wrapper: bff<T>(), bffList<T>(), bffOrThrow<T>())
│ ├── api.ts → Core data access (books, verses, prose chapters, glossary) │ ├── api.ts → Core data access (books, verses, prose chapters, glossary) — calls BFF API
│ ├── books.ts → Generic book data layer (replaces 29 individual book files)
│ ├── constants.ts → Shared constants (BOOK_NAMES, CC_LILAS) │ ├── constants.ts → Shared constants (BOOK_NAMES, CC_LILAS)
│ ├── og.ts → OG image metadata builders (ogForVerse, ogForLetter, etc.) │ ├── og.ts → OG image metadata builders (ogForVerse, ogForLetter, etc.)
│ ├── text-highlight.ts → Text fragment encode/decode, highlight matching │ ├── text-highlight.ts → Text fragment encode/decode, highlight matching
│ ├── source-reference.ts → Source reference from URL (for copy attribution) │ ├── source-reference.ts → Source reference from URL (for copy attribution)
│ ├── disciples.ts → Disciples data access (by letter, place, year, search) │ ├── disciples.ts → Disciples data access — calls BFF API
│ ├── letters.ts → Letters data access (by year, recipient, location) │ ├── letters.ts → Letters data access (by year, recipient, location) — calls BFF API
│ ├── transcriptions.ts → Transcriptions data access (reads from Prisma/MongoDB) │ ├── transcriptions.ts → Transcriptions data access — calls BFF API
│ ├── utils.ts → cn() helper │ ├── utils.ts → cn() helper
│ ├── [book].ts → Per-book data access layers (nod, tlc, iso, noi, ej, ek, etc.) │ ├── [book].ts → Per-book data access layers (nod, tlc, iso, noi, ej, ek, etc.) — calls BFF API
│ ├── other-documents.ts → Other Documents data access │ ├── other-documents.ts → Other Documents data access — calls BFF API
│ ├── journals.ts → Journals data access │ ├── journals.ts → Journals data access — calls BFF API
│ └── magazines.ts → Magazines data access │ └── magazines.ts → Magazines data access — calls BFF API
├── prisma/
│ └── schema.prisma → All models (Verse, BookMeta, ProseChapter, Essay, Letter,
│ GlossaryEntry, Disciple, Transcription, etc.)
└── public/ └── public/
├── fonts/Noto_Sans/static/ → NotoSans-Regular.ttf + NotoSans-Bold.ttf (OG images) ├── fonts/Noto_Sans/static/ → NotoSans-Regular.ttf + NotoSans-Bold.ttf (OG images)
├── images/ → OG card image (Prabhupāda) ├── images/ → OG card image (Prabhupāda)
@@ -132,232 +129,50 @@ extractor/ → Extraction & seeding tools (not deployed)
--- ---
## Database Schema (Prisma + MongoDB) ## Data Architecture
```prisma The knowledgebase **never connects to MongoDB directly**. All data is served by the **Go BFF API** server.
type Synonym {
word String
meanings String[]
}
model Verse { ### BFF Server
id String @id @default(auto()) @map("_id") @db.ObjectId
book String // "bg" | "sb" | "cc" | "sb1962" | "iso" | "noi"
division String? // null for BG/SB1962/ISO/NOI; canto for SB; lila for CC
chapter Int
verse Int
verseEnd Int?
reference String
bookTitle String
devanagari String
bengali String? // only CC has Bengali
transliteration String
synonyms Synonym[]
translation String
purport String[]
@@unique([book, division, chapter, verse]) - **URL**: Configured via `BFF_URL` environment variable (default: `http://localhost:8080`)
@@index([book]) - The BFF reads from MongoDB — the knowledgebase frontend has no database driver or connection string
@@index([book, division]) - All API endpoints live under `/api/v1/...` on the BFF server
@@index([book, division, chapter])
@@map("verses")
}
model BookMeta { ### BFF Client (`lib/bff.ts`)
id String @id @default(auto()) @map("_id") @db.ObjectId
book String @unique
data Json
@@map("book_meta") The typed fetch wrapper provides three helpers:
}
model ProseChapter { - **`bff<T>(path, options?)`** — Fetch JSON from the BFF. Returns `null` on 404, throws `BffError` on other failures.
id String @id @default(auto()) @map("_id") @db.ObjectId - **`bffList<T>(path, options?)`** — Fetch a list. Returns `[]` on 404 (instead of null).
book String - **`bffOrThrow<T>(path, options?)`** — Fetch JSON, throwing on 404. Use when a missing resource should be an error.
volume Int // 0 = no volumes (sentinel for non-volume books)
chapter Int? // null for intro segments
title String
type String // "chapter" | "intro-segment" | "conclusion"
slug String
order Int
paragraphs String[]
@@unique([book, volume, slug]) Options include `params` (query parameters), `revalidate` (Next.js ISR seconds, default 60), and `tags` (cache tags).
@@index([book])
@@index([book, volume])
@@index([book, volume, type])
@@map("prose_chapters")
}
model Essay { ### Response Envelope
id String @id @default(auto()) @map("_id") @db.ObjectId
slug String @unique
title String
author String
year Int?
group String? // e.g., "two-essays"
order Int
paragraphs String[]
@@index([group]) BFF responses use a `{ success: true, data: ... }` envelope. The `bff()` client auto-unwraps this — callers receive the inner `data` value directly.
@@map("essays")
}
model OtherDocumentMeta { ### Case Mapping
id String @id @default(auto()) @map("_id") @db.ObjectId
book String @unique
data Json
@@map("other_document_meta") The BFF uses **snake_case** JSON field names (Go convention). Each frontend data layer file (`lib/letters.ts`, `lib/transcriptions.ts`, `lib/books.ts`, etc.) maps BFF responses to **camelCase** TypeScript types used by React components.
}
model OtherDocumentChapter { ### Data Layer Files
id String @id @default(auto()) @map("_id") @db.ObjectId
book String
chapter Int
title String
type String
slug String
order Int
paragraphs String[]
@@unique([book, slug]) Individual `lib/*.ts` files are thin wrappers that:
@@index([book]) 1. Call a BFF endpoint via `bff()` / `bffList()` / `bffOrThrow()`
@@map("other_document_chapters") 2. Map the snake_case response to camelCase frontend types
} 3. Export typed functions consumed by Next.js server components
model JournalMeta { Key data layers and their BFF endpoint prefixes:
id String @id @default(auto()) @map("_id") @db.ObjectId - `lib/api.ts` → `/api/v1/books/*`, `/api/v1/glossary/*` — Books, verses, prose chapters, glossary
book String @unique - `lib/books.ts` → `/api/v1/books/{code}/*` — Generic book data layer (replaces 29 individual per-book files)
data Json - `lib/letters.ts` → `/api/v1/letters/*` — Letters (by year, recipient, location)
- `lib/transcriptions.ts` → `/api/v1/transcriptions/*` — Transcriptions (by year, place, type)
@@map("journal_meta") - `lib/disciples.ts` → `/api/v1/disciples/*` — Disciples directory
} - `lib/other-documents.ts` → `/api/v1/other-documents/*` — Other Documents
- `lib/journals.ts` → `/api/v1/journals/*` — Journals
model JournalChapter { - `lib/magazines.ts` → `/api/v1/magazines/*` — Magazines
id String @id @default(auto()) @map("_id") @db.ObjectId
book String
chapter Int
title String
type String
slug String
order Int
paragraphs String[]
@@unique([book, slug])
@@index([book])
@@map("journal_chapters")
}
model MagazineMeta {
id String @id @default(auto()) @map("_id") @db.ObjectId
book String @unique
data Json
@@map("magazine_meta")
}
model MagazineIssue {
id String @id @default(auto()) @map("_id") @db.ObjectId
book String
volume Int
part Int
title String
slug String
order Int
paragraphs String[]
paragraphsJson String? // JSON string fallback for large issues (BSON wire protocol workaround)
@@unique([book, slug])
@@index([book])
@@index([book, volume])
@@map("magazine_issues")
}
model Letter {
id String @id @default(auto()) @map("_id") @db.ObjectId
slug String @unique
recipients String[] // array of individual recipient names
recipientDisplay String // original combined string for display
date String?
dateRef String?
year Int?
month Int?
day Int?
location String?
salutation String?
paragraphs String[]
@@index([year])
@@index([recipients])
@@index([location])
@@index([year, month])
@@map("letters")
}
model GlossaryEntry {
id String @id @default(auto()) @map("_id") @db.ObjectId
term String
definition String
definitionHtml String
@@unique([term])
@@map("glossary")
}
model Disciple {
id String @id @default(auto()) @map("_id") @db.ObjectId
initiatedName String
suffix String // "Dasa" | "Devi Dasi" | "Sw"
initiationDate DateTime? // 1st of the month (exact day unknown unless verified)
initiationPlace String?
karmiName String? // pre-initiation (birth) name
avatar String? // URL to profile photo
about String? // biographical notes
tags String[]
isVerified Boolean @default(false)
@@index([initiatedName])
@@index([initiationPlace])
@@index([initiationDate])
@@index([suffix])
@@index([tags])
@@map("disciples")
}
type ContentBlock {
text String
format String @default("paragraph")
alignment String @default("left")
speaker String?
}
model Transcription {
id String @id @default(auto()) @map("_id") @db.ObjectId
slug String @unique
name String
type String // "lecture" | "conversation"
date String?
year Int?
month Int?
day Int?
place String?
context String?
reference String?
summary String?
content ContentBlock[]
tags String[]
participants String[]
language String @default("en")
source String?
durationMinutes Int?
audioUrl String?
isPublished Boolean @default(false)
notes String?
@@map("transcriptions")
}
```
--- ---
@@ -503,7 +318,7 @@ Seed script: `npx tsx scripts/seed-disciples.ts`
**Schema notes**: **Schema notes**:
- `recipients` is an array of individual names (e.g., `["Mukunda", "Janaki"]`) - `recipients` is an array of individual names (e.g., `["Mukunda", "Janaki"]`)
- `recipientDisplay` is the original combined string (e.g., `"Mukunda, Janaki"`) - `recipientDisplay` is the original combined string (e.g., `"Mukunda, Janaki"`)
- Searching for a recipient uses Prisma `{ has: name }` to match any letter where that person appears - Searching for a recipient filters via the BFF endpoint (matches any letter where that person appears)
- Multi-recipient groups are split by `,`, `;`, ` and `, ` & ` - Multi-recipient groups are split by `,`, `;`, ` and `, ` & `
- Trailing punctuation (`:`, `,`, `.`) cleaned from names - Trailing punctuation (`:`, `,`, `.`) cleaned from names
@@ -518,9 +333,7 @@ Seed script: `npx tsx scripts/seed-disciples.ts`
- **Audio**: 99.1% have linked audio recordings from Cloudflare R2 (`audioUrl` field) - **Audio**: 99.1% have linked audio recordings from Cloudflare R2 (`audioUrl` field)
- **Empty content**: 30 transcriptions (Hindi/Bengali lectures) have no English text but may have audio - **Empty content**: 30 transcriptions (Hindi/Bengali lectures) have no English text but may have audio
Data layer: `lib/transcriptions.ts` (reads from Prisma/MongoDB, NOT local JSON files) Data layer: `lib/transcriptions.ts` (calls BFF API, NOT local JSON files or direct MongoDB)
Seed script: `cd knowledgebase && npx tsx scripts/seed-transcriptions.ts`
### Other ### Other
@@ -771,7 +584,10 @@ SSR and DS have parent chapters with sub-chapters stored as individual `prose_ch
A site-wide floating audio player for transcription recordings (and future music library). Built with framer-motion for animations. A site-wide floating audio player for transcription recordings (and future music library). Built with framer-motion for animations.
- **Desktop**: Centered floating bar (max-width 680px) with cover image, track info, transport controls (prev/play/next), volume slider, source link, close button - **Desktop**: Centered floating bar (max-width 680px) with cover image, track info, transport controls (prev/play/next), volume slider, source link, close button
- **Mobile**: Auto-minimizes to a floating pill (cover image with circular progress ring and audio wave animation) after 8 seconds of inactivity. Tap to expand. Full player shows two rows: track info row + centered transport controls - **Mobile**: Full player shows two rows: track info row + centered transport controls
- **Auto-minimize**: After 8 seconds of inactivity on **both mobile and desktop**, the player auto-minimizes to a floating pill. The minimized pill shows cover image with a circular progress ring and audio wave animation. Tap/click to expand.
- **X button behavior**: Minimizes the player if audio is playing; stops playback entirely if audio is paused
- **Clicking outside the player**: Minimizes it (does not stop playback)
- **Z-index**: `z-40` — below text selection toolbar (z-80), drawers (z-90), modals (z-100) - **Z-index**: `z-40` — below text selection toolbar (z-80), drawers (z-90), modals (z-100)
- **Hidden when selection toolbar is active** on mobile to avoid overlap - **Hidden when selection toolbar is active** on mobile to avoid overlap
- **Forced dark surface** (`bg-[#1c1c1e]`) for accessibility contrast regardless of theme - **Forced dark surface** (`bg-[#1c1c1e]`) for accessibility contrast regardless of theme
@@ -799,8 +615,9 @@ The `AudioPlayerProvider` wraps the entire app in `layout.tsx`. The `FloatingPla
## Environment Variables ## Environment Variables
``` ```
DATABASE_URL=mongodb://user:pass@host:port/spsays?authSource=admin BFF_URL=http://localhost:8080 # Go BFF API server URL (default: http://localhost:8080)
NEXT_PUBLIC_SITE_URL=https://prabhupadasays.org ALLOW_RTF=true # Enable RTF viewer (development only)
NEXT_PUBLIC_SITE_URL=https://prabhupadasays.org # Public site URL for OG images
``` ```
--- ---
@@ -808,21 +625,20 @@ NEXT_PUBLIC_SITE_URL=https://prabhupadasays.org
## How to Run ## How to Run
```bash ```bash
# Development (from knowledgebase/) # 1. Start the Go BFF server (required — all data comes from the BFF)
npm run dev cd spsays-bff && go run cmd/api/main.go # Starts on :8080 by default
# Prisma # 2. Development (from knowledgebase/)
npx prisma generate # Regenerate Prisma client after schema changes npm run dev # Next.js dev server with Turbopack
# Build # 3. Build
npx next build npx next build
# Extraction & seeding scripts are in extractor/ — see extractor/scripts/ # Extraction & seeding scripts are in extractor/ — see extractor/scripts/
# BFF API server (from ../spsays-bff/)
make run # Build + start Go API on :8080
``` ```
> **Note**: No `npx prisma generate`, `npx prisma db push`, or MongoDB connection string needed in the frontend. The BFF server must be running for any page to load data.
--- ---
## Verse JSON Schema (BG/SB/CC/SB1962/ISO/NOI) ## Verse JSON Schema (BG/SB/CC/SB1962/ISO/NOI)
@@ -946,7 +762,7 @@ Content block formats: `"paragraph"` (body text), `"verse"` (centered italic), `
## Known Issues ## Known Issues
1. **MongoDB BSON wire corruption**: The MongoDB server occasionally corrupts large BSON documents. Workaround: seed using `insertOne` + `$push` for paragraphs one at a time. 1. **BFF dependency** — The knowledgebase requires the Go BFF server to be running; all pages will fail to load data if the BFF is down.
2. **SB verse numbering**: 5 duplicate verse reference tags in the HTML. Fixed by using the TEXT N header as authoritative. 2. **SB verse numbering**: 5 duplicate verse reference tags in the HTML. Fixed by using the TEXT N header as authoritative.
@@ -956,10 +772,8 @@ Content block formats: `"paragraph"` (body text), `"verse"` (centered italic), `
5. **CSS zoom for font scaling**: `zoom` property works in Chrome/Safari/Edge but may not work in older Firefox (< 126). A `transform: scale()` fallback is provided via `@supports not (zoom: 1.1)`. 5. **CSS zoom for font scaling**: `zoom` property works in Chrome/Safari/Edge but may not work in older Firefox (< 126). A `transform: scale()` fallback is provided via `@supports not (zoom: 1.1)`.
6. **Password protection requires ACCESS_KEY at runtime**: The `PasswordGate` component checks the server at runtime via `/api/verify-access`. The env var must be available when the server is running, not just at build time. 6. **Transcription BSON fallback** — 1 document stores content as `contentJson` string due to BSON wire protocol limits; `getTranscription()` transparently parses it
7. **Transcription BSON fallback** — 1 document stores content as `contentJson` string due to BSON wire protocol limits; `getTranscription()` transparently parses it 7. **Empty transcriptions** — 30 entries have no English text content (Hindi/Bengali recordings); the UI shows "No transcription available" with the audio player still accessible
8. **Empty transcriptions** — 30 entries have no English text content (Hindi/Bengali recordings); the UI shows "No transcription available" with the audio player still accessible 8. **Audio player z-index** — Player is hidden when text selection toolbar is active on mobile to avoid overlap
9. **Audio player z-index** — Player is hidden when text selection toolbar is active on mobile to avoid overlap