Barazo AppView backend barazo.forum
at main 146 lines 4.6 kB view raw
1import { z } from 'zod/v4' 2 3// --------------------------------------------------------------------------- 4// Shared enums 5// --------------------------------------------------------------------------- 6 7/** Valid maturity rating values for categories and communities. */ 8export const maturityRatingSchema = z.enum(['safe', 'mature', 'adult']) 9 10export type MaturityRating = z.infer<typeof maturityRatingSchema> 11 12// --------------------------------------------------------------------------- 13// Request schemas 14// --------------------------------------------------------------------------- 15 16/** Slug pattern: lowercase alphanumeric segments separated by single hyphens. */ 17const slugPattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/ 18 19/** Schema for creating a new category. */ 20export const createCategorySchema = z.object({ 21 name: z 22 .string() 23 .trim() 24 .min(1, 'Name is required') 25 .max(100, 'Name must be at most 100 characters'), 26 slug: z 27 .string() 28 .min(1, 'Slug is required') 29 .max(50, 'Slug must be at most 50 characters') 30 .regex( 31 slugPattern, 32 "Slug must be lowercase alphanumeric with single hyphens (e.g. 'general-discussion')" 33 ), 34 description: z.string().max(500, 'Description must be at most 500 characters').optional(), 35 parentId: z.string().optional(), 36 sortOrder: z 37 .number() 38 .int('Sort order must be an integer') 39 .min(0, 'Sort order must be non-negative') 40 .optional(), 41 maturityRating: maturityRatingSchema.optional(), 42}) 43 44export type CreateCategoryInput = z.infer<typeof createCategorySchema> 45 46/** Schema for updating an existing category (all fields optional). */ 47export const updateCategorySchema = z.object({ 48 name: z 49 .string() 50 .trim() 51 .min(1, 'Name must not be empty') 52 .max(100, 'Name must be at most 100 characters') 53 .optional(), 54 slug: z 55 .string() 56 .min(1, 'Slug must not be empty') 57 .max(50, 'Slug must be at most 50 characters') 58 .regex( 59 slugPattern, 60 "Slug must be lowercase alphanumeric with single hyphens (e.g. 'general-discussion')" 61 ) 62 .optional(), 63 description: z 64 .string() 65 .max(500, 'Description must be at most 500 characters') 66 .nullable() 67 .optional(), 68 parentId: z.string().nullable().optional(), 69 sortOrder: z 70 .number() 71 .int('Sort order must be an integer') 72 .min(0, 'Sort order must be non-negative') 73 .optional(), 74 maturityRating: maturityRatingSchema.optional(), 75}) 76 77export type UpdateCategoryInput = z.infer<typeof updateCategorySchema> 78 79/** Schema for updating community/category maturity rating. */ 80export const updateMaturitySchema = z.object({ 81 maturityRating: maturityRatingSchema, 82}) 83 84export type UpdateMaturityInput = z.infer<typeof updateMaturitySchema> 85 86// --------------------------------------------------------------------------- 87// Query schemas 88// --------------------------------------------------------------------------- 89 90/** Schema for listing categories with optional filtering. */ 91export const categoryQuerySchema = z.object({ 92 parentId: z.string().optional(), 93}) 94 95export type CategoryQueryInput = z.infer<typeof categoryQuerySchema> 96 97// --------------------------------------------------------------------------- 98// Response schemas (for OpenAPI documentation) 99// --------------------------------------------------------------------------- 100 101/** Schema describing a single category in API responses. */ 102export const categoryResponseSchema = z.object({ 103 id: z.string(), 104 slug: z.string(), 105 name: z.string(), 106 description: z.string().nullable(), 107 parentId: z.string().nullable(), 108 sortOrder: z.number(), 109 communityDid: z.string(), 110 maturityRating: maturityRatingSchema, 111 createdAt: z.string(), 112 updatedAt: z.string(), 113}) 114 115export type CategoryResponse = z.infer<typeof categoryResponseSchema> 116 117/** Schema describing a category with its children (tree structure). */ 118export const categoryTreeResponseSchema: z.ZodType<CategoryTreeResponse> = z.lazy(() => 119 z.object({ 120 id: z.string(), 121 slug: z.string(), 122 name: z.string(), 123 description: z.string().nullable(), 124 parentId: z.string().nullable(), 125 sortOrder: z.number(), 126 communityDid: z.string(), 127 maturityRating: maturityRatingSchema, 128 createdAt: z.string(), 129 updatedAt: z.string(), 130 children: z.array(categoryTreeResponseSchema), 131 }) 132) 133 134export interface CategoryTreeResponse { 135 id: string 136 slug: string 137 name: string 138 description: string | null 139 parentId: string | null 140 sortOrder: number 141 communityDid: string 142 maturityRating: 'safe' | 'mature' | 'adult' 143 createdAt: string 144 updatedAt: string 145 children: CategoryTreeResponse[] 146}