/** * seed-mog.ts — Push Message of Godhead JSON data into MongoDB. * * Uses the native MongoDB driver (same pattern as seed-mg.ts) to avoid * Prisma replica-set write requirements. * * MOG is a prose book with no volume structure. * We use volume=0 as the sentinel for "no volumes". * * Collections written: * - book_meta → one doc { book: "mog", data: } * - prose_chapters → 1 intro segment + 3 chapters = 4 docs * * Usage: * npx tsx scripts/seed-mog.ts */ 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 MOG_DIR = path.join(BASE_DIR, "lib", "jsons", "books", "mog"); /** Volume sentinel for books without volumes. */ const NO_VOLUME = 0; // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- function readJson(filePath: string): T { return JSON.parse(fs.readFileSync(filePath, "utf-8")); } /** * Insert documents one at a time with a small delay every N docs. * Uses insertOne to avoid BSON wire-protocol corruption. */ async function insertBatched( col: ReturnType["collection"]>, docs: Record[], ) { for (let i = 0; i < docs.length; i++) { await col.insertOne(docs[i]); if ((i + 1) % 100 === 0) { await new Promise((r) => setTimeout(r, 100)); } } } // --------------------------------------------------------------------------- // Types for the JSON files // --------------------------------------------------------------------------- type MogIntroJson = { book: string; title: string; author: string; year: number; description: string; totalChapters: number; chapters: { number: number; title: string }[]; }; type MogIntroSegmentJson = { slug: string; title: string; type: string; book: string; order: number; paragraphs: string[]; }; type MogChapterJson = { book: string; chapter: number; title: string; type: string; paragraphs: string[]; }; // --------------------------------------------------------------------------- // Seed function // --------------------------------------------------------------------------- async function seedMog(db: ReturnType) { const introPath = path.join(MOG_DIR, "intro.json"); if (!fs.existsSync(introPath)) { console.error("❌ MOG intro.json not found at", introPath); process.exit(1); } const intro = readJson(introPath); // ---- 1. Book meta ---- const metaCol = db.collection("book_meta"); await metaCol.deleteMany({ book: "mog" }); await metaCol.insertOne({ book: "mog", data: intro }); console.log(" 📖 MOG book meta inserted"); // ---- 2. Prose chapters collection ---- const proseCol = db.collection("prose_chapters"); await proseCol.deleteMany({ book: "mog" }); const docs: Record[] = []; // Intro segments (front-pages) const introSegmentFiles = ["front-pages.json"]; const introDir = path.join(MOG_DIR, "intro"); for (const file of introSegmentFiles) { const filePath = path.join(introDir, file); if (!fs.existsSync(filePath)) { console.warn(` ⚠️ Intro segment not found, skipping: ${file}`); continue; } const seg = readJson(filePath); docs.push({ book: "mog", volume: NO_VOLUME, chapter: null, title: seg.title, type: "intro-segment", slug: `intro-${seg.slug}`, order: seg.order, paragraphs: seg.paragraphs, }); } console.log(` 📄 Intro segments: ${docs.length}`); // Chapters (01.json through 03.json) const chaptersDir = path.join(MOG_DIR, "chapters"); if (fs.existsSync(chaptersDir)) { const chapterFiles = fs .readdirSync(chaptersDir) .filter((f) => f.endsWith(".json")) .sort(); for (const file of chapterFiles) { const ch = readJson(path.join(chaptersDir, file)); const chNum = ch.chapter ?? parseInt(file.replace(".json", ""), 10); docs.push({ book: "mog", volume: NO_VOLUME, chapter: chNum, title: ch.title, type: "chapter", slug: String(chNum), order: 100 + chNum, paragraphs: ch.paragraphs, }); } console.log(` 📖 Chapters: ${chapterFiles.length}`); } // Insert all docs console.log(`\n Inserting ${docs.length} documents...`); await insertBatched(proseCol, docs); const totalDocs = await proseCol.countDocuments({ book: "mog" }); console.log(` ✅ MOG: ${totalDocs} prose entries in MongoDB`); } // --------------------------------------------------------------------------- // Main // --------------------------------------------------------------------------- async function main() { console.log("🌱 Seeding Message of Godhead...\n"); const client = new MongoClient(DATABASE_URL!); await client.connect(); console.log("📡 Connected to MongoDB\n"); const db = client.db(); const start = Date.now(); await seedMog(db); const elapsed = ((Date.now() - start) / 1000).toFixed(1); console.log(`\n🎉 Done in ${elapsed}s`); await client.close(); } main().catch((err) => { console.error("❌ Seed failed:", err); process.exit(1); });