From 4cbb146bc166f8209d5be8f5b5476f89da969602 Mon Sep 17 00:00:00 2001 From: gosangam Date: Sun, 10 May 2026 01:12:10 +0530 Subject: [PATCH] project context update --- PROJECT_CONTEXT.md | 310 +++++++++------------------------------------ 1 file changed, 62 insertions(+), 248 deletions(-) diff --git a/PROJECT_CONTEXT.md b/PROJECT_CONTEXT.md index 51358c92..fd72882e 100644 --- a/PROJECT_CONTEXT.md +++ b/PROJECT_CONTEXT.md @@ -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 - **next-themes** for dark/light/system mode - **vaul** for mobile drawer navigation -- **Prisma v6** with MongoDB (provider `prisma-client-js`) -- **MongoDB** native driver (`mongodb` package) for seeding (Prisma requires replica set for writes) +- **Go BFF API** — all data fetched via `lib/bff.ts` HTTP client calling the Go BFF server (no direct database access) +- **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 -- **Database**: MongoDB at `DATABASE_URL` env variable with `?authSource=admin` --- @@ -96,23 +95,21 @@ knowledgebase/ │ ├── logo.tsx → Theme-aware logo component │ └── theme-provider.tsx → next-themes wrapper ├── lib/ -│ ├── db.ts → Prisma client singleton -│ ├── api.ts → Core data access (books, verses, prose chapters, glossary) +│ ├── bff.ts → BFF API client (typed fetch wrapper: bff(), bffList(), bffOrThrow()) +│ ├── 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) │ ├── og.ts → OG image metadata builders (ogForVerse, ogForLetter, etc.) │ ├── text-highlight.ts → Text fragment encode/decode, highlight matching │ ├── source-reference.ts → Source reference from URL (for copy attribution) -│ ├── disciples.ts → Disciples data access (by letter, place, year, search) -│ ├── letters.ts → Letters data access (by year, recipient, location) -│ ├── transcriptions.ts → Transcriptions data access (reads from Prisma/MongoDB) +│ ├── disciples.ts → Disciples data access — calls BFF API +│ ├── letters.ts → Letters data access (by year, recipient, location) — calls BFF API +│ ├── transcriptions.ts → Transcriptions data access — calls BFF API │ ├── utils.ts → cn() helper -│ ├── [book].ts → Per-book data access layers (nod, tlc, iso, noi, ej, ek, etc.) -│ ├── other-documents.ts → Other Documents data access -│ ├── journals.ts → Journals data access -│ └── magazines.ts → Magazines data access -├── prisma/ -│ └── schema.prisma → All models (Verse, BookMeta, ProseChapter, Essay, Letter, -│ GlossaryEntry, Disciple, Transcription, 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 — calls BFF API +│ ├── journals.ts → Journals data access — calls BFF API +│ └── magazines.ts → Magazines data access — calls BFF API └── public/ ├── fonts/Noto_Sans/static/ → NotoSans-Regular.ttf + NotoSans-Bold.ttf (OG images) ├── images/ → OG card image (Prabhupāda) @@ -132,232 +129,50 @@ extractor/ → Extraction & seeding tools (not deployed) --- -## Database Schema (Prisma + MongoDB) +## Data Architecture -```prisma -type Synonym { - word String - meanings String[] -} +The knowledgebase **never connects to MongoDB directly**. All data is served by the **Go BFF API** server. -model Verse { - 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[] +### BFF Server - @@unique([book, division, chapter, verse]) - @@index([book]) - @@index([book, division]) - @@index([book, division, chapter]) - @@map("verses") -} +- **URL**: Configured via `BFF_URL` environment variable (default: `http://localhost:8080`) +- The BFF reads from MongoDB — the knowledgebase frontend has no database driver or connection string +- All API endpoints live under `/api/v1/...` on the BFF server -model BookMeta { - id String @id @default(auto()) @map("_id") @db.ObjectId - book String @unique - data Json +### BFF Client (`lib/bff.ts`) - @@map("book_meta") -} +The typed fetch wrapper provides three helpers: -model ProseChapter { - id String @id @default(auto()) @map("_id") @db.ObjectId - book String - 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[] +- **`bff(path, options?)`** — Fetch JSON from the BFF. Returns `null` on 404, throws `BffError` on other failures. +- **`bffList(path, options?)`** — Fetch a list. Returns `[]` on 404 (instead of null). +- **`bffOrThrow(path, options?)`** — Fetch JSON, throwing on 404. Use when a missing resource should be an error. - @@unique([book, volume, slug]) - @@index([book]) - @@index([book, volume]) - @@index([book, volume, type]) - @@map("prose_chapters") -} +Options include `params` (query parameters), `revalidate` (Next.js ISR seconds, default 60), and `tags` (cache tags). -model Essay { - 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[] +### Response Envelope - @@index([group]) - @@map("essays") -} +BFF responses use a `{ success: true, data: ... }` envelope. The `bff()` client auto-unwraps this — callers receive the inner `data` value directly. -model OtherDocumentMeta { - id String @id @default(auto()) @map("_id") @db.ObjectId - book String @unique - data Json +### Case Mapping - @@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 { - id String @id @default(auto()) @map("_id") @db.ObjectId - book String - chapter Int - title String - type String - slug String - order Int - paragraphs String[] +### Data Layer Files - @@unique([book, slug]) - @@index([book]) - @@map("other_document_chapters") -} +Individual `lib/*.ts` files are thin wrappers that: +1. Call a BFF endpoint via `bff()` / `bffList()` / `bffOrThrow()` +2. Map the snake_case response to camelCase frontend types +3. Export typed functions consumed by Next.js server components -model JournalMeta { - id String @id @default(auto()) @map("_id") @db.ObjectId - book String @unique - data Json - - @@map("journal_meta") -} - -model JournalChapter { - 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") -} -``` +Key data layers and their BFF endpoint prefixes: +- `lib/api.ts` → `/api/v1/books/*`, `/api/v1/glossary/*` — Books, verses, prose chapters, glossary +- `lib/books.ts` → `/api/v1/books/{code}/*` — Generic book data layer (replaces 29 individual per-book files) +- `lib/letters.ts` → `/api/v1/letters/*` — Letters (by year, recipient, location) +- `lib/transcriptions.ts` → `/api/v1/transcriptions/*` — Transcriptions (by year, place, type) +- `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 +- `lib/magazines.ts` → `/api/v1/magazines/*` — Magazines --- @@ -503,7 +318,7 @@ Seed script: `npx tsx scripts/seed-disciples.ts` **Schema notes**: - `recipients` is an array of individual names (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 `, ` & ` - 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) - **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) - -Seed script: `cd knowledgebase && npx tsx scripts/seed-transcriptions.ts` +Data layer: `lib/transcriptions.ts` (calls BFF API, NOT local JSON files or direct MongoDB) ### 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. - **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) - **Hidden when selection toolbar is active** on mobile to avoid overlap - **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 ``` -DATABASE_URL=mongodb://user:pass@host:port/spsays?authSource=admin -NEXT_PUBLIC_SITE_URL=https://prabhupadasays.org +BFF_URL=http://localhost:8080 # Go BFF API server URL (default: http://localhost:8080) +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 ```bash -# Development (from knowledgebase/) -npm run dev +# 1. Start the Go BFF server (required — all data comes from the BFF) +cd spsays-bff && go run cmd/api/main.go # Starts on :8080 by default -# Prisma -npx prisma generate # Regenerate Prisma client after schema changes +# 2. Development (from knowledgebase/) +npm run dev # Next.js dev server with Turbopack -# Build +# 3. Build npx next build # 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) @@ -946,7 +762,7 @@ Content block formats: `"paragraph"` (body text), `"verse"` (centered italic), ` ## 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. @@ -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)`. -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 - -9. **Audio player z-index** — Player is hidden when text selection toolbar is active on mobile to avoid overlap \ No newline at end of file +8. **Audio player z-index** — Player is hidden when text selection toolbar is active on mobile to avoid overlap \ No newline at end of file