chore(release): v1.4.45 — analytics 9 s perf fix + audit-marathon closure + v1.4.44 hotfix merged

Audit-marathon close: 14 waves + W10 QA + reconcile. Perf fix (analytics 9 s -> 2-3 s), Zod multi-issue rollout (41 routes), Withings parked-state automation, mobile-UI WCAG 2.5.5 close, QoL i18n + offline banner + account delete, security PII drop + replay-injection scan. Migration 0075 required. Tests 4815 -> 5076.
This commit is contained in:
Marc-André Bombeck authored and GitHub committed 2026-05-21 23:13:21 +02:00
1 parent 324d4bb411
commit 6c7fc7dc84
167 files changed
+11233 -371

No files matched your search

@@ -505,4 +505,46 @@ describe("PUT /api/admin/settings", () => {
expect(res.status).toBe(422);
expect(prisma.appSettings.upsert).not.toHaveBeenCalled();
});
describe("v1.4.43 W6 — multi-issue 422 envelope", () => {
it("surfaces TWO simultaneous validation errors", async () => {
// Bad `registrationEnabled` (string not boolean) + bad
// `reminderLateMinutes` (string not int).
const res = await PUT(
jsonReq({
registrationEnabled: "string",
reminderLateMinutes: "string",
}),
);
expect(res.status).toBe(422);
const body = (await res.json()) as {
data: null;
error: string;
details: {
issues: Array<{ path: string; code: string; message: string }>;
};
};
expect(body.data).toBeNull();
expect(body.error).toBe("Validation failed");
expect(body.details.issues.length).toBeGreaterThanOrEqual(2);
for (const issue of body.details.issues) {
expect(Object.keys(issue).sort()).toEqual(["code", "message", "path"]);
}
});
it("surfaces THREE simultaneous validation errors", async () => {
const res = await PUT(
jsonReq({
registrationEnabled: "string",
reminderLateMinutes: "string",
reminderMissedMinutes: "string",
}),
);
expect(res.status).toBe(422);
const body = (await res.json()) as {
details: { issues: Array<unknown> };
};
expect(body.details.issues.length).toBeGreaterThanOrEqual(3);
});
});
});
@@ -154,4 +154,47 @@ describe("PUT /api/admin/settings/assistant-flags", () => {
const res = await PUT(putReq({ assistantPotatoEnabled: false }));
expect(res.status).toBe(422);
});
describe("v1.4.43 W6 — multi-issue 422 envelope", () => {
it("surfaces TWO simultaneous validation errors", async () => {
vi.mocked(getSession).mockResolvedValue(ADMIN_OK as never);
// Two bad-typed flags.
const res = await PUT(
putReq({
assistantEnabled: "string",
assistantCoachEnabled: 999,
}),
);
expect(res.status).toBe(422);
const body = (await res.json()) as {
data: null;
error: string;
details: {
issues: Array<{ path: string; code: string; message: string }>;
};
};
expect(body.data).toBeNull();
expect(body.error).toBe("Validation failed");
expect(body.details.issues.length).toBeGreaterThanOrEqual(2);
for (const issue of body.details.issues) {
expect(Object.keys(issue).sort()).toEqual(["code", "message", "path"]);
}
});
it("surfaces THREE simultaneous validation errors", async () => {
vi.mocked(getSession).mockResolvedValue(ADMIN_OK as never);
const res = await PUT(
putReq({
assistantEnabled: "string",
assistantCoachEnabled: 999,
assistantBriefingEnabled: "string",
}),
);
expect(res.status).toBe(422);
const body = (await res.json()) as {
details: { issues: Array<unknown> };
};
expect(body.details.issues.length).toBeGreaterThanOrEqual(3);
});
});
});
@@ -32,6 +32,7 @@ import {
apiSuccess,
apiError,
getClientIp,
returnAllZodIssues,
safeJson,
} from "@/lib/api-response";
import { auditLog } from "@/lib/auth/audit";
@@ -124,7 +125,8 @@ export const PUT = apiHandler(async (request: NextRequest) => {
const parsed = assistantFlagsSchema.safeParse(body);
if (!parsed.success) {
return apiError(parsed.error.issues[0].message, 422);
// v1.4.43 W6 — multi-issue 422.
return returnAllZodIssues(parsed.error, 422);
}
const updates: Partial<AssistantFlagsRow> = {};
+3 -1
View File
@@ -5,6 +5,7 @@ import {
apiSuccess,
apiError,
getClientIp,
returnAllZodIssues,
safeJson,
} from "@/lib/api-response";
import { annotate } from "@/lib/logging/context";
@@ -71,7 +72,8 @@ export const PUT = apiHandler(async (request: NextRequest) => {
if (jsonError) return jsonError;
const parsed = adminSettingsSchema.safeParse(body);
if (!parsed.success) {
return apiError(parsed.error.issues[0].message, 422);
// v1.4.43 W6 — multi-issue 422.
return returnAllZodIssues(parsed.error, 422);
}
const data = parsed.data;