website/src/pages/api/stats.json.ts
2026-09-09 15:21:35 -04:00

93 lines
2.9 KiB
TypeScript

import type { APIRoute } from "astro";
import { env as cfEnv } from "cloudflare:workers";
export const prerender = false;
type Env = Record<string, string | undefined>;
function getEnv(): Env {
const nodeEnv = typeof process !== "undefined" ? process.env : {};
let workerEnv: Env = {};
try {
workerEnv = cfEnv as unknown as Env;
} catch {
}
return { ...nodeEnv, ...workerEnv };
}
const num = (v: any): number =>
typeof v === "number" ? v : typeof v?.value === "number" ? v.value : 0;
export const GET: APIRoute = async () => {
const e = getEnv();
const base = (e.UMAMI_URL || "https://api.umami.is/v1").replace(/\/+$/, "");
const id = e.UMAMI_WEBSITE_ID;
const key = e.UMAMI_API_KEY;
const shareId = e.UMAMI_SHARE_ID;
const json = (data: unknown, status = 200) =>
new Response(JSON.stringify(data), {
status,
headers: { "content-type": "application/json", "cache-control": "public, max-age=300" },
});
const hidden = {
configured: false,
pageviews: 0,
visitors: 0,
visits: 0,
topPages: [] as { path: string; count: number }[],
topReferrers: [] as { ref: string; count: number }[],
};
if (!(shareId || (id && key))) return json(hidden);
const headers: Record<string, string> = { accept: "application/json" };
let websiteId = id;
try {
if (key) {
headers["x-umami-api-key"] = key;
} else if (shareId) {
const origin = base.replace(/\/v1$/, "").replace(/\/api$/, "");
const r = await fetch(`${origin}/api/share/${shareId}`, {
headers: { accept: "application/json" },
});
const s: any = await r.json();
if (s?.token) {
headers["x-umami-share-token"] = s.token;
headers["x-umami-share-context"] = "1";
}
if (s?.websiteId) websiteId = s.websiteId;
}
if (!websiteId) return json({ ...hidden, configured: true });
const w = `${base}/websites/${websiteId}`;
const range = `startAt=0&endAt=${Date.now()}`;
const get = (path: string) => fetch(`${w}/${path}`, { headers }).then((r) => r.json());
const [data, pages, refs] = await Promise.all([
get(`stats?${range}`),
get(`metrics?${range}&type=path&limit=6`).catch(() => []),
get(`metrics?${range}&type=referrer&limit=6`).catch(() => []),
]);
const pageviews = num((data as any)?.pageviews);
const visitors = num((data as any)?.visitors);
const visits = num((data as any)?.visits);
if (!pageviews && !visitors) return json({ ...hidden, configured: true });
const topPages = (Array.isArray(pages) ? pages : [])
.map((m: any) => ({ path: String(m?.x ?? ""), count: num(m?.y) }))
.filter((p) => p.path);
const topReferrers = (Array.isArray(refs) ? refs : [])
.map((m: any) => ({ ref: String(m?.x ?? ""), count: num(m?.y) }))
.filter((r) => r.ref);
return json({ configured: true, pageviews, visitors, visits, topPages, topReferrers });
} catch {
return json({ ...hidden, configured: true });
}
};