feat: Zod validation on all API inputs (PRD §5.2)

26 files with Zod schemas, 27 safeParse calls.
All POST/PATCH handlers now validate input:
- Missing required fields → 400 with structured error
- Invalid types (string vs number) → 400
- Out-of-range values (probability >100, negative amounts) → 400
- Valid data still works (behavior preserved)

Pattern: schema.safeParse(await req.json()) → 400 on failure
This commit is contained in:
Z User committed 2026-10-27 21:29:51 +00:00
1 parent 3d56770669
commit 2614ce4e3d
25 files changed
+316 -116

No files matched your search

+13 -6
View File
@@ -1,4 +1,5 @@
import { NextResponse } from 'next/server'
import { z } from 'zod'
import * as net from 'net'
export const dynamic = 'force-dynamic'
@@ -13,6 +14,14 @@ interface MailTestResult {
error?: string
}
const testMailSchema = z.object({
smtpHost: z.string().optional(),
smtpPort: z.number().int().min(0).max(65535).optional(),
imapHost: z.string().optional(),
imapPort: z.number().int().min(0).max(65535).optional(),
user: z.string().optional(),
})
// Real TCP connection test to an SMTP/IMAP server.
// Attempts to open a socket, read the greeting banner, then close.
// No fake success — returns the actual connection result or error.
@@ -76,13 +85,11 @@ function testConnection(host: string, port: number, timeoutMs = 8000): Promise<M
// POST /api/aos/setup/test-mail
// Real SMTP + IMAP connection test. No simulation.
export async function POST(req: Request) {
const body = (await req.json().catch(() => ({}))) as {
smtpHost?: string
smtpPort?: number
imapHost?: string
imapPort?: number
user?: string
const parsed = testMailSchema.safeParse(await req.json().catch(() => ({})))
if (!parsed.success) {
return NextResponse.json({ error: 'validation failed', details: parsed.error.flatten() }, { status: 400 })
}
const body = parsed.data
const [smtp, imap] = await Promise.all([
testConnection(body.smtpHost ?? '', Number(body.smtpPort) || 0),