Files
wfh/src/lib/validation.ts
T

89 lines
2.4 KiB
TypeScript
Raw Normal View History

import { z } from "zod";
import { NextResponse } from "next/server";
export const TASK_STATUSES = ["PENDING", "DONE", "IN_PROGRESS", "CANCELLED"] as const;
export const REPORT_STATUSES = ["IN_PROGRESS", "COMPLETED", "SUBMITTED"] as const;
const httpLink = z
.string()
.trim()
.max(2000)
.refine((v) => v === "" || /^https?:\/\//i.test(v), {
message: "Link must start with http:// or https://",
});
export const createReportSchema = z.object({
managerName: z.string().trim().min(1).max(200),
date: z
.string()
.regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be YYYY-MM-DD")
.optional(),
});
export const updateReportSchema = z.object({
managerName: z.string().trim().min(1).max(200).optional(),
status: z.enum(REPORT_STATUSES).optional(),
});
export const createTaskSchema = z.object({
reportId: z.string().min(1),
type: z.enum(["PLANNED", "COMPLETED"]),
description: z.string().max(2000).default(""),
timeEstimate: z.string().max(100).nullish(),
notes: z.string().max(2000).nullish(),
link: httpLink.nullish(),
status: z.enum(TASK_STATUSES).nullish(),
});
export const updateTaskSchema = z.object({
id: z.string().min(1),
type: z.enum(["PLANNED", "COMPLETED"]).optional(),
description: z.string().max(2000).optional(),
timeEstimate: z.string().max(100).nullish(),
notes: z.string().max(2000).nullish(),
link: httpLink.nullish(),
status: z.enum(TASK_STATUSES).nullish(),
});
export const updateSettingSchema = z.object({
key: z.enum(["GOOGLE_DRIVE_FOLDER_ID"]),
value: z.string().trim().max(500),
});
export const updateUserRoleSchema = z.object({
userId: z.string().min(1),
role: z.enum(["EMPLOYEE", "ADMIN"]),
});
/**
* Parse and validate a request body. Returns the parsed data, or a
* NextResponse 400 that the route should return immediately.
*/
export async function parseBody<T extends z.ZodTypeAny>(
req: Request,
schema: T
): Promise<{ data: z.infer<T>; error: null } | { data: null; error: NextResponse }> {
let json: unknown;
try {
json = await req.json();
} catch {
return {
data: null,
error: NextResponse.json({ error: "Invalid JSON body" }, { status: 400 }),
};
}
const result = schema.safeParse(json);
if (!result.success) {
return {
data: null,
error: NextResponse.json(
{ error: "Validation failed", details: result.error.flatten().fieldErrors },
{ status: 400 }
),
};
}
return { data: result.data, error: null };
}