From 1ebdf5f9c9ea72a98f7ab5be9a148271952d2047 Mon Sep 17 00:00:00 2001 From: Matt Date: Sat, 7 Mar 2026 18:26:28 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20batch=205=20=E2=80=94=20input=20validati?= =?UTF-8?q?on=20tightening=20+=20health=20check=20endpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - z.any() replaced with z.record(z.string()) on webhook headers - availabilityJson typed with z.array(z.object({ start, end })) - Frontend webhook headers converted from array to Record before API call - Docker HEALTHCHECK added to Dockerfile (health endpoint already existed) Co-Authored-By: Claude Opus 4.6 --- docker/Dockerfile | 3 +++ src/app/(admin)/admin/settings/webhooks/page.tsx | 7 ++++++- src/server/routers/user.ts | 4 ++-- src/server/routers/webhook.ts | 4 ++-- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index d57b620..712fdda 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -69,5 +69,8 @@ EXPOSE 7600 ENV PORT=7600 ENV HOSTNAME="0.0.0.0" +HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ + CMD wget -qO- http://localhost:7600/api/health || exit 1 + # Run via entrypoint (migrate then start) CMD ["/app/docker-entrypoint.sh"] diff --git a/src/app/(admin)/admin/settings/webhooks/page.tsx b/src/app/(admin)/admin/settings/webhooks/page.tsx index 64a5b27..557dffc 100644 --- a/src/app/(admin)/admin/settings/webhooks/page.tsx +++ b/src/app/(admin)/admin/settings/webhooks/page.tsx @@ -210,11 +210,16 @@ export default function WebhooksPage() { return } + const filteredHeaders = formData.headers.filter((h) => h.key) + const headersRecord = filteredHeaders.length > 0 + ? Object.fromEntries(filteredHeaders.map((h) => [h.key, h.value])) + : undefined + const payload = { name: formData.name, url: formData.url, events: formData.events, - headers: formData.headers.filter((h) => h.key) as Record[] | undefined, + headers: headersRecord, maxRetries: formData.maxRetries, } diff --git a/src/server/routers/user.ts b/src/server/routers/user.ts index 3b61923..71c3dfd 100644 --- a/src/server/routers/user.ts +++ b/src/server/routers/user.ts @@ -106,7 +106,7 @@ export const userRouter = router({ notificationPreference: z.enum(['EMAIL', 'WHATSAPP', 'BOTH', 'NONE']).optional(), expertiseTags: z.array(z.string()).max(15).optional(), digestFrequency: z.enum(['none', 'daily', 'weekly']).optional(), - availabilityJson: z.any().optional(), + availabilityJson: z.array(z.object({ start: z.string(), end: z.string() })).optional(), preferredWorkload: z.number().int().min(1).max(100).optional().nullable(), }) ) @@ -539,7 +539,7 @@ export const userRouter = router({ status: z.enum(['NONE', 'INVITED', 'ACTIVE', 'SUSPENDED']).optional(), expertiseTags: z.array(z.string()).optional(), maxAssignments: z.number().int().min(1).max(100).optional().nullable(), - availabilityJson: z.any().optional(), + availabilityJson: z.array(z.object({ start: z.string(), end: z.string() })).optional(), preferredWorkload: z.number().int().min(1).max(100).optional().nullable(), }) ) diff --git a/src/server/routers/webhook.ts b/src/server/routers/webhook.ts index 0b9b77d..a95feab 100644 --- a/src/server/routers/webhook.ts +++ b/src/server/routers/webhook.ts @@ -81,7 +81,7 @@ export const webhookRouter = router({ name: z.string().min(1).max(200), url: z.string().url(), events: z.array(z.string()).min(1), - headers: z.any().optional(), + headers: z.record(z.string()).optional(), maxRetries: z.number().int().min(0).max(10).default(3), }) ) @@ -126,7 +126,7 @@ export const webhookRouter = router({ name: z.string().min(1).max(200).optional(), url: z.string().url().optional(), events: z.array(z.string()).min(1).optional(), - headers: z.any().optional(), + headers: z.record(z.string()).optional(), isActive: z.boolean().optional(), maxRetries: z.number().int().min(0).max(10).optional(), })