93 lines
2.9 KiB
TypeScript
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 });
|
|
}
|
|
};
|