#!/usr/bin/env node import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const root = path.resolve(__dirname, ".."); const categoriesDir = path.join(root, "src", "content", "categories"); const defaultEndpoint = "http://localhost:1337/graphql"; const endpoint = process.env.STRAPI_GRAPHQL_URL ?? process.env.PUBLIC_GRAPHQL_URL ?? defaultEndpoint; const token = process.env.STRAPI_TOKEN ?? process.env.STRAPI_API_TOKEN ?? process.env.AUTH_TOKEN; function readCategorySource(slug) { const filePath = path.join(categoriesDir, `${slug}.json`); if (!fs.existsSync(filePath)) { throw new Error(`Missing source file: ${path.relative(root, filePath)}`); } return JSON.parse(fs.readFileSync(filePath, "utf8")); } function discoverCategorySlugs() { if (!fs.existsSync(categoriesDir)) { throw new Error(`Categories directory missing: ${path.relative(root, categoriesDir)}`); } const slugs = []; for (const dirent of fs.readdirSync(categoriesDir, { withFileTypes: true })) { if (!dirent.isFile() || !dirent.name.endsWith(".json")) continue; const slug = path.basename(dirent.name, ".json"); const source = readCategorySource(slug); const { group } = source; if (group !== "solution" && group !== "service") { throw new Error( `Category ${slug} has invalid group "${String(group)}". Expected "solution" or "service".` ); } slugs.push(slug); } if (slugs.length === 0) { throw new Error(`No *.json categories in ${path.relative(root, categoriesDir)}`); } return slugs; } async function gql(query, variables = {}) { const headers = { "Content-Type": "application/json", Accept: "application/graphql-response+json, application/json", }; if (token) { headers.Authorization = `Bearer ${token}`; } const response = await fetch(endpoint, { method: "POST", headers, body: JSON.stringify({ query, variables }), }); if (!response.ok) { throw new Error(`GraphQL HTTP ${response.status}: ${response.statusText}`); } const payload = await response.json(); if (payload.errors?.length) { const messages = payload.errors.map((err) => err.message).join("; "); throw new Error(`GraphQL error: ${messages}`); } return payload.data; } async function categoryExists(slug) { const query = ` query CategoryBySlug($slug: String!) { categories(filters: { slug: { eq: $slug } }, pagination: { limit: 1 }) { documentId } } `; const data = await gql(query, { slug }); return Array.isArray(data?.categories) && data.categories.length > 0; } async function createCategory(input) { const mutation = ` mutation CreateCategory($data: CategoryInput!) { createCategory(data: $data) { documentId slug title group } } `; const data = await gql(mutation, { data: input }); return data?.createCategory; } async function seedCategories(slugs) { let createdCount = 0; let skippedCount = 0; for (const slug of slugs) { const source = readCategorySource(slug); const { title, description = "", group } = source; if (group !== "solution" && group !== "service") { throw new Error( `Category ${slug} has invalid group "${String(group)}". Expected "solution" or "service".` ); } if (await categoryExists(slug)) { skippedCount += 1; console.log(`SKIP ${slug} (already exists)`); continue; } const created = await createCategory({ slug, title, description, group, }); createdCount += 1; console.log( `CREATE ${created.slug} -> ${created.title} [group=${created.group}]` ); } return { createdCount, skippedCount }; } async function main() { console.log(`Seeding categories to ${endpoint}`); if (!token) { console.warn( "No STRAPI token provided. If endpoint is protected, set STRAPI_TOKEN." ); } const slugs = discoverCategorySlugs(); const { createdCount, skippedCount } = await seedCategories(slugs); console.log( `Done. created=${createdCount}, skipped=${skippedCount}, total=${createdCount + skippedCount}` ); } main().catch((error) => { console.error(error); process.exit(1); });