Fetch CV data via API with file fallback
This commit is contained in:
78
_data/cv.js
78
_data/cv.js
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user