import { z } from 'zod' import { defineRouteContract } from '@/lib/api/contracts/types' export const chatAuthTypeSchema = z.enum(['public', 'password', 'email', 'sso']) export type ChatAuthType = z.output export const chatIdParamsSchema = z.object({ id: z.string().min(1), }) export const chatIdentifierParamsSchema = z.object({ identifier: z.string().min(1), }) export const chatOutputConfigSchema = z.object({ blockId: z.string().min(1), path: z.string().min(1), }) export const deployedChatOutputConfigSchema = z.object({ blockId: z.string(), path: z.string().optional(), }) export const chatCustomizationsSchema = z.object({ primaryColor: z.string(), welcomeMessage: z.string(), imageUrl: z.string().optional(), }) export const createChatBodySchema = z.object({ workflowId: z.string().min(1, 'Workflow ID is required'), identifier: z .string() .min(1, 'Identifier is required') .regex(/^[a-z0-9-]+$/, 'Identifier can only contain lowercase letters, numbers, and hyphens'), title: z.string().min(1, 'Title is required'), description: z.string().optional(), customizations: chatCustomizationsSchema, authType: chatAuthTypeSchema.default('public'), password: z.string().optional(), allowedEmails: z.array(z.string()).optional().default([]), outputConfigs: z.array(chatOutputConfigSchema).optional().default([]), }) export type CreateChatBody = z.input export const updateChatBodySchema = z.object({ workflowId: z.string().min(1, 'Workflow ID is required').optional(), identifier: z .string() .min(1, 'Identifier is required') .regex(/^[a-z0-9-]+$/, 'Identifier can only contain lowercase letters, numbers, and hyphens') .optional(), title: z.string().min(1, 'Title is required').optional(), description: z.string().optional(), customizations: chatCustomizationsSchema.optional(), authType: chatAuthTypeSchema.optional(), password: z.string().optional(), allowedEmails: z.array(z.string()).optional(), outputConfigs: z.array(chatOutputConfigSchema).optional(), }) export type UpdateChatBody = z.input export const createChatResponseSchema = z.object({ id: z.string(), chatId: z.string(), chatUrl: z.string(), message: z.string(), }) export type CreateChatResponse = z.output export const updateChatResponseSchema = z.object({ id: z.string(), chatUrl: z.string(), message: z.string(), }) export type UpdateChatResponse = z.output export const deleteChatResponseSchema = z.object({ message: z.string(), }) export const deployedChatConfigSchema = z.object({ id: z.string(), title: z.string(), description: z.preprocess((value) => value ?? '', z.string()), customizations: z.preprocess( (value) => value ?? {}, z .object({ primaryColor: z.string().optional(), logoUrl: z.string().optional(), imageUrl: z.string().optional(), welcomeMessage: z.string().optional(), headerText: z.string().optional(), }) .passthrough() ), authType: z.preprocess((value) => value ?? 'public', chatAuthTypeSchema), outputConfigs: z.preprocess( (value) => value ?? undefined, z.array(deployedChatOutputConfigSchema).optional() ), }) export type DeployedChatConfig = z.output export const deployedChatAuthBodySchema = z.object({ password: z.string().max(1024, 'Password is too long').optional(), email: z.string().email('Invalid email format').optional().or(z.literal('')), }) export type DeployedChatAuthBody = z.input const MAX_CHAT_INPUT_CHARS = 1_000_000 const MAX_CHAT_FILE_DATA_CHARS = 14 * 1024 * 1024 const MAX_CHAT_FILES = 15 export const deployedChatFileSchema = z.object({ name: z.string().min(1, 'File name is required').max(255, 'File name is too long'), type: z.string().min(1, 'File type is required').max(255, 'File type is too long'), size: z.number().positive('File size must be positive'), data: z .string() .min(1, 'File data is required') .max(MAX_CHAT_FILE_DATA_CHARS, 'File data exceeds the maximum allowed size'), lastModified: z.number().optional(), }) export const deployedChatPostBodySchema = z.object({ input: z.string().max(MAX_CHAT_INPUT_CHARS, 'Input is too long').optional(), password: z.string().max(1024, 'Password is too long').optional(), email: z.string().email('Invalid email format').optional().or(z.literal('')), conversationId: z.string().max(256, 'Conversation ID is too long').optional(), files: z .array(deployedChatFileSchema) .max(MAX_CHAT_FILES, `A maximum of ${MAX_CHAT_FILES} files is allowed`) .optional() .default([]), }) export type DeployedChatPostBody = z.input export const chatSSOBodySchema = z.object({ email: z.string().email('Invalid email address'), }) export const chatSSOResponseSchema = z.object({ eligible: z.boolean(), }) export type ChatSSOResponse = z.output export const chatEmailOtpRequestBodySchema = z.object({ email: z.string().email('Invalid email address'), }) export const chatEmailOtpVerifyBodySchema = chatEmailOtpRequestBodySchema.extend({ otp: z.string().length(6, 'OTP must be 6 digits'), }) export const chatEmailOtpRequestResponseSchema = z.object({ message: z.string(), }) export const identifierValidationQuerySchema = z.object({ identifier: z .string() .min(1, 'Identifier is required') .regex(/^[a-z0-9-]+$/, 'Identifier can only contain lowercase letters, numbers, and hyphens') .max(100, 'Identifier must be 100 characters or less'), }) export const identifierValidationResponseSchema = z.object({ available: z.boolean(), error: z.string().nullable().optional(), }) export const createChatContract = defineRouteContract({ method: 'POST', path: '/api/chat', body: createChatBodySchema, response: { mode: 'json', schema: createChatResponseSchema, }, }) export const getDeployedChatConfigContract = defineRouteContract({ method: 'GET', path: '/api/chat/[identifier]', params: chatIdentifierParamsSchema, response: { mode: 'json', schema: deployedChatConfigSchema, }, }) export const authenticateDeployedChatContract = defineRouteContract({ method: 'POST', path: '/api/chat/[identifier]', params: chatIdentifierParamsSchema, body: deployedChatAuthBodySchema, response: { mode: 'json', schema: deployedChatConfigSchema, }, }) export const deployedChatPostContract = defineRouteContract({ method: 'POST', path: '/api/chat/[identifier]', params: chatIdentifierParamsSchema, body: deployedChatPostBodySchema, response: { mode: 'json', schema: deployedChatConfigSchema, }, }) export const chatSSOContract = defineRouteContract({ method: 'POST', path: '/api/chat/[identifier]/sso', params: chatIdentifierParamsSchema, body: chatSSOBodySchema, response: { mode: 'json', schema: chatSSOResponseSchema, }, }) export const requestChatEmailOtpContract = defineRouteContract({ method: 'POST', path: '/api/chat/[identifier]/otp', params: chatIdentifierParamsSchema, body: chatEmailOtpRequestBodySchema, response: { mode: 'json', schema: chatEmailOtpRequestResponseSchema, }, }) export const verifyChatEmailOtpContract = defineRouteContract({ method: 'PUT', path: '/api/chat/[identifier]/otp', params: chatIdentifierParamsSchema, body: chatEmailOtpVerifyBodySchema, response: { mode: 'json', schema: deployedChatConfigSchema, }, }) export const validateChatIdentifierContract = defineRouteContract({ method: 'GET', path: '/api/chat/validate', query: identifierValidationQuerySchema, response: { mode: 'json', schema: identifierValidationResponseSchema, }, }) export const updateChatContract = defineRouteContract({ method: 'PATCH', path: '/api/chat/manage/[id]', params: chatIdParamsSchema, body: updateChatBodySchema, response: { mode: 'json', schema: updateChatResponseSchema, }, }) export const deleteChatContract = defineRouteContract({ method: 'DELETE', path: '/api/chat/manage/[id]', params: chatIdParamsSchema, response: { mode: 'json', schema: deleteChatResponseSchema, }, })