242 lines
6.7 KiB
JavaScript
242 lines
6.7 KiB
JavaScript
#!/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 sourceFile = path.join(root, "src", "content", "home-page.json");
|
|
|
|
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;
|
|
const isDryRun = process.argv.includes("--dry-run");
|
|
|
|
function readSource() {
|
|
if (!fs.existsSync(sourceFile)) {
|
|
throw new Error(`Missing source file: ${path.relative(root, sourceFile)}`);
|
|
}
|
|
return JSON.parse(fs.readFileSync(sourceFile, "utf8"));
|
|
}
|
|
|
|
// Drop null/undefined keys so optional Strapi fields (e.g. anchor) are omitted
|
|
// rather than sent as explicit nulls.
|
|
function compact(object) {
|
|
return Object.fromEntries(
|
|
Object.entries(object).filter(([, value]) => value !== null && value !== undefined)
|
|
);
|
|
}
|
|
|
|
// Maps a source section ("type") to its Strapi dynamic-zone component payload.
|
|
// resolveCategory turns a category slug into the documentId Strapi expects for
|
|
// the card -> category relation.
|
|
const SECTION_BUILDERS = {
|
|
hero: (section) =>
|
|
compact({
|
|
__component: "sections.home-hero",
|
|
title: section.title,
|
|
description: section.description,
|
|
anchor: section.anchor,
|
|
badge: section.badge,
|
|
stats: (section.stats ?? []).map((stat) => ({
|
|
key: stat.key,
|
|
value: stat.value,
|
|
})),
|
|
}),
|
|
services: (section, resolveCategory) =>
|
|
compact({
|
|
__component: "sections.services",
|
|
title: section.title,
|
|
description: section.description,
|
|
anchor: section.anchor,
|
|
cards: (section.cards ?? []).map((card) =>
|
|
compact({
|
|
title: card.title,
|
|
description: card.description,
|
|
icon: { value: card.icon },
|
|
color: { value: card.color },
|
|
category: resolveCategory(card.category),
|
|
})
|
|
),
|
|
}),
|
|
solutions: (section, resolveCategory) =>
|
|
compact({
|
|
__component: "sections.solutions",
|
|
title: section.title,
|
|
description: section.description,
|
|
anchor: section.anchor,
|
|
cards: (section.cards ?? []).map((card) =>
|
|
compact({
|
|
title: card.title,
|
|
description: card.description,
|
|
footnote: card.footnote,
|
|
icon: { value: card.icon },
|
|
category: resolveCategory(card.category),
|
|
})
|
|
),
|
|
}),
|
|
cases: (section) =>
|
|
compact({
|
|
__component: "sections.cases",
|
|
title: section.title,
|
|
description: section.description,
|
|
anchor: section.anchor,
|
|
cards: (section.cards ?? []).map((card) =>
|
|
compact({
|
|
title: card.title,
|
|
description: card.description,
|
|
pretitle: card.pretitle,
|
|
badge: card.badge,
|
|
footnote: card.footnote,
|
|
color: { value: card.color },
|
|
icon: { value: card.icon },
|
|
})
|
|
),
|
|
}),
|
|
about: (section) =>
|
|
compact({
|
|
__component: "sections.about",
|
|
title: section.title,
|
|
description: section.description,
|
|
anchor: section.anchor,
|
|
partners: (section.partners ?? []).map((value) => ({ value })),
|
|
cards: (section.cards ?? []).map((card) =>
|
|
compact({
|
|
title: card.title,
|
|
description: card.description,
|
|
color: { value: card.color },
|
|
icon: { value: card.icon },
|
|
})
|
|
),
|
|
}),
|
|
};
|
|
|
|
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 fetchCategoryDocumentIds() {
|
|
const query = `
|
|
query CategoryIds {
|
|
categories(pagination: { limit: 500 }) {
|
|
documentId
|
|
slug
|
|
}
|
|
}
|
|
`;
|
|
|
|
const data = await gql(query);
|
|
const map = new Map();
|
|
for (const category of data?.categories ?? []) {
|
|
if (!category?.slug || !category?.documentId) continue;
|
|
map.set(category.slug, category.documentId);
|
|
}
|
|
return map;
|
|
}
|
|
|
|
function buildSections(source, categoryIdBySlug) {
|
|
const sections = Array.isArray(source.sections) ? source.sections : [];
|
|
|
|
const resolveCategory = (slug) => {
|
|
const documentId = categoryIdBySlug.get(slug);
|
|
if (!documentId) {
|
|
throw new Error(
|
|
`Category "${slug}" is missing in Strapi. Seed categories before seeding the home page.`
|
|
);
|
|
}
|
|
return documentId;
|
|
};
|
|
|
|
return sections.map((section) => {
|
|
const build = SECTION_BUILDERS[section.type];
|
|
if (!build) {
|
|
throw new Error(`Unknown home-page section type "${String(section.type)}".`);
|
|
}
|
|
return build(section, resolveCategory);
|
|
});
|
|
}
|
|
|
|
async function updateHomePage(sections) {
|
|
const mutation = `
|
|
mutation UpdateHomePage($data: HomePageInput!) {
|
|
updateHomePage(data: $data) {
|
|
sections {
|
|
__typename
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
const data = await gql(mutation, { data: { sections } });
|
|
return data?.updateHomePage;
|
|
}
|
|
|
|
async function main() {
|
|
console.log(`Seeding home page to ${endpoint}${isDryRun ? " (dry-run)" : ""}`);
|
|
if (!token) {
|
|
console.warn("No STRAPI token provided. If endpoint is protected, set STRAPI_TOKEN.");
|
|
}
|
|
|
|
const source = readSource();
|
|
|
|
if (isDryRun && process.argv.includes("--offline")) {
|
|
// Allow inspecting the payload shape without touching Strapi.
|
|
const sections = buildSections(source, new Map(
|
|
(source.sections ?? [])
|
|
.flatMap((section) => section.cards ?? [])
|
|
.map((card) => card.category)
|
|
.filter(Boolean)
|
|
.map((slug) => [slug, `dryrun-${slug}`])
|
|
));
|
|
console.log(JSON.stringify(sections, null, 2));
|
|
return;
|
|
}
|
|
|
|
const categoryIdBySlug = await fetchCategoryDocumentIds();
|
|
const sections = buildSections(source, categoryIdBySlug);
|
|
|
|
if (isDryRun) {
|
|
console.log(JSON.stringify(sections, null, 2));
|
|
console.log(`DRY-RUN UPDATE homePage (${sections.length} sections)`);
|
|
return;
|
|
}
|
|
|
|
const updated = await updateHomePage(sections);
|
|
console.log(`UPDATE homePage (${updated.sections.length} sections)`);
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error(error);
|
|
process.exit(1);
|
|
});
|