/** * seed-journals.ts — Push Journals into MongoDB. * * Collections written: * - journal_meta → one doc per journal * - journal_chapters → N chapter docs per journal * * Usage: * npx tsx scripts/seed-journals.ts */ import dotenv from "dotenv"; import fs from "fs"; import path from "path"; import { MongoClient } from "mongodb"; 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"); process.exit(1); } const BASE_DIR = path.resolve(__dirname, ".."); const JOURNALS_DIR = path.join(BASE_DIR, "lib", "jsons", "journals"); type IntroJson = { book: string; title: string; author: string; year: number | null; description: string | null; totalChapters: number; chapters: unknown[]; }; type ChapterJson = { book: string; chapter: number; title: string; type: string; slug?: string; paragraphs: string[]; }; function readJson(filePath: string): T { return JSON.parse(fs.readFileSync(filePath, "utf-8")); } function sanitizeDoc(obj: unknown): unknown { if (typeof obj === "string") { let s = obj.normalize("NFC"); s = s.replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, ""); s = s.replace(/[\uD800-\uDFFF]/g, ""); s = s.replace(/[\u200B-\u200F\u2028-\u202F\uFEFF]/g, ""); s = Buffer.from(s, "utf-8").toString("utf-8"); return s; } if (Array.isArray(obj)) return obj.map(sanitizeDoc); if (obj !== null && typeof obj === "object") { const result: Record = {}; for (const [key, value] of Object.entries(obj as Record)) result[key] = sanitizeDoc(value); return result; } return obj; } async function seedOneJournal( db: ReturnType, docDir: string, ): Promise<{ bookCode: string; chapters: number }> { const introPath = path.join(docDir, "intro.json"); if (!fs.existsSync(introPath)) throw new Error(`No intro.json in ${docDir}`); const intro = sanitizeDoc(readJson(introPath)) as IntroJson; const bookCode = intro.book; const metaCol = db.collection("journal_meta"); await metaCol.deleteMany({ book: bookCode }); await metaCol.insertOne({ book: bookCode, data: intro }); console.log(` šŸ“– Inserted journal_meta for "${intro.title}" (${bookCode})`); const chaptersCol = db.collection("journal_chapters"); await chaptersCol.deleteMany({ book: bookCode }); const chaptersDir = path.join(docDir, "chapters"); let inserted = 0; if (fs.existsSync(chaptersDir)) { const files = fs .readdirSync(chaptersDir) .filter((f) => f.endsWith(".json")) .sort(); for (const file of files) { const ch = sanitizeDoc( readJson(path.join(chaptersDir, file)), ) as ChapterJson; const chNum = ch.chapter ?? parseInt(file.replace(".json", ""), 10); const slug = ch.slug ?? String(chNum); await chaptersCol.insertOne({ book: bookCode, chapter: chNum, title: ch.title, type: ch.type || "chapter", slug, order: 100 + chNum, paragraphs: ch.paragraphs, }); inserted++; } console.log(` šŸ“„ Chapters: ${inserted}`); } const total = await chaptersCol.countDocuments({ book: bookCode }); console.log(` āœ… ${bookCode}: ${total} chapter entries`); return { bookCode, chapters: inserted }; } async function main() { console.log("🌱 Seeding Journals...\n"); const client = new MongoClient(DATABASE_URL!); await client.connect(); console.log("šŸ“” Connected to MongoDB\n"); const db = client.db(); const start = Date.now(); if (!fs.existsSync(JOURNALS_DIR)) { console.error(`āŒ ${JOURNALS_DIR} not found`); process.exit(1); } const subdirs = fs .readdirSync(JOURNALS_DIR) .filter((e) => { const p = path.join(JOURNALS_DIR, e); return ( fs.statSync(p).isDirectory() && fs.existsSync(path.join(p, "intro.json")) ); }) .sort(); if (subdirs.length === 0) { console.error("āŒ No journal directories found"); process.exit(1); } console.log(`šŸ“‚ Found ${subdirs.length} journal(s)\n`); let totalChapters = 0; const bookCodes: string[] = []; for (const subdir of subdirs) { console.log(`\nšŸ“ Processing: ${subdir}`); const result = await seedOneJournal(db, path.join(JOURNALS_DIR, subdir)); bookCodes.push(result.bookCode); totalChapters += result.chapters; } const elapsed = ((Date.now() - start) / 1000).toFixed(1); console.log("\n" + "─".repeat(50)); console.log( `šŸ“Š Summary: ${subdirs.length} journals, ${totalChapters} chapters`, ); console.log(` Book codes: ${bookCodes.join(", ")}`); console.log(`\nšŸŽ‰ Done in ${elapsed}s`); await client.close(); } main().catch((err) => { console.error("āŒ Seed failed:", err); process.exit(1); });