Barazo AppView backend barazo.forum
at main 115 lines 3.7 kB view raw
1import { z } from 'zod/v4' 2 3// --------------------------------------------------------------------------- 4// Self-label schemas (com.atproto.label.defs#selfLabels) 5// --------------------------------------------------------------------------- 6 7const selfLabelSchema = z.object({ 8 val: z.string().max(128), 9}) 10 11const selfLabelsSchema = z.object({ 12 values: z.array(selfLabelSchema).max(10), 13}) 14 15// --------------------------------------------------------------------------- 16// Request schemas 17// --------------------------------------------------------------------------- 18 19/** Schema for creating a new topic. */ 20export const createTopicSchema = z.object({ 21 title: z 22 .string() 23 .trim() 24 .min(1, 'Title is required') 25 .max(200, 'Title must be at most 200 characters'), 26 content: z 27 .string() 28 .min(1, 'Content is required') 29 .max(100000, 'Content must be at most 100,000 characters'), 30 category: z.string().trim().min(1, 'Category is required'), 31 tags: z 32 .array(z.string().trim().min(1).max(30, 'Tag must be at most 30 characters')) 33 .max(5, 'At most 5 tags allowed') 34 .optional(), 35 labels: selfLabelsSchema.optional(), 36}) 37 38export type CreateTopicInput = z.infer<typeof createTopicSchema> 39 40/** Schema for updating an existing topic (all fields optional). */ 41export const updateTopicSchema = z.object({ 42 title: z 43 .string() 44 .trim() 45 .min(1, 'Title must not be empty') 46 .max(200, 'Title must be at most 200 characters') 47 .optional(), 48 content: z 49 .string() 50 .min(1, 'Content must not be empty') 51 .max(100000, 'Content must be at most 100,000 characters') 52 .optional(), 53 category: z.string().trim().min(1, 'Category must not be empty').optional(), 54 tags: z 55 .array(z.string().trim().min(1).max(30, 'Tag must be at most 30 characters')) 56 .max(5, 'At most 5 tags allowed') 57 .optional(), 58 labels: selfLabelsSchema.optional(), 59}) 60 61export type UpdateTopicInput = z.infer<typeof updateTopicSchema> 62 63// --------------------------------------------------------------------------- 64// Query schemas 65// --------------------------------------------------------------------------- 66 67/** Schema for listing topics with pagination and optional filtering. */ 68export const topicQuerySchema = z.object({ 69 cursor: z.string().optional(), 70 limit: z 71 .string() 72 .transform((val) => Number(val)) 73 .pipe(z.number().int().min(1).max(100)) 74 .optional() 75 .default(25), 76 category: z.string().optional(), 77 tag: z.string().optional(), 78 sort: z.enum(['latest', 'popular']).optional().default('latest'), 79}) 80 81export type TopicQueryInput = z.infer<typeof topicQuerySchema> 82 83// --------------------------------------------------------------------------- 84// Response schemas (for OpenAPI documentation) 85// --------------------------------------------------------------------------- 86 87/** Schema describing a single topic in API responses. */ 88export const topicResponseSchema = z.object({ 89 uri: z.string(), 90 rkey: z.string(), 91 authorDid: z.string(), 92 title: z.string(), 93 content: z.string(), 94 category: z.string(), 95 site: z.string().nullable(), 96 tags: z.array(z.string()).nullable(), 97 labels: z.object({ values: z.array(z.object({ val: z.string() })) }).nullable(), 98 communityDid: z.string(), 99 cid: z.string(), 100 replyCount: z.number(), 101 reactionCount: z.number(), 102 lastActivityAt: z.string(), 103 publishedAt: z.string(), 104 indexedAt: z.string(), 105}) 106 107export type TopicResponse = z.infer<typeof topicResponseSchema> 108 109/** Schema for a paginated topic list response. */ 110export const topicListResponseSchema = z.object({ 111 topics: z.array(topicResponseSchema), 112 cursor: z.string().nullable(), 113}) 114 115export type TopicListResponse = z.infer<typeof topicListResponseSchema>