Files
portifolio-evandrodevbr/lib/project-health.ts
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

113 lines
2.7 KiB
TypeScript

export const PROJECT_HEALTH_MAX_AGE_MS = 15 * 60 * 1000;
export interface ProjectHealthCheck {
status: number | null;
contentType: string | null;
checkedAt: string;
finalUrl?: string;
latencyMs?: number;
error?: string;
}
export interface ProjectHealthSnapshot {
checkedAt: string;
projects: Record<string, ProjectHealthCheck>;
}
interface ProjectLike {
id: string;
featured?: boolean;
}
function isFresh(checkedAt: string, now: Date) {
const timestamp = Date.parse(checkedAt);
return (
Number.isFinite(timestamp) &&
now.getTime() - timestamp <= PROJECT_HEALTH_MAX_AGE_MS
);
}
export function isCurrentLiveCheck(
check: ProjectHealthCheck | undefined,
now = new Date(),
) {
return Boolean(
check &&
check.status === 200 &&
check.contentType?.toLowerCase().startsWith("text/html") &&
isFresh(check.checkedAt, now),
);
}
export function filterLiveProjects<T extends ProjectLike>(
projects: T[],
snapshot: ProjectHealthSnapshot | null,
now = new Date(),
) {
if (!snapshot || !isFresh(snapshot.checkedAt, now)) {
return [] as T[];
}
return projects.filter(
(project) =>
project.featured !== false &&
isCurrentLiveCheck(snapshot.projects[project.id], now),
);
}
interface ProjectDemo {
id: string;
demoUrl: string;
}
interface HealthResponse {
status: number;
url: string;
headers: { get(name: string): string | null };
body?: { cancel?: () => unknown } | null;
}
type HealthFetcher = (url: string, init: RequestInit) => Promise<HealthResponse>;
export async function createProjectHealthSnapshot(
projects: ProjectDemo[],
fetcher: HealthFetcher = (url, init) => fetch(url, init),
now = new Date(),
): Promise<ProjectHealthSnapshot> {
const checkedAt = now.toISOString();
const checks = await Promise.all(
projects.map(async (project) => {
try {
const response = await fetcher(project.demoUrl, {
redirect: "follow",
signal: AbortSignal.timeout(10_000),
headers: { "user-agent": "evandro-project-health/1.0" },
});
await response.body?.cancel?.();
return [
project.id,
{
status: response.status,
contentType: response.headers.get("content-type"),
checkedAt,
finalUrl: response.url,
},
] as const;
} catch (error) {
return [
project.id,
{
status: null,
contentType: null,
checkedAt,
error: error instanceof Error ? error.message : "Health check failed",
},
] as const;
}
}),
);
return { checkedAt, projects: Object.fromEntries(checks) };
}