Spaces:
Runtime error
Runtime error
File size: 4,122 Bytes
cd8bd0a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | import { NextResponse } from "next/server";
import { getCombos, createCombo, getComboByName, isCloudEnabled } from "@/lib/localDb";
import { getConsistentMachineId } from "@/shared/utils/machineId";
import { syncToCloud } from "@/lib/cloudSync";
import { validateCompositeTiersConfig } from "@/lib/combos/compositeTiers";
import { normalizeComboModels } from "@/lib/combos/steps";
import { validateComboDAG, clampComboDepth } from "@omniroute/open-sse/services/combo.ts";
import { createComboSchema } from "@/shared/validation/schemas";
import { isValidationFailure, validateBody } from "@/shared/validation/helpers";
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
import { comboErrorResponse } from "@/lib/api/comboErrorResponse";
// GET /api/combos - Get all combos
export async function GET(request: Request) {
const authError = await requireManagementAuth(request);
if (authError) return authError;
try {
const combos = await getCombos();
return NextResponse.json({ combos });
} catch (error) {
console.log("Error fetching combos:", error);
return NextResponse.json({ error: "Failed to fetch combos" }, { status: 500 });
}
}
// POST /api/combos - Create new combo
export async function POST(request) {
const authError = await requireManagementAuth(request);
if (authError) return authError;
try {
const body = await request.json();
// Zod validation (covers name format, length, etc.)
const validation = validateBody(createComboSchema, body);
if (isValidationFailure(validation)) {
return NextResponse.json({ error: validation.error }, { status: 400 });
}
const allCombos = await getCombos();
const normalizedModels = normalizeComboModels(validation.data.models, {
comboName: validation.data.name,
// `allCombos` from `getCombos()` is typed as the DB-shaped record
// (JsonRecord & { version: 2; models: ComboStep[] }) which is
// structurally compatible with the local ComboCollectionLike in
// `normalizeComboModels` but TS does not infer the relationship.
allCombos: allCombos as never,
});
const comboInput = {
...validation.data,
models: normalizedModels,
};
const { name, strategy, config } = comboInput;
const compositeValidation = validateCompositeTiersConfig(comboInput);
if (compositeValidation.success === false) {
const failure = compositeValidation as {
success: false;
error: { message: string; details: unknown[] };
};
return comboErrorResponse(
"COMBO_003",
400,
{ reason: failure.error.message, details: failure.error.details },
request
);
}
// Check if name already exists
const existing = await getComboByName(name);
if (existing) {
return NextResponse.json({ error: "Combo name already exists" }, { status: 400 });
}
// Validate nested combo DAG (no circular references, max depth)
// Temporarily add the new combo to validate its graph
const tempCombo = {
...comboInput,
name,
strategy,
config,
};
try {
validateComboDAG(
name,
[...allCombos, tempCombo],
new Set(),
0,
clampComboDepth((config as { maxComboDepth?: unknown } | undefined)?.maxComboDepth)
);
} catch (dagError) {
return NextResponse.json({ error: dagError.message }, { status: 400 });
}
const combo = await createCombo(comboInput);
// Auto sync to Cloud if enabled
await syncToCloudIfEnabled();
return NextResponse.json(combo, { status: 201 });
} catch (error) {
console.log("Error creating combo:", error);
return NextResponse.json({ error: "Failed to create combo" }, { status: 500 });
}
}
/**
* Sync to Cloud if enabled
*/
async function syncToCloudIfEnabled() {
try {
const cloudEnabled = await isCloudEnabled();
if (!cloudEnabled) return;
const machineId = await getConsistentMachineId();
await syncToCloud(machineId);
} catch (error) {
console.log("Error syncing to cloud:", error);
}
}
|