/** * seed-db.ts — Push all extracted JSON data into MongoDB using the native driver. * * Prisma with MongoDB requires a replica set for writes (even createMany/deleteMany). * This script uses the MongoDB native driver directly to avoid that constraint. * The collections and field names match the Prisma schema so the frontend can * still read via Prisma without issues. * * Usage: * npx tsx scripts/seed-db.ts * * Prerequisites: * - DATABASE_URL must be set in .env * - All extraction scripts (bg, sb, cc, glossary) must have been run */ import dotenv from "dotenv"; import fs from "fs"; import path from "path"; import { MongoClient } from "mongodb"; // Load env dotenv.config({ path: ".env.local" }); dotenv.config({ path: ".env" }); const DATABASE_URL = process.env.DATABASE_URL; if (!DATABASE_URL) { console.error("❌ DATABASE_URL is not set in .env"); process.exit(1); } const BASE_DIR = path.resolve(__dirname, ".."); const LIB_DIR = path.join(BASE_DIR, "lib", "jsons"); // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- function readJson(filePath: string): T { return JSON.parse(fs.readFileSync(filePath, "utf-8")); } function readAllJsonInDir(dir: string): unknown[] { if (!fs.existsSync(dir)) return []; return fs .readdirSync(dir) .filter((f) => f.endsWith(".json")) .sort() .map((f) => readJson(path.join(dir, f))); } // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- type BgVerse = { book_title: string; chapter: number; verse: number; verse_end?: number; reference: string; devanagari: string; transliteration: string; synonyms: { word: string; meanings: string[] }[]; translation: string; purport: string[]; }; type SbVerse = { book_title: string; canto: number; chapter: number; verse: number; verse_end?: number; reference: string; devanagari: string; transliteration: string; synonyms: { word: string; meanings: string[] }[]; translation: string; purport: string[]; }; type CcVerse = { book_title: string; lila: string; chapter: number; verse: number; verse_end?: number; reference: string; devanagari: string; bengali: string; transliteration: string; synonyms: { word: string; meanings: string[] }[]; translation: string; purport: string[]; }; type Sb1962Verse = { book_title: string; chapter: number; verse: number; verse_end?: number; reference: string; transliteration: string; synonyms: { word: string; meanings: string[] }[]; translation: string; purport: string[]; }; type GlossaryData = { title: string; description: string; totalEntries: number; letters: string[]; entries: { term: string; definition: string; definition_html: string; }[]; }; // --------------------------------------------------------------------------- // BG // --------------------------------------------------------------------------- async function seedBg(db: ReturnType) { const introPath = path.join(LIB_DIR, "books", "bg", "intro.json"); if (!fs.existsSync(introPath)) { console.log(" ⏭ BG intro.json not found, skipping"); return; } const intro = readJson(introPath); // Book meta const metaCol = db.collection("book_meta"); await metaCol.deleteMany({ book: "bg" }); await metaCol.insertOne({ book: "bg", data: intro }); console.log(" 📖 BG book meta inserted"); // Verses const versesCol = db.collection("verses"); await versesCol.deleteMany({ book: "bg" }); const chaptersDir = path.join(LIB_DIR, "books", "bg", "chapters"); if (!fs.existsSync(chaptersDir)) { console.log(" ⏭ BG chapters dir not found, skipping verses"); return; } const chapterDirs = fs .readdirSync(chaptersDir) .filter((d) => fs.statSync(path.join(chaptersDir, d)).isDirectory()) .sort(); const docs: Record[] = []; for (const chDir of chapterDirs) { const versesDir = path.join(chaptersDir, chDir, "verses"); const verses = readAllJsonInDir(versesDir) as BgVerse[]; for (const v of verses) { docs.push({ book: "bg", division: null, chapter: v.chapter, verse: v.verse, verseEnd: v.verse_end ?? null, reference: v.reference, bookTitle: v.book_title, devanagari: v.devanagari, bengali: null, transliteration: v.transliteration, synonyms: v.synonyms, translation: v.translation, purport: v.purport, }); } } if (docs.length > 0) { await versesCol.insertMany(docs, { ordered: false }); } console.log(` ✅ BG: ${docs.length} verses`); } // --------------------------------------------------------------------------- // SB // --------------------------------------------------------------------------- async function seedSb(db: ReturnType) { const introPath = path.join(LIB_DIR, "books", "sb", "intro.json"); if (!fs.existsSync(introPath)) { console.log(" ⏭ SB intro.json not found, skipping"); return; } const intro = readJson(introPath); const metaCol = db.collection("book_meta"); await metaCol.deleteMany({ book: "sb" }); await metaCol.insertOne({ book: "sb", data: intro }); console.log(" 📖 SB book meta inserted"); const versesCol = db.collection("verses"); await versesCol.deleteMany({ book: "sb" }); const cantosDir = path.join(LIB_DIR, "books", "sb", "cantos"); if (!fs.existsSync(cantosDir)) { console.log(" ⏭ SB cantos dir not found, skipping verses"); return; } const cantoDirs = fs .readdirSync(cantosDir) .filter((d) => fs.statSync(path.join(cantosDir, d)).isDirectory()) .sort(); let totalCount = 0; for (const cantoDir of cantoDirs) { const chaptersDir = path.join(cantosDir, cantoDir, "chapters"); if (!fs.existsSync(chaptersDir)) continue; const chapterDirs = fs .readdirSync(chaptersDir) .filter((d) => fs.statSync(path.join(chaptersDir, d)).isDirectory()) .sort(); const docs: Record[] = []; for (const chDir of chapterDirs) { const versesDir = path.join(chaptersDir, chDir, "verses"); const verses = readAllJsonInDir(versesDir) as SbVerse[]; for (const v of verses) { docs.push({ book: "sb", division: String(v.canto), chapter: v.chapter, verse: v.verse, verseEnd: v.verse_end ?? null, reference: v.reference, bookTitle: v.book_title, devanagari: v.devanagari, bengali: null, transliteration: v.transliteration, synonyms: v.synonyms, translation: v.translation, purport: v.purport, }); } } if (docs.length > 0) { await versesCol.insertMany(docs, { ordered: false }); } totalCount += docs.length; console.log(` Canto ${cantoDir}: ${docs.length} verses`); } console.log(` ✅ SB: ${totalCount} verses`); } // --------------------------------------------------------------------------- // CC // --------------------------------------------------------------------------- async function seedCc(db: ReturnType) { const introPath = path.join(LIB_DIR, "books", "cc", "intro.json"); if (!fs.existsSync(introPath)) { console.log(" ⏭ CC intro.json not found, skipping"); return; } const intro = readJson(introPath); const metaCol = db.collection("book_meta"); await metaCol.deleteMany({ book: "cc" }); await metaCol.insertOne({ book: "cc", data: intro }); console.log(" 📖 CC book meta inserted"); const versesCol = db.collection("verses"); await versesCol.deleteMany({ book: "cc" }); const lilasDir = path.join(LIB_DIR, "books", "cc", "lilas"); if (!fs.existsSync(lilasDir)) { console.log(" ⏭ CC lilas dir not found, skipping verses"); return; } const lilaDirs = fs .readdirSync(lilasDir) .filter((d) => fs.statSync(path.join(lilasDir, d)).isDirectory()) .sort(); let totalCount = 0; for (const lilaDir of lilaDirs) { const chaptersDir = path.join(lilasDir, lilaDir, "chapters"); if (!fs.existsSync(chaptersDir)) continue; const chapterDirs = fs .readdirSync(chaptersDir) .filter((d) => fs.statSync(path.join(chaptersDir, d)).isDirectory()) .sort(); const docs: Record[] = []; for (const chDir of chapterDirs) { const versesDir = path.join(chaptersDir, chDir, "verses"); const verses = readAllJsonInDir(versesDir) as CcVerse[]; for (const v of verses) { docs.push({ book: "cc", division: v.lila, chapter: v.chapter, verse: v.verse, verseEnd: v.verse_end ?? null, reference: v.reference, bookTitle: v.book_title, devanagari: v.devanagari, bengali: v.bengali, transliteration: v.transliteration, synonyms: v.synonyms, translation: v.translation, purport: v.purport, }); } } if (docs.length > 0) { await versesCol.insertMany(docs, { ordered: false }); } totalCount += docs.length; console.log(` Līlā ${lilaDir}: ${docs.length} verses`); } console.log(` ✅ CC: ${totalCount} verses`); } // --------------------------------------------------------------------------- // SB 1962 // --------------------------------------------------------------------------- async function seedSb1962(db: ReturnType) { const introPath = path.join(LIB_DIR, "books", "sb-1962", "intro.json"); if (!fs.existsSync(introPath)) { console.log(" ⏭ SB 1962 intro.json not found, skipping"); return; } const intro = readJson(introPath); const metaCol = db.collection("book_meta"); await metaCol.deleteMany({ book: "sb1962" }); await metaCol.insertOne({ book: "sb1962", data: intro }); console.log(" 📖 SB 1962 book meta inserted"); const versesCol = db.collection("verses"); await versesCol.deleteMany({ book: "sb1962" }); const chaptersDir = path.join(LIB_DIR, "books", "sb-1962", "chapters"); if (!fs.existsSync(chaptersDir)) { console.log(" ⏭ SB 1962 chapters dir not found, skipping verses"); return; } const chapterDirs = fs .readdirSync(chaptersDir) .filter((d) => fs.statSync(path.join(chaptersDir, d)).isDirectory()) .sort(); const docs: Record[] = []; for (const chDir of chapterDirs) { const versesDir = path.join(chaptersDir, chDir, "verses"); const verses = readAllJsonInDir(versesDir) as Sb1962Verse[]; for (const v of verses) { docs.push({ book: "sb1962", division: null, chapter: v.chapter, verse: v.verse, verseEnd: v.verse_end ?? null, reference: v.reference, bookTitle: v.book_title, devanagari: "", bengali: null, transliteration: v.transliteration, synonyms: v.synonyms, translation: v.translation, purport: v.purport, }); } } if (docs.length > 0) { await versesCol.insertMany(docs, { ordered: false }); } console.log(` ✅ SB 1962: ${docs.length} verses`); } // --------------------------------------------------------------------------- // Glossary // --------------------------------------------------------------------------- async function seedGlossary(db: ReturnType) { const glossaryPath = path.join(LIB_DIR, "glossary", "index.json"); if (!fs.existsSync(glossaryPath)) { console.log(" ⏭ Glossary index.json not found, skipping"); return; } const data = readJson(glossaryPath); const col = db.collection("glossary"); await col.deleteMany({}); const docs = data.entries.map((entry) => ({ term: entry.term, definition: entry.definition, definitionHtml: entry.definition_html, })); if (docs.length > 0) { await col.insertMany(docs, { ordered: false }); } console.log(` ✅ Glossary: ${docs.length} entries`); } // --------------------------------------------------------------------------- // Create indexes (matching Prisma schema) // --------------------------------------------------------------------------- async function createIndexes(db: ReturnType) { console.log(" 📇 Creating indexes..."); const versesCol = db.collection("verses"); await versesCol.createIndex({ book: 1 }); await versesCol.createIndex({ book: 1, division: 1 }); await versesCol.createIndex({ book: 1, division: 1, chapter: 1 }); await versesCol.createIndex( { book: 1, division: 1, chapter: 1, verse: 1 }, { unique: true }, ); const metaCol = db.collection("book_meta"); await metaCol.createIndex({ book: 1 }, { unique: true }); const glossaryCol = db.collection("glossary"); await glossaryCol.createIndex({ term: 1 }, { unique: true }); console.log(" ✅ Indexes created"); } // --------------------------------------------------------------------------- // Main // --------------------------------------------------------------------------- async function main() { console.log("🌱 Seeding database...\n"); const client = new MongoClient(DATABASE_URL!); await client.connect(); console.log("📡 Connected to MongoDB\n"); const db = client.db(); // uses the database name from the URL const start = Date.now(); console.log("[1/6] Bhagavad-gītā"); await seedBg(db); console.log(); console.log("[2/6] Śrīmad-Bhāgavatam"); await seedSb(db); console.log(); console.log("[3/6] Śrī Caitanya-caritāmṛta"); await seedCc(db); console.log(); console.log("[4/6] Śrīmad-Bhāgavatam (1962 Edition)"); await seedSb1962(db); console.log(); console.log("[5/6] Glossary"); await seedGlossary(db); console.log(); console.log("[6/6] Indexes"); await createIndexes(db); console.log(); const elapsed = ((Date.now() - start) / 1000).toFixed(1); console.log(`🎉 Done in ${elapsed}s`); await client.close(); } main().catch((e) => { console.error("❌ Seed failed:", e); process.exit(1); });