mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
- Added CORS_HEADERS spread to 400/503 responses in chat/completions route - Added createAutoComboSchema with Zod validation to /api/combos/auto - Isolated JSON parsing errors with structured 400 response - Prevented String(err) leakage on 500 errors
This commit is contained in:
@@ -10,6 +10,8 @@
|
||||
*/
|
||||
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { createAutoComboSchema } from "@/shared/validation/schemas";
|
||||
import { isValidationFailure, validateBody } from "@/shared/validation/helpers";
|
||||
|
||||
// ── In-memory auto-combo store (mirrors open-sse/services/autoCombo/engine.ts) ──
|
||||
|
||||
@@ -45,29 +47,45 @@ interface AutoComboConfig {
|
||||
const autoCombos = new Map<string, AutoComboConfig>();
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
let rawBody: unknown;
|
||||
try {
|
||||
const body = await req.json();
|
||||
const { id, name, candidatePool, weights, modePack, budgetCap, explorationRate } = body;
|
||||
rawBody = await req.json();
|
||||
} catch {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: {
|
||||
message: "Invalid request",
|
||||
details: [{ field: "body", message: "Invalid JSON body" }],
|
||||
},
|
||||
},
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
if (!id || !name) {
|
||||
return NextResponse.json({ error: "id and name required" }, { status: 400 });
|
||||
try {
|
||||
const validation = validateBody(createAutoComboSchema, rawBody);
|
||||
if (isValidationFailure(validation)) {
|
||||
return NextResponse.json({ error: validation.error }, { status: 400 });
|
||||
}
|
||||
const { id, name, candidatePool, weights, modePack, budgetCap, explorationRate } =
|
||||
validation.data;
|
||||
|
||||
const config: AutoComboConfig = {
|
||||
id,
|
||||
name,
|
||||
type: "auto",
|
||||
candidatePool: candidatePool || [],
|
||||
weights: weights || DEFAULT_WEIGHTS,
|
||||
candidatePool,
|
||||
weights: weights ?? DEFAULT_WEIGHTS,
|
||||
modePack,
|
||||
budgetCap,
|
||||
explorationRate: explorationRate ?? 0.05,
|
||||
explorationRate,
|
||||
};
|
||||
autoCombos.set(id, config);
|
||||
|
||||
return NextResponse.json(config, { status: 201 });
|
||||
} catch (err) {
|
||||
return NextResponse.json({ error: String(err) }, { status: 500 });
|
||||
console.log("Error creating auto-combo:", err);
|
||||
return NextResponse.json({ error: "Failed to create auto-combo" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CORS_ORIGIN } from "@/shared/utils/cors";
|
||||
import { CORS_ORIGIN, CORS_HEADERS } from "@/shared/utils/cors";
|
||||
import { callCloudWithMachineId } from "@/shared/utils/cloud";
|
||||
import { handleChat } from "@/sse/handlers/chat";
|
||||
import { initTranslators } from "@omniroute/open-sse/translator/index.ts";
|
||||
@@ -53,7 +53,7 @@ export async function POST(request) {
|
||||
detections: result.detections.length,
|
||||
},
|
||||
}),
|
||||
{ status: 400, headers: { "Content-Type": "application/json" } }
|
||||
{ status: 400, headers: { ...CORS_HEADERS, "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ export async function POST(request) {
|
||||
code: "SECURITY_002",
|
||||
},
|
||||
}),
|
||||
{ status: 503, headers: { "Content-Type": "application/json" } }
|
||||
{ status: 503, headers: { ...CORS_HEADERS, "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,29 @@ export const createComboSchema = z.object({
|
||||
config: comboConfigSchema,
|
||||
});
|
||||
|
||||
// ──── Auto-Combo Schemas ────
|
||||
|
||||
const scoringWeightsSchema = z
|
||||
.object({
|
||||
quota: z.number().min(0).max(1),
|
||||
health: z.number().min(0).max(1),
|
||||
costInv: z.number().min(0).max(1),
|
||||
latencyInv: z.number().min(0).max(1),
|
||||
taskFit: z.number().min(0).max(1),
|
||||
stability: z.number().min(0).max(1),
|
||||
})
|
||||
.optional();
|
||||
|
||||
export const createAutoComboSchema = z.object({
|
||||
id: z.string().trim().min(1, "id is required").max(100),
|
||||
name: z.string().trim().min(1, "name is required").max(200),
|
||||
candidatePool: z.array(z.string().min(1)).optional().default([]),
|
||||
weights: scoringWeightsSchema,
|
||||
modePack: z.string().max(100).optional(),
|
||||
budgetCap: z.number().positive().optional(),
|
||||
explorationRate: z.number().min(0).max(1).optional().default(0.05),
|
||||
});
|
||||
|
||||
// ──── Settings Schemas ────
|
||||
// FASE-01: Removed .passthrough() — only explicitly listed fields are accepted
|
||||
|
||||
|
||||
Reference in New Issue
Block a user