mirror of
https://github.com/ItzCrazyKns/Perplexica.git
synced 2025-10-14 11:38:14 +00:00
validate the request body of api/chat to prevent malformed request body
This commit is contained in:
@@ -17,37 +17,11 @@ import {
|
|||||||
getCustomOpenaiModelName,
|
getCustomOpenaiModelName,
|
||||||
} from '@/lib/config';
|
} from '@/lib/config';
|
||||||
import { searchHandlers } from '@/lib/search';
|
import { searchHandlers } from '@/lib/search';
|
||||||
|
import { ChatApiBody as Body, Message, safeValidateBody } from './validation';
|
||||||
|
|
||||||
export const runtime = 'nodejs';
|
export const runtime = 'nodejs';
|
||||||
export const dynamic = 'force-dynamic';
|
export const dynamic = 'force-dynamic';
|
||||||
|
|
||||||
type Message = {
|
|
||||||
messageId: string;
|
|
||||||
chatId: string;
|
|
||||||
content: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type ChatModel = {
|
|
||||||
provider: string;
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type EmbeddingModel = {
|
|
||||||
provider: string;
|
|
||||||
name: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Body = {
|
|
||||||
message: Message;
|
|
||||||
optimizationMode: 'speed' | 'balanced' | 'quality';
|
|
||||||
focusMode: string;
|
|
||||||
history: Array<[string, string]>;
|
|
||||||
files: Array<string>;
|
|
||||||
chatModel: ChatModel;
|
|
||||||
embeddingModel: EmbeddingModel;
|
|
||||||
systemInstructions: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEmitterEvents = async (
|
const handleEmitterEvents = async (
|
||||||
stream: EventEmitter,
|
stream: EventEmitter,
|
||||||
writer: WritableStreamDefaultWriter,
|
writer: WritableStreamDefaultWriter,
|
||||||
@@ -187,7 +161,17 @@ const handleHistorySave = async (
|
|||||||
|
|
||||||
export const POST = async (req: Request) => {
|
export const POST = async (req: Request) => {
|
||||||
try {
|
try {
|
||||||
const body = (await req.json()) as Body;
|
const reqBody = (await req.json()) as Body;
|
||||||
|
|
||||||
|
const parseBody = safeValidateBody(reqBody);
|
||||||
|
if (!parseBody.success) {
|
||||||
|
return Response.json(
|
||||||
|
{ message: 'Invalid request body', error: parseBody.error },
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = parseBody.data as Body;
|
||||||
const { message } = body;
|
const { message } = body;
|
||||||
|
|
||||||
if (message.content === '') {
|
if (message.content === '') {
|
||||||
|
70
src/app/api/chat/validation.ts
Normal file
70
src/app/api/chat/validation.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
// Message schema
|
||||||
|
const messageSchema = z.object({
|
||||||
|
messageId: z.string().min(1, 'Message ID is required'),
|
||||||
|
chatId: z.string().min(1, 'Chat ID is required'),
|
||||||
|
content: z.string().min(1, 'Message content is required'),
|
||||||
|
});
|
||||||
|
|
||||||
|
// ChatModel schema
|
||||||
|
const chatModelSchema = z.object({
|
||||||
|
provider: z.string().optional(),
|
||||||
|
name: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// EmbeddingModel schema
|
||||||
|
const embeddingModelSchema = z.object({
|
||||||
|
provider: z.string().optional(),
|
||||||
|
name: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Main Body schema
|
||||||
|
export const bodySchema = z.object({
|
||||||
|
message: messageSchema,
|
||||||
|
optimizationMode: z.enum(['speed', 'balanced', 'quality'], {
|
||||||
|
errorMap: () => ({
|
||||||
|
message: 'Optimization mode must be one of: speed, balanced, quality',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
focusMode: z.string().min(1, 'Focus mode is required'),
|
||||||
|
history: z
|
||||||
|
.array(
|
||||||
|
z.tuple([z.string(), z.string()], {
|
||||||
|
errorMap: () => ({
|
||||||
|
message: 'History items must be tuples of two strings',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.optional()
|
||||||
|
.default([]),
|
||||||
|
files: z.array(z.string()).optional().default([]),
|
||||||
|
chatModel: chatModelSchema.optional().default({}),
|
||||||
|
embeddingModel: embeddingModelSchema.optional().default({}),
|
||||||
|
systemInstructions: z.string().optional().default(''),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Message = z.infer<typeof messageSchema>;
|
||||||
|
export type ChatModel = z.infer<typeof chatModelSchema>;
|
||||||
|
export type EmbeddingModel = z.infer<typeof embeddingModelSchema>;
|
||||||
|
export type ChatApiBody = z.infer<typeof bodySchema>;
|
||||||
|
|
||||||
|
// Safe validation that returns success/error
|
||||||
|
export function safeValidateBody(data: unknown) {
|
||||||
|
const result = bodySchema.safeParse(data);
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: result.error.errors.map((e) => ({
|
||||||
|
path: e.path.join('.'),
|
||||||
|
message: e.message,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: result.data,
|
||||||
|
};
|
||||||
|
}
|
Reference in New Issue
Block a user