Files
portifolio-evandrodevbr/scripts/check-projects.mjs
T
Evandro Fonseca JuniorandClaude Sonnet 5 d76bbf5974 feat: revamp portfolio with case studies, curriculo page, and local SEO
Adds dynamic case study and curriculo routes, project health monitoring
for live demo status, and strengthens local SEO (structured data,
service-area targeting for Garuva/Joinville/Curitiba, a dedicated
"criação de sites" service page) so search results better match the
professional title (Engenheiro da Computação) and experience (5 anos).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-07-15 01:31:46 -03:00

69 lines
1.9 KiB
JavaScript

import { readFile, rename, writeFile } from "node:fs/promises";
import { resolve } from "node:path";
const root = process.cwd();
const sourcePath = resolve(root, "public/portfolio-data.json");
const outputPath = resolve(root, "data/project-health.json");
const timeoutMs = 10_000;
const data = JSON.parse(await readFile(sourcePath, "utf8"));
const projects = data.projects.filter((project) => project.demoUrl);
const checkedAt = new Date().toISOString();
const results = await Promise.all(
projects.map(async ({ id, demoUrl }) => {
const startedAt = Date.now();
try {
const response = await fetch(demoUrl, {
redirect: "follow",
signal: AbortSignal.timeout(timeoutMs),
headers: { "user-agent": "evandro-project-health/1.0" },
});
const contentType = response.headers.get("content-type");
await response.body?.cancel();
return [
id,
{
status: response.status,
contentType,
checkedAt,
finalUrl: response.url,
latencyMs: Date.now() - startedAt,
},
];
} catch (error) {
return [
id,
{
status: null,
contentType: null,
checkedAt,
latencyMs: Date.now() - startedAt,
error:
error instanceof Error
? error.message
: "Unknown health-check error",
},
];
}
}),
);
const snapshot = { checkedAt, projects: Object.fromEntries(results) };
const temporaryPath = `${outputPath}.${process.pid}.tmp`;
await writeFile(
temporaryPath,
`${JSON.stringify(snapshot, null, 2)}\n`,
"utf8",
);
await rename(temporaryPath, outputPath);
const live = Object.values(snapshot.projects).filter(
(check) =>
check.status === 200 &&
check.contentType?.toLowerCase().startsWith("text/html"),
).length;
console.log(`Checked ${projects.length} project demos; ${live} are live.`);