#!/usr/bin/env node /** * Import Documentaries into Strapi CMS (with upsert support) * * Usage: * node scripts/import-documentaries.js [path-to-json] * * Environment variables: * STRAPI_URL - Strapi API URL (default: http://localhost:1337) * * The JSON file should match the structure in docs/seeds/documentaries-seed.json */ const fs = require("fs"); const path = require("path"); const STRAPI_URL = process.env.STRAPI_URL || "http://localhost:1337"; const HEADERS = { "Content-Type": "application/json", }; async function apiRequest(endpoint, method = "GET", body = null) { const url = `${STRAPI_URL}/api${endpoint}`; const options = { method, headers: HEADERS, ...(body ? { body: JSON.stringify(body) } : {}), }; const response = await fetch(url, options); const data = await response.json().catch(() => null); if (!response.ok) { const errMsg = data?.error?.message || JSON.stringify(data); throw new Error(`${response.status}: ${errMsg}`); } return data; } async function findGameBySlug(slug) { try { const res = await apiRequest(`/games?filters[slug][$eq]=${slug}`); return res.data?.[0] || null; } catch { return null; } } async function findDocumentaryBySlug(slug) { try { // Documentary doesn't have slug, find via game relation const game = await findGameBySlug(slug); if (!game) return null; const res = await apiRequest(`/documentaries?filters[game][id][$eq]=${game.id}`); return res.data?.[0] || null; } catch { return null; } } async function createGame(gameData) { const payload = { data: { ...gameData, publishedAt: new Date().toISOString(), }, }; const created = await apiRequest("/games", "POST", payload); console.log(` āœ… Created game '${gameData.title}' (id: ${created.data.id})`); return created.data; } async function updateGame(documentId, gameData) { const payload = { data: { ...gameData, }, }; const updated = await apiRequest(`/games/${documentId}`, "PUT", payload); console.log(` šŸ”„ Updated game '${gameData.title}' (id: ${updated.data.id})`); return updated.data; } async function createDocumentary(docData, gameId) { const payload = { data: { title: docData.title, description: docData.description, game: gameId, publishedAt: new Date().toISOString(), }, }; const created = await apiRequest("/documentaries", "POST", payload); console.log(` āœ… Created documentary '${docData.title}' (id: ${created.data.id})`); return created.data; } async function updateDocumentary(documentId, docData, gameId) { const payload = { data: { title: docData.title, description: docData.description, game: gameId, }, }; const updated = await apiRequest(`/documentaries/${documentId}`, "PUT", payload); console.log(` šŸ”„ Updated documentary '${docData.title}' (id: ${updated.data.id})`); return updated.data; } async function deleteChaptersByDocumentary(documentaryId) { try { // Find all chapters linked to this documentary const res = await apiRequest(`/chapters?filters[documentary][id][$eq]=${documentaryId}&pagination[pageSize]=100`); const chapters = res.data || []; for (const ch of chapters) { await apiRequest(`/chapters/${ch.documentId}`, "DELETE"); console.log(` šŸ—‘ļø Deleted old chapter '${ch.title}'`); } } catch (err) { console.log(` āš ļø Could not delete old chapters: ${err.message}`); } } async function createChapters(chapters, documentaryId) { for (const chapter of chapters) { const payload = { data: { title: chapter.title, content: chapter.content, order: chapter.order, documentary: documentaryId, publishedAt: new Date().toISOString(), }, }; const created = await apiRequest("/chapters", "POST", payload); console.log(` āœ… Chapter ${chapter.order}: '${chapter.title}' (id: ${created.data.id})`); } } async function importDocumentaries(jsonPath) { console.log(`\nšŸ“– Importing documentaries from: ${jsonPath}`); console.log(`šŸ”— Strapi URL: ${STRAPI_URL}\n`); const raw = fs.readFileSync(jsonPath, "utf-8"); const seedData = JSON.parse(raw); if (!seedData.documentaries || !Array.isArray(seedData.documentaries)) { throw new Error("Invalid seed file: 'documentaries' array not found"); } let imported = 0; let updated = 0; let errors = 0; for (const doc of seedData.documentaries) { try { console.log(`\nšŸŽ¬ Processing: ${doc.title}`); const existingGame = await findGameBySlug(doc.game.slug); let game; if (existingGame) { game = await updateGame(existingGame.documentId, doc.game); updated++; } else { game = await createGame(doc.game); imported++; } const existingDoc = await findDocumentaryBySlug(doc.game.slug); let documentary; if (existingDoc) { documentary = await updateDocumentary(existingDoc.documentId, doc, game.id); // Delete old chapters and recreate console.log(` šŸ“ Recreating chapters...`); await deleteChaptersByDocumentary(existingDoc.documentId); await createChapters(doc.chapters, existingDoc.documentId); } else { documentary = await createDocumentary(doc, game.id); console.log(` šŸ“ Creating ${doc.chapters.length} chapters...`); await createChapters(doc.chapters, documentary.documentId); } console.log(` ✨ Completed: ${doc.title}`); } catch (err) { errors++; console.error(` āŒ Failed to import '${doc.title}':`, err.message); } } console.log(`\nšŸ“Š Import Summary:`); console.log(` Total documentaries: ${seedData.documentaries.length}`); console.log(` Games created: ${imported}`); console.log(` Games updated: ${updated}`); console.log(` Failed: ${errors}`); console.log(`\n✨ Done!\n`); } // Main const jsonPath = process.argv[2] || path.join(__dirname, "..", "docs", "seeds", "documentaries-seed.json"); if (!fs.existsSync(jsonPath)) { console.error(`āŒ Seed file not found: ${jsonPath}`); console.error(`Usage: node scripts/import-documentaries.js [path-to-json]`); process.exit(1); } importDocumentaries(jsonPath).catch((err) => { console.error("\nšŸ’„ Fatal error:", err.message); process.exit(1); });