fix: batch 5 — input validation tightening + health check endpoint
- 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 <noreply@anthropic.com>
This commit is contained in:
@@ -69,5 +69,8 @@ EXPOSE 7600
|
|||||||
ENV PORT=7600
|
ENV PORT=7600
|
||||||
ENV HOSTNAME="0.0.0.0"
|
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)
|
# Run via entrypoint (migrate then start)
|
||||||
CMD ["/app/docker-entrypoint.sh"]
|
CMD ["/app/docker-entrypoint.sh"]
|
||||||
|
|||||||
@@ -210,11 +210,16 @@ export default function WebhooksPage() {
|
|||||||
return
|
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 = {
|
const payload = {
|
||||||
name: formData.name,
|
name: formData.name,
|
||||||
url: formData.url,
|
url: formData.url,
|
||||||
events: formData.events,
|
events: formData.events,
|
||||||
headers: formData.headers.filter((h) => h.key) as Record<string, string>[] | undefined,
|
headers: headersRecord,
|
||||||
maxRetries: formData.maxRetries,
|
maxRetries: formData.maxRetries,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ export const userRouter = router({
|
|||||||
notificationPreference: z.enum(['EMAIL', 'WHATSAPP', 'BOTH', 'NONE']).optional(),
|
notificationPreference: z.enum(['EMAIL', 'WHATSAPP', 'BOTH', 'NONE']).optional(),
|
||||||
expertiseTags: z.array(z.string()).max(15).optional(),
|
expertiseTags: z.array(z.string()).max(15).optional(),
|
||||||
digestFrequency: z.enum(['none', 'daily', 'weekly']).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(),
|
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(),
|
status: z.enum(['NONE', 'INVITED', 'ACTIVE', 'SUSPENDED']).optional(),
|
||||||
expertiseTags: z.array(z.string()).optional(),
|
expertiseTags: z.array(z.string()).optional(),
|
||||||
maxAssignments: z.number().int().min(1).max(100).optional().nullable(),
|
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(),
|
preferredWorkload: z.number().int().min(1).max(100).optional().nullable(),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export const webhookRouter = router({
|
|||||||
name: z.string().min(1).max(200),
|
name: z.string().min(1).max(200),
|
||||||
url: z.string().url(),
|
url: z.string().url(),
|
||||||
events: z.array(z.string()).min(1),
|
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),
|
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(),
|
name: z.string().min(1).max(200).optional(),
|
||||||
url: z.string().url().optional(),
|
url: z.string().url().optional(),
|
||||||
events: z.array(z.string()).min(1).optional(),
|
events: z.array(z.string()).min(1).optional(),
|
||||||
headers: z.any().optional(),
|
headers: z.record(z.string()).optional(),
|
||||||
isActive: z.boolean().optional(),
|
isActive: z.boolean().optional(),
|
||||||
maxRetries: z.number().int().min(0).max(10).optional(),
|
maxRetries: z.number().int().min(0).max(10).optional(),
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user