diff --git a/apps/web/app/api/albums/handlers.ts b/apps/web/app/api/albums/handlers.ts index 441be3c..a98fd62 100644 --- a/apps/web/app/api/albums/handlers.ts +++ b/apps/web/app/api/albums/handlers.ts @@ -61,7 +61,15 @@ export async function handleCreateAlbum(input: { return { status: 401, body: { error: "admin_required" } }; } - const body = createAlbumBodySchema.parse(input.body ?? {}); + const bodyParsed = createAlbumBodySchema.safeParse(input.body ?? {}); + if (!bodyParsed.success) { + return { + status: 400, + body: { error: "invalid_body", issues: bodyParsed.error.issues }, + }; + } + + const body = bodyParsed.data; const db = (input.db ?? getDb()) as DbLike; const rows = await db< { diff --git a/apps/web/app/api/tags/handlers.ts b/apps/web/app/api/tags/handlers.ts index 4c4c2d0..7a81e5c 100644 --- a/apps/web/app/api/tags/handlers.ts +++ b/apps/web/app/api/tags/handlers.ts @@ -50,7 +50,15 @@ export async function handleCreateTag(input: { return { status: 401, body: { error: "admin_required" } }; } - const body = createTagBodySchema.parse(input.body ?? {}); + const bodyParsed = createTagBodySchema.safeParse(input.body ?? {}); + if (!bodyParsed.success) { + return { + status: 400, + body: { error: "invalid_body", issues: bodyParsed.error.issues }, + }; + } + + const body = bodyParsed.data; const db = (input.db ?? getDb()) as DbLike; const rows = await db< { diff --git a/apps/web/src/__tests__/albums-admin-auth.test.ts b/apps/web/src/__tests__/albums-admin-auth.test.ts index 3f78be9..7d32f98 100644 --- a/apps/web/src/__tests__/albums-admin-auth.test.ts +++ b/apps/web/src/__tests__/albums-admin-auth.test.ts @@ -100,6 +100,14 @@ test("albums POST inserts and writes audit log", async () => { ); }); +test("albums POST rejects invalid body", async () => { + const { handleCreateAlbum } = await import("../../app/api/albums/handlers"); + const res = await handleCreateAlbum({ adminOk: true, body: { name: "" } }); + expect(res.status).toBe(400); + expect(res.body).toMatchObject({ error: "invalid_body" }); + expect(Array.isArray((res.body as { issues?: unknown }).issues)).toBe(true); +}); + test("album add asset inserts and writes audit log", async () => { const { handleAddAlbumAsset } = await import( "../../app/api/albums/handlers" diff --git a/apps/web/src/__tests__/tags-admin-auth.test.ts b/apps/web/src/__tests__/tags-admin-auth.test.ts index c88bd48..e74c438 100644 --- a/apps/web/src/__tests__/tags-admin-auth.test.ts +++ b/apps/web/src/__tests__/tags-admin-auth.test.ts @@ -73,3 +73,11 @@ test("tags POST inserts and writes audit log", async () => { true, ); }); + +test("tags POST rejects invalid body", async () => { + const { handleCreateTag } = await import("../../app/api/tags/handlers"); + const res = await handleCreateTag({ adminOk: true, body: { name: "" } }); + expect(res.status).toBe(400); + expect(res.body).toMatchObject({ error: "invalid_body" }); + expect(Array.isArray((res.body as { issues?: unknown }).issues)).toBe(true); +});