mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
Pure-history medication detail page, legacy step consolidation, and the finish of the outbound-fetch migration. - /medications/[id] becomes a read-only history surface; every setting moves into a new advanced-settings sheet, and the edit trigger becomes a two-option picker. The list page is unchanged. - A pg-boss job folds pre-v1.5.0 granular step rows into one canonical daily total per user per day and soft-deletes the originals; migration 0087 adds a partial index for the converging discovery scan. - Every remaining server-side fetch routes through safeFetch (Withings, Codex OAuth + SSE, bug-report, Umami, GlitchTip, Loki); a new healthlog/safe-fetch-required ESLint rule guards the boundary. - Fixes: intake-edit dialog seeded from real values, consolidation skips a unique-constraint day instead of stranding it, avatar upload reads the body once under a bounded cap, egress lint rejects absolute URLs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
58 lines
1.6 KiB
TypeScript
58 lines
1.6 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { apiHandler } from "@/lib/api-handler";
|
|
import { annotate } from "@/lib/logging/context";
|
|
import { getPublicMonitoringSettings } from "@/lib/monitoring-settings";
|
|
import { safeFetch } from "@/lib/safe-fetch";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
const NOOP_SCRIPT = "/* umami disabled */";
|
|
|
|
export const GET = apiHandler(async () => {
|
|
annotate({ action: { name: "monitoring.umami-script" } });
|
|
|
|
const settings = await getPublicMonitoringSettings();
|
|
if (
|
|
!settings.umamiEnabled ||
|
|
!settings.umamiScriptUrl ||
|
|
!settings.umamiWebsiteId
|
|
) {
|
|
return new NextResponse(NOOP_SCRIPT, {
|
|
status: 200,
|
|
headers: {
|
|
"Content-Type": "application/javascript; charset=utf-8",
|
|
"Cache-Control": "public, max-age=60",
|
|
},
|
|
});
|
|
}
|
|
|
|
const response = await safeFetch(
|
|
settings.umamiScriptUrl,
|
|
{
|
|
next: { revalidate: 3600 },
|
|
},
|
|
// Operator-configured host — pin the connect-time IP so a low-TTL
|
|
// DNS record cannot rebind the fetch at an internal range between
|
|
// the input-time `isPublicUrl` accept and the socket connect.
|
|
{ requirePublicHost: true },
|
|
);
|
|
if (!response.ok) {
|
|
return new NextResponse(NOOP_SCRIPT, {
|
|
status: 200,
|
|
headers: {
|
|
"Content-Type": "application/javascript; charset=utf-8",
|
|
"Cache-Control": "public, max-age=60",
|
|
},
|
|
});
|
|
}
|
|
|
|
const script = await response.text();
|
|
return new NextResponse(script, {
|
|
status: 200,
|
|
headers: {
|
|
"Content-Type": "application/javascript; charset=utf-8",
|
|
"Cache-Control": "public, max-age=1800",
|
|
},
|
|
});
|
|
});
|