chore(release): v1.4.40 — comprehensive Critical+High architecture closure plus iOS PB30 enablement

Eleven touch-disjoint waves landed on develop plus a six-axis QA
reconcile round before tagging. See CHANGELOG.md for the user-facing
prose; this commit message documents the wave + report inventory for
release-management traceability.

Waves shipped (each with a phase report under .planning/):

- phase-W-POOL-v1440-report.md — p-limit(4) wrapper around the
  analytics 15-way fan-out + pg.Pool max raised 10 -> 20. Closes the
  v1.4.39 empirical-trace finding #1 (Prisma pool starvation).
- phase-W-DELETED-v1440-report.md — 11 reader-tier helpers + 3
  integration contracts close audit Critical Finding #3 (soft-delete
  invisibility) across dashboard, analytics, summaries, rollup tiers,
  and the Coach snapshot path.
- phase-W-INSIGHTS-v1440-report.md — mood-rollup swap on the three
  /api/insights/{features,targets,cards} routes plus the
  compliance-rollup hook gap closure for bulk-projection mints; the
  post-QA reconcile patch closes the six remaining measurement.findMany
  sites in the cluster that still leaked tombstoned rows.
- phase-W-INFRA-v1440-report.md — rollup helper umbrella consolidation
  under src/lib/rollups/* with zero orphan re-exports; lint, typecheck,
  knip, and 93/93 rollup tests green post-restructure.
- phase-W-RSC-v1440-report.md — per-tile Suspense boundaries plus the
  queryKey factory-bypass walker test (extended to comparison/ in the
  reconcile pass).
- phase-W-PRIVACY-v1440-report.md — bilingual /privacy page with 17
  paired-section tests pinning the 9 SB-3 disclosure requirements.
- phase-W-AASA-v1440-report.md — /.well-known/apple-app-site-association
  endpoint pinned to S8WDX4W5KX.dev.healthlog.app with app-ID parity
  assertions guarding against split rotation.
- phase-W-APNS-NOTIFY-v1440-report.md — MEDICATION_REMINDER
  time-sensitive APNs payload (priority 10 + interruption-level)
  scoped via a parameterised test that pins all 6 other event-types
  do not bypass Focus. AP-2 .p8 install gates real delivery.
- phase-W-CONSENT-v1440-report.md — AI consent receipts CRUD with
  append-only invariant pinned in test; revoke-then-regrant leaves
  both rows; idempotent DELETE returns 200 + receipt: null.
- phase-W-WMY-WIRE-v1440-report.md — three previously-dead WMY readers
  (week/month/year) wired through summaries-slice and
  health-score-fast-path with a linear-composability parity test
  pinning the cross-consumer routing contract.
- phase-W-GHOSTS-v1440-report.md — ~1 177 lines of dead code removed
  across 9 atomic commits; startOfUtcDay + wallClockInTz consolidation
  eliminates a sibling-drift risk; knip whitelist trimmed to three
  files pending follow-up.

QA reconcile patches applied per the v1.4.40 QA round (six reports
under .planning/round-v1440-QA-*-findings.md):

- senior C1 — dashboard-suspense-boundaries regex aligned with the
  post-W-INFRA Thread 2 userTimezone-local lift.
- senior H1 — soft-delete filter added to the six remaining
  insights measurement.findMany sites.
- specialist H1 — Promise.allSettled around bulk-projection
  compliance recompute on both /api/medications/intake and
  /api/dashboard/summary.
- ux H1 — compare-toggle migrated to queryKeys.dashboardWidgets
  factory; walker test extended to src/components/comparison/.
- security M1 — consent artefact 64 KB cap enforced via
  Buffer.byteLength (UTF-8 bytes, not UTF-16 code units).

Quality gates green at tag: pnpm typecheck, pnpm lint, pnpm knip
--include files,dependencies,binaries,unlisted, pnpm test --run
(4726 passing / 1 skipped). Tests 4525 -> 4726 unit + 1 skipped over
the v1.4.39 -> v1.4.40 span.

User-facing prose: see CHANGELOG.md.
This commit is contained in:
Marc-André Bombeck committed 2026-05-21 12:48:02 +02:00
1 parent f9996552ed
commit 1114cf7e63
146 files changed
+8207 -2279

No files matched your search

@@ -1,164 +0,0 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
vi.mock("@/lib/api-handler", () => ({
apiHandler: <T extends (...args: unknown[]) => unknown>(fn: T) => fn,
requireAdmin: vi.fn(async () => ({
user: { id: "u-admin" },
session: { id: "s-1" },
})),
}));
vi.mock("@/lib/db", () => ({
prisma: {
appSettings: {
findUnique: vi.fn(),
},
},
}));
vi.mock("@/lib/rate-limit", () => ({
checkRateLimit: vi.fn(async () => ({ allowed: true })),
}));
vi.mock("@/lib/logging/context", () => ({
annotate: vi.fn(),
}));
vi.mock("@/lib/crypto", () => ({
decrypt: vi.fn((x: string) => x),
}));
vi.mock("@/lib/monitoring/glitchtip", () => ({
sendGlitchtipEvent: vi.fn(),
}));
import { POST } from "../route";
import { prisma } from "@/lib/db";
import { checkRateLimit } from "@/lib/rate-limit";
import { sendGlitchtipEvent } from "@/lib/monitoring/glitchtip";
interface ApiErrorEnvelope {
data: null;
error: string;
}
interface ApiSuccessEnvelope<T> {
data: T;
error: null;
}
const originalFetch = global.fetch;
beforeEach(() => {
vi.mocked(checkRateLimit).mockResolvedValue({
allowed: true,
remaining: 5,
resetAt: Date.now() + 60_000,
} as never);
vi.mocked(prisma.appSettings.findUnique).mockReset();
vi.mocked(sendGlitchtipEvent).mockReset();
global.fetch = vi.fn() as never;
});
afterEach(() => {
global.fetch = originalFetch;
});
function emptyRequest(): Request {
return new Request("http://localhost/api/monitoring/glitchtip/test", {
method: "POST",
headers: { "content-length": "0" },
});
}
describe("POST /api/monitoring/glitchtip/test", () => {
it("happy path returns ok with statusCode", async () => {
vi.mocked(prisma.appSettings.findUnique).mockResolvedValueOnce({
glitchtipDsn: "https://abc@glitch.example.com/1",
} as never);
vi.mocked(sendGlitchtipEvent).mockResolvedValueOnce({
ok: true,
method: "envelope",
status: 200,
});
const response = await POST(emptyRequest() as never);
expect(response.status).toBe(200);
const body = (await response.json()) as ApiSuccessEnvelope<{
ok: boolean;
statusCode: number;
latencyMs: number;
}>;
expect(body.data.ok).toBe(true);
expect(body.data.statusCode).toBe(200);
expect(typeof body.data.latencyMs).toBe("number");
// CRITICAL guard against the `id: "default"` typo regression.
expect(prisma.appSettings.findUnique).toHaveBeenCalledWith(
expect.objectContaining({ where: { id: "singleton" } }),
);
});
it("rejects HTTP DSN with 422", async () => {
vi.mocked(prisma.appSettings.findUnique).mockResolvedValueOnce({
glitchtipDsn: "http://abc@glitch.example.com/1",
} as never);
const response = await POST(emptyRequest() as never);
expect(response.status).toBe(422);
const body = (await response.json()) as ApiErrorEnvelope & {
meta: { errorCode: string };
};
expect(body.meta.errorCode).toBe("dsn_not_https");
expect(sendGlitchtipEvent).not.toHaveBeenCalled();
});
it("rejects DSN pointing at a private host (SSRF guard)", async () => {
vi.mocked(prisma.appSettings.findUnique).mockResolvedValueOnce({
glitchtipDsn: "https://abc@127.0.0.1/1",
} as never);
const response = await POST(emptyRequest() as never);
expect(response.status).toBe(422);
const body = (await response.json()) as ApiErrorEnvelope & {
meta: { errorCode: string };
};
expect(body.meta.errorCode).toBe("dsn_host_not_public");
expect(sendGlitchtipEvent).not.toHaveBeenCalled();
});
it("rate-limit denial returns 429 with no upstream call", async () => {
vi.mocked(checkRateLimit).mockResolvedValueOnce({
allowed: false,
remaining: 0,
resetAt: Date.now() + 60_000,
} as never);
const response = await POST(emptyRequest() as never);
expect(response.status).toBe(429);
expect(sendGlitchtipEvent).not.toHaveBeenCalled();
});
it("returns 422 when DSN is not configured", async () => {
vi.mocked(prisma.appSettings.findUnique).mockResolvedValueOnce({
glitchtipDsn: null,
} as never);
const response = await POST(emptyRequest() as never);
expect(response.status).toBe(422);
const body = (await response.json()) as ApiErrorEnvelope;
expect(body.error).toMatch(/not configured/i);
expect(sendGlitchtipEvent).not.toHaveBeenCalled();
});
it("does not leak DSN/Bearer/sk- on upstream 401", async () => {
vi.mocked(prisma.appSettings.findUnique).mockResolvedValueOnce({
glitchtipDsn: "https://abc@glitch.example.com/1",
} as never);
vi.mocked(sendGlitchtipEvent).mockRejectedValueOnce(
new Error(
"401 Unauthorized: Authorization: Bearer sk-secret from https://glitch.example.com/api/1/envelope/",
),
);
const response = await POST(emptyRequest() as never);
const text = await response.text();
expect(response.status).toBe(502);
expect(text).not.toMatch(/sk-/);
expect(text).not.toMatch(/glitch\.example\.com/);
});
});
@@ -1,121 +0,0 @@
import type { NextRequest } from "next/server";
import { apiHandler, requireAdmin } from "@/lib/api-handler";
import { apiSuccess, apiError } from "@/lib/api-response";
import { checkRateLimit } from "@/lib/rate-limit";
import { annotate } from "@/lib/logging/context";
import { prisma } from "@/lib/db";
import { sendGlitchtipEvent } from "@/lib/monitoring/glitchtip";
import { isPublicUrl } from "@/lib/validations/notifications";
export const dynamic = "force-dynamic";
const TIMEOUT_MS = 5_000;
export const POST = apiHandler(async (request: NextRequest) => {
void request;
const { user } = await requireAdmin();
annotate({ action: { name: "monitoring.glitchtip.test" } });
const rl = await checkRateLimit(`glitchtip-test:${user.id}`, 5, 60_000);
if (!rl.allowed) {
return apiError("Too many test requests", 429, {
errorCode: "rate_limited_self",
});
}
const settings = await prisma.appSettings.findUnique({
where: { id: "singleton" },
select: { glitchtipDsn: true },
});
if (!settings?.glitchtipDsn) {
return apiError("Glitchtip DSN not configured", 422, {
errorCode: "not_configured",
});
}
// DSN is admin-set, but a compromised/social-engineered admin (or a stored
// settings record from a hostile import) could aim Glitchtip at internal
// hosts. Validate the host is publicly reachable and uses HTTPS before we
// hand the URL to the helper.
let dsnHost: string;
try {
const parsed = new URL(settings.glitchtipDsn);
if (parsed.protocol !== "https:") {
return apiError("Glitchtip DSN must use HTTPS", 422, {
errorCode: "dsn_not_https",
});
}
if (!isPublicUrl(`${parsed.protocol}//${parsed.host}`)) {
return apiError("Glitchtip DSN host is not publicly reachable", 422, {
errorCode: "dsn_host_not_public",
});
}
dsnHost = parsed.host;
} catch {
return apiError("Glitchtip DSN is malformed", 422, {
errorCode: "dsn_invalid",
});
}
const start = performance.now();
const send = sendGlitchtipEvent({
dsn: settings.glitchtipDsn,
input: {
environment: process.env.NODE_ENV ?? "production",
message: "HealthLog Glitchtip self-test",
level: "info",
// sourceTag rides through into the event tags. Adding "noise: true" lets
// operators silence these in their inbound Glitchtip rules so the test
// button doesn't pollute the issue inbox.
sourceTag: "self-test:noise",
},
});
const timeoutErr = new Error("timeout");
timeoutErr.name = "AbortError";
const timeout = new Promise<never>((_, reject) =>
setTimeout(() => reject(timeoutErr), TIMEOUT_MS),
);
try {
const result = await Promise.race([send, timeout]);
const latencyMs = Math.round(performance.now() - start);
if (!result.ok) {
annotate({
meta: {
glitchtip_test_status: result.status ?? null,
glitchtip_test_method: result.method ?? null,
glitchtip_test_latency_ms: latencyMs,
glitchtip_test_host: dsnHost,
},
});
return apiError("Glitchtip rejected the event", 502, {
errorCode: "upstream_error",
});
}
return apiSuccess({
ok: true,
statusCode: result.status ?? 200,
latencyMs,
});
} catch (e) {
const err = e as Error;
const isTimeout = err.name === "AbortError";
const code = isTimeout ? "timeout" : "connection_failed";
annotate({
meta: {
glitchtip_test_code: code,
glitchtip_test_host: dsnHost,
glitchtip_test_error: err.message.slice(0, 200),
},
});
return apiError(
isTimeout ? "Glitchtip request timed out" : "Glitchtip connection failed",
502,
{ errorCode: code },
);
}
});
@@ -1,135 +0,0 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
vi.mock("@/lib/api-handler", () => ({
apiHandler: <T extends (...args: unknown[]) => unknown>(fn: T) => fn,
requireAdmin: vi.fn(async () => ({
user: { id: "u-admin" },
session: { id: "s-1" },
})),
}));
vi.mock("@/lib/db", () => ({
prisma: {
appSettings: {
findUnique: vi.fn(),
},
},
}));
vi.mock("@/lib/rate-limit", () => ({
checkRateLimit: vi.fn(async () => ({ allowed: true })),
}));
vi.mock("@/lib/logging/context", () => ({
annotate: vi.fn(),
}));
vi.mock("@/lib/crypto", () => ({
decrypt: vi.fn((x: string) => x),
}));
import { POST } from "../route";
import { prisma } from "@/lib/db";
import { checkRateLimit } from "@/lib/rate-limit";
interface ApiErrorEnvelope {
data: null;
error: string;
}
interface ApiSuccessEnvelope<T> {
data: T;
error: null;
}
const originalFetch = global.fetch;
beforeEach(() => {
vi.mocked(checkRateLimit).mockResolvedValue({
allowed: true,
remaining: 5,
resetAt: Date.now() + 60_000,
} as never);
vi.mocked(prisma.appSettings.findUnique).mockReset();
global.fetch = vi.fn() as never;
});
afterEach(() => {
global.fetch = originalFetch;
});
function emptyRequest(): Request {
return new Request("http://localhost/api/monitoring/umami/test", {
method: "POST",
headers: { "content-length": "0" },
});
}
describe("POST /api/monitoring/umami/test", () => {
it("happy path returns ok with hasMarker=true", async () => {
vi.mocked(prisma.appSettings.findUnique).mockResolvedValueOnce({
umamiScriptUrl: "https://analytics.example.com/script.js",
} as never);
(global.fetch as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce(
new Response("(function(){window.umami=function(){};})();", {
status: 200,
}),
);
const response = await POST(emptyRequest() as never);
expect(response.status).toBe(200);
const body = (await response.json()) as ApiSuccessEnvelope<{
ok: boolean;
statusCode: number;
hasMarker: boolean;
}>;
expect(body.data.ok).toBe(true);
expect(body.data.hasMarker).toBe(true);
expect(body.data.statusCode).toBe(200);
// CRITICAL guard: AppSettings is keyed on `id: "singleton"` everywhere
// else in the codebase. A typo here ("default") would silently make the
// route return 422 in prod even with valid settings.
expect(prisma.appSettings.findUnique).toHaveBeenCalledWith(
expect.objectContaining({ where: { id: "singleton" } }),
);
});
it("rate-limit denial returns 429 with no upstream call", async () => {
vi.mocked(checkRateLimit).mockResolvedValueOnce({
allowed: false,
remaining: 0,
resetAt: Date.now() + 60_000,
} as never);
const response = await POST(emptyRequest() as never);
expect(response.status).toBe(429);
expect(global.fetch).not.toHaveBeenCalled();
});
it("returns 422 when URL not configured", async () => {
vi.mocked(prisma.appSettings.findUnique).mockResolvedValueOnce({
umamiScriptUrl: null,
} as never);
const response = await POST(emptyRequest() as never);
expect(response.status).toBe(422);
const body = (await response.json()) as ApiErrorEnvelope;
expect(body.error).toMatch(/not configured/i);
expect(global.fetch).not.toHaveBeenCalled();
});
it("does not leak Bearer/sk- or upstream URL on upstream 401", async () => {
vi.mocked(prisma.appSettings.findUnique).mockResolvedValueOnce({
umamiScriptUrl: "https://analytics.example.com/script.js",
} as never);
(global.fetch as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce(
new Response(
"Authorization: Bearer sk-secret echoed back from https://analytics.example.com/script.js",
{ status: 401 },
),
);
const response = await POST(emptyRequest() as never);
const text = await response.text();
expect(response.status).toBe(502);
expect(text).not.toMatch(/sk-/);
expect(text).not.toMatch(/analytics\.example\.com/);
});
});
-130
View File
@@ -1,130 +0,0 @@
import type { NextRequest } from "next/server";
import { apiHandler, requireAdmin } from "@/lib/api-handler";
import { apiSuccess, apiError } from "@/lib/api-response";
import { checkRateLimit } from "@/lib/rate-limit";
import { annotate } from "@/lib/logging/context";
import { prisma } from "@/lib/db";
import { isPublicUrl } from "@/lib/validations/notifications";
export const dynamic = "force-dynamic";
const TIMEOUT_MS = 5_000;
const MAX_BODY_BYTES = 256 * 1024;
function redact(text: string): string {
return text.replace(/https?:\/\/\S+/gi, "[url]");
}
export const POST = apiHandler(async (request: NextRequest) => {
void request;
const { user } = await requireAdmin();
annotate({ action: { name: "monitoring.umami.test" } });
const rl = await checkRateLimit(`umami-test:${user.id}`, 5, 60_000);
if (!rl.allowed) {
return apiError("Too many test requests", 429, {
errorCode: "rate_limited_self",
});
}
const settings = await prisma.appSettings.findUnique({
where: { id: "singleton" },
select: { umamiScriptUrl: true },
});
if (!settings?.umamiScriptUrl) {
return apiError("Umami script URL not configured", 422, {
errorCode: "not_configured",
});
}
const url = settings.umamiScriptUrl;
if (!isPublicUrl(url) || !url.startsWith("https://")) {
return apiError("Umami URL must be a public HTTPS endpoint", 422, {
errorCode: "url_not_public",
});
}
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
const start = performance.now();
try {
const res = await fetch(url, {
method: "GET",
signal: controller.signal,
cache: "no-store",
// `manual` neutralises a redirect-follow SSRF — even though the URL was
// checked with isPublicUrl(), a public host can 302 to a private IP.
redirect: "manual",
});
const latencyMs = Math.round(performance.now() - start);
if (res.status >= 300 && res.status < 400) {
annotate({ meta: { umami_test_status: res.status } });
return apiError("Umami URL redirects — check configuration", 502, {
errorCode: "redirected",
});
}
// Cap response read to 256 KB so a hostile public server can't stream
// gigabytes through us; combined with the 5 s abort, RAM use stays bounded.
let body = "";
if (res.body) {
const reader = res.body.getReader();
const decoder = new TextDecoder();
let total = 0;
try {
while (total < MAX_BODY_BYTES) {
const { done, value } = await reader.read();
if (done) break;
total += value.byteLength;
body += decoder.decode(value, { stream: true });
}
body += decoder.decode();
} finally {
reader.releaseLock();
}
}
if (!res.ok) {
annotate({
meta: {
umami_test_status: res.status,
umami_test_latency_ms: latencyMs,
},
});
return apiError("Umami connection failed", 502, {
errorCode: "upstream_error",
});
}
// Look for tracker-shape signal rather than a literal "umami" substring —
// a 404 page that mentions "Powered by Umami" would otherwise slip through.
const hasMarker = /umami\.track|window\.umami|"umami"/i.test(body);
return apiSuccess({
ok: hasMarker,
statusCode: res.status,
latencyMs,
hasMarker,
});
} catch (e) {
const err = e as Error;
const isAbort = err.name === "AbortError";
const code = isAbort ? "timeout" : "connection_failed";
annotate({
meta: {
umami_test_code: code,
umami_test_error: redact(err.message).slice(0, 300),
},
});
return apiError(
isAbort ? "Umami request timed out" : "Umami connection failed",
502,
{ errorCode: code },
);
} finally {
clearTimeout(timer);
}
});