Fetch CV data via API with file fallback

This commit is contained in:
svemagie
2026-03-08 06:36:20 +01:00
parent a1eedb72ca
commit dd400cbef5
4 changed files with 232 additions and 56 deletions

View File

@@ -1,37 +1,79 @@
/** /**
* CV Data — reads from indiekit-endpoint-cv plugin data file. * CV Data
* *
* The CV plugin writes content/.indiekit/cv.json on every save * API-first for split backend/frontend deployments:
* and on startup. Eleventy reads that file here. * - Try Indiekit public API (`/cvapi/data.json`, fallback `/cv/data.json`)
* - Fallback to local plugin file (`content/.indiekit/cv.json`)
* *
* Falls back to empty defaults if no plugin is installed. * Returns empty defaults if neither source is available.
*/ */
import EleventyFetch from "@11ty/eleventy-fetch";
import { readFileSync } from "node:fs"; import { readFileSync } from "node:fs";
import { resolve, dirname } from "node:path"; import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url)); const __dirname = dirname(fileURLToPath(import.meta.url));
const INDIEKIT_URL =
process.env.INDIEKIT_URL || process.env.SITE_URL || "https://example.com";
export default function () { const EMPTY_CV = {
lastUpdated: null,
experience: [],
projects: [],
skills: {},
skillTypes: {},
languages: [],
education: [],
interests: {},
interestTypes: {},
};
async function fetchFromIndiekit(path) {
const urls = [
`${INDIEKIT_URL}/cvapi/${path}`,
`${INDIEKIT_URL}/cv/${path}`,
];
for (const url of urls) {
try {
console.log(`[cv] Fetching from Indiekit: ${url}`);
const data = await EleventyFetch(url, {
duration: "15m",
type: "json",
});
console.log(`[cv] Indiekit ${path} success via ${url}`);
return data;
} catch (error) {
console.log(`[cv] Indiekit API unavailable at ${url}: ${error.message}`);
}
}
return null;
}
function readLocalCvFile() {
try { try {
const cvPath = resolve(__dirname, "..", "content", ".indiekit", "cv.json"); const cvPath = resolve(__dirname, "..", "content", ".indiekit", "cv.json");
const raw = readFileSync(cvPath, "utf8"); const raw = readFileSync(cvPath, "utf8");
const data = JSON.parse(raw); const data = JSON.parse(raw);
console.log("[cv] Loaded CV data from plugin"); console.log("[cv] Loaded CV data from local plugin file");
return data; return data;
} catch { } catch {
// No CV plugin data file — return empty defaults return null;
return {
lastUpdated: null,
experience: [],
projects: [],
skills: {},
skillTypes: {},
languages: [],
education: [],
interests: {},
interestTypes: {},
};
} }
} }
export default async function () {
const apiData = await fetchFromIndiekit("data.json");
if (apiData && typeof apiData === "object") {
return { ...EMPTY_CV, ...apiData };
}
const localData = readLocalCvFile();
if (localData && typeof localData === "object") {
return { ...EMPTY_CV, ...localData };
}
return EMPTY_CV;
}

View File

@@ -1,29 +1,75 @@
/** /**
* CV Page Configuration Data * CV Page Configuration Data
* Reads config from indiekit-endpoint-cv plugin CV page builder.
* Falls back to null — cv.njk then uses the default hardcoded layout.
* *
* The CV plugin writes a .indiekit/cv-page.json file that Eleventy watches. * API-first for split backend/frontend deployments:
* On change, a rebuild picks up the new config, allowing layout changes * - Try Indiekit public API (`/cvapi/page.json`, fallback `/cv/page.json`)
* without a Docker rebuild. * - Fallback to local plugin file (`content/.indiekit/cv-page.json`)
*
* Falls back to null so cv.njk can use the hardcoded default layout.
*/ */
import EleventyFetch from "@11ty/eleventy-fetch";
import { readFileSync } from "node:fs"; import { readFileSync } from "node:fs";
import { resolve, dirname } from "node:path"; import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url)); const __dirname = dirname(fileURLToPath(import.meta.url));
const INDIEKIT_URL =
process.env.INDIEKIT_URL || process.env.SITE_URL || "https://example.com";
export default function () { async function fetchFromIndiekit(path) {
const urls = [
`${INDIEKIT_URL}/cvapi/${path}`,
`${INDIEKIT_URL}/cv/${path}`,
];
for (const url of urls) {
try {
console.log(`[cvPageConfig] Fetching from Indiekit: ${url}`);
const data = await EleventyFetch(url, {
duration: "15m",
type: "json",
});
console.log(`[cvPageConfig] Indiekit ${path} success via ${url}`);
return data;
} catch (error) {
console.log(
`[cvPageConfig] Indiekit API unavailable at ${url}: ${error.message}`
);
}
}
return null;
}
function readLocalConfigFile() {
try { try {
// Resolve via the content/ symlink relative to the Eleventy project const configPath = resolve(
const configPath = resolve(__dirname, "..", "content", ".indiekit", "cv-page.json"); __dirname,
"..",
"content",
".indiekit",
"cv-page.json"
);
const raw = readFileSync(configPath, "utf8"); const raw = readFileSync(configPath, "utf8");
const config = JSON.parse(raw); const config = JSON.parse(raw);
console.log("[cvPageConfig] Loaded CV page builder config"); console.log("[cvPageConfig] Loaded local CV page builder config");
return config; return config;
} catch { } catch {
// No CV page builder config — fall back to hardcoded layout in cv.njk
return null; return null;
} }
} }
export default async function () {
const apiConfig = await fetchFromIndiekit("page.json");
if (apiConfig && typeof apiConfig === "object") {
return apiConfig;
}
const localConfig = readLocalConfigFile();
if (localConfig && typeof localConfig === "object") {
return localConfig;
}
return null;
}

View File

@@ -1,37 +1,79 @@
/** /**
* CV Data — reads from indiekit-endpoint-cv plugin data file. * CV Data
* *
* The CV plugin writes content/.indiekit/cv.json on every save * API-first for split backend/frontend deployments:
* and on startup. Eleventy reads that file here. * - Try Indiekit public API (`/cvapi/data.json`, fallback `/cv/data.json`)
* - Fallback to local plugin file (`content/.indiekit/cv.json`)
* *
* Falls back to empty defaults if no plugin is installed. * Returns empty defaults if neither source is available.
*/ */
import EleventyFetch from "@11ty/eleventy-fetch";
import { readFileSync } from "node:fs"; import { readFileSync } from "node:fs";
import { resolve, dirname } from "node:path"; import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url)); const __dirname = dirname(fileURLToPath(import.meta.url));
const INDIEKIT_URL =
process.env.INDIEKIT_URL || process.env.SITE_URL || "https://example.com";
export default function () { const EMPTY_CV = {
lastUpdated: null,
experience: [],
projects: [],
skills: {},
skillTypes: {},
languages: [],
education: [],
interests: {},
interestTypes: {},
};
async function fetchFromIndiekit(path) {
const urls = [
`${INDIEKIT_URL}/cvapi/${path}`,
`${INDIEKIT_URL}/cv/${path}`,
];
for (const url of urls) {
try {
console.log(`[cv] Fetching from Indiekit: ${url}`);
const data = await EleventyFetch(url, {
duration: "15m",
type: "json",
});
console.log(`[cv] Indiekit ${path} success via ${url}`);
return data;
} catch (error) {
console.log(`[cv] Indiekit API unavailable at ${url}: ${error.message}`);
}
}
return null;
}
function readLocalCvFile() {
try { try {
const cvPath = resolve(__dirname, "..", "content", ".indiekit", "cv.json"); const cvPath = resolve(__dirname, "..", "content", ".indiekit", "cv.json");
const raw = readFileSync(cvPath, "utf8"); const raw = readFileSync(cvPath, "utf8");
const data = JSON.parse(raw); const data = JSON.parse(raw);
console.log("[cv] Loaded CV data from plugin"); console.log("[cv] Loaded CV data from local plugin file");
return data; return data;
} catch { } catch {
// No CV plugin data file — return empty defaults return null;
return {
lastUpdated: null,
experience: [],
projects: [],
skills: {},
skillTypes: {},
languages: [],
education: [],
interests: {},
interestTypes: {},
};
} }
} }
export default async function () {
const apiData = await fetchFromIndiekit("data.json");
if (apiData && typeof apiData === "object") {
return { ...EMPTY_CV, ...apiData };
}
const localData = readLocalCvFile();
if (localData && typeof localData === "object") {
return { ...EMPTY_CV, ...localData };
}
return EMPTY_CV;
}

View File

@@ -1,29 +1,75 @@
/** /**
* CV Page Configuration Data * CV Page Configuration Data
* Reads config from indiekit-endpoint-cv plugin CV page builder.
* Falls back to null — cv.njk then uses the default hardcoded layout.
* *
* The CV plugin writes a .indiekit/cv-page.json file that Eleventy watches. * API-first for split backend/frontend deployments:
* On change, a rebuild picks up the new config, allowing layout changes * - Try Indiekit public API (`/cvapi/page.json`, fallback `/cv/page.json`)
* without a Docker rebuild. * - Fallback to local plugin file (`content/.indiekit/cv-page.json`)
*
* Falls back to null so cv.njk can use the hardcoded default layout.
*/ */
import EleventyFetch from "@11ty/eleventy-fetch";
import { readFileSync } from "node:fs"; import { readFileSync } from "node:fs";
import { resolve, dirname } from "node:path"; import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url)); const __dirname = dirname(fileURLToPath(import.meta.url));
const INDIEKIT_URL =
process.env.INDIEKIT_URL || process.env.SITE_URL || "https://example.com";
export default function () { async function fetchFromIndiekit(path) {
const urls = [
`${INDIEKIT_URL}/cvapi/${path}`,
`${INDIEKIT_URL}/cv/${path}`,
];
for (const url of urls) {
try {
console.log(`[cvPageConfig] Fetching from Indiekit: ${url}`);
const data = await EleventyFetch(url, {
duration: "15m",
type: "json",
});
console.log(`[cvPageConfig] Indiekit ${path} success via ${url}`);
return data;
} catch (error) {
console.log(
`[cvPageConfig] Indiekit API unavailable at ${url}: ${error.message}`
);
}
}
return null;
}
function readLocalConfigFile() {
try { try {
// Resolve via the content/ symlink relative to the Eleventy project const configPath = resolve(
const configPath = resolve(__dirname, "..", "content", ".indiekit", "cv-page.json"); __dirname,
"..",
"content",
".indiekit",
"cv-page.json"
);
const raw = readFileSync(configPath, "utf8"); const raw = readFileSync(configPath, "utf8");
const config = JSON.parse(raw); const config = JSON.parse(raw);
console.log("[cvPageConfig] Loaded CV page builder config"); console.log("[cvPageConfig] Loaded local CV page builder config");
return config; return config;
} catch { } catch {
// No CV page builder config — fall back to hardcoded layout in cv.njk
return null; return null;
} }
} }
export default async function () {
const apiConfig = await fetchFromIndiekit("page.json");
if (apiConfig && typeof apiConfig === "object") {
return apiConfig;
}
const localConfig = readLocalConfigFile();
if (localConfig && typeof localConfig === "object") {
return localConfig;
}
return null;
}