/** * Shared Zod schemas for API route input validation. * Import the relevant schema in the route handler, call .safeParse(body), * and return 400 if !result.success. */ import { z } from 'zod' // ─── Shared enums (derived from lib/types.ts) ────────────────────────────── export const NOTE_COLOR_SCHEMA = z.enum([ 'default', 'red', 'orange', 'yellow', 'green', 'teal', 'blue', 'purple', 'pink', 'gray', ]) export const NOTE_TYPE_SCHEMA = z.enum(['text', 'markdown', 'richtext', 'checklist']) export const NOTE_SIZE_SCHEMA = z.enum(['small', 'medium', 'large']) export const LABEL_COLOR_SCHEMA = z.enum([ 'red', 'orange', 'yellow', 'green', 'teal', 'blue', 'purple', 'pink', 'gray', ]) // ─── Note schemas ─────────────────────────────────────────────────────────── const CheckItemSchema = z.object({ id: z.string().max(100), text: z.string().max(5_000), checked: z.boolean(), }) /** POST /api/notes */ export const CreateNoteSchema = z.object({ title: z.string().max(500).nullable().optional(), content: z.string().max(500_000).optional().default(''), color: NOTE_COLOR_SCHEMA.optional().default('default'), type: NOTE_TYPE_SCHEMA.optional().default('text'), size: NOTE_SIZE_SCHEMA.optional().default('small'), checkItems: z.array(CheckItemSchema).max(500).nullable().optional(), labels: z.array(z.string().max(200)).max(100).nullable().optional(), images: z.unknown().nullable().optional(), notebookId: z.string().max(100).nullable().optional(), }) /** PUT /api/notes */ export const UpdateNoteSchema = z.object({ id: z.string().min(1).max(100), title: z.string().max(500).nullable().optional(), content: z.string().max(500_000).optional(), color: NOTE_COLOR_SCHEMA.optional(), type: NOTE_TYPE_SCHEMA.optional(), size: NOTE_SIZE_SCHEMA.optional(), checkItems: z.array(CheckItemSchema).max(500).nullable().optional(), labels: z.array(z.string().max(200)).max(100).nullable().optional(), isPinned: z.boolean().optional(), isArchived: z.boolean().optional(), images: z.unknown().nullable().optional(), }) /** GET /api/notes query params */ export const GetNotesQuerySchema = z.object({ archived: z.enum(['true', 'false']).optional(), search: z.string().max(500).optional(), notebookId: z.string().max(100).optional(), limit: z.coerce.number().int().min(1).max(500).optional(), }) // ─── Notebook schemas ──────────────────────────────────────────────────────── /** POST /api/notebooks */ export const CreateNotebookSchema = z.object({ name: z.string().min(1, 'Name is required').max(200).trim(), icon: z.string().max(20).optional(), color: z.string().max(30).optional(), parentId: z.string().max(100).nullable().optional(), }) /** PATCH /api/notebooks/[id] */ export const PatchNotebookSchema = z.object({ name: z.string().min(1).max(200).trim().optional(), icon: z.string().max(20).optional(), color: z.string().max(30).optional(), order: z.number().int().min(0).optional(), trashedAt: z.string().datetime().nullable().optional(), parentId: z.string().max(100).nullable().optional(), }) // ─── Label schemas ─────────────────────────────────────────────────────────── /** POST /api/labels */ export const CreateLabelSchema = z.object({ name: z.string().min(1, 'Name is required').max(200).trim(), color: LABEL_COLOR_SCHEMA.optional(), notebookId: z.string().min(1).max(100), }) /** PUT /api/labels/[id] */ export const UpdateLabelSchema = z.object({ name: z.string().min(1).max(200).trim().optional(), color: LABEL_COLOR_SCHEMA.optional(), })