mirror of
https://github.com/tiennm99/ccs.git
synced 2026-08-20 06:23:10 +00:00
fix(cliproxy): address code review feedback (attempt 4/5)
- Add default_tier and provider validation in POST/PUT routes - Extract shared validateCompositeTiers() helper for DRY - Validate providers against CLIPROXY_SUPPORTED_PROVIDERS
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
import { Router, Request, Response } from 'express';
|
import { Router, Request, Response } from 'express';
|
||||||
import { isReservedName, RESERVED_PROFILE_NAMES } from '../../config/reserved-names';
|
import { isReservedName, RESERVED_PROFILE_NAMES } from '../../config/reserved-names';
|
||||||
import type { CLIProxyProvider } from '../../cliproxy/types';
|
import type { CLIProxyProvider } from '../../cliproxy/types';
|
||||||
|
import { CLIPROXY_SUPPORTED_PROVIDERS } from '../../config/unified-config-types';
|
||||||
import {
|
import {
|
||||||
createVariant,
|
createVariant,
|
||||||
removeVariant,
|
removeVariant,
|
||||||
@@ -19,6 +20,33 @@ import {
|
|||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
|
const VALID_TIERS = ['opus', 'sonnet', 'haiku'] as const;
|
||||||
|
|
||||||
|
/** Validate composite tiers shape and provider/default_tier values. Returns error string or null. */
|
||||||
|
function validateCompositeTiers(
|
||||||
|
tiers: Record<string, { provider?: string; model?: string }>,
|
||||||
|
defaultTier?: string
|
||||||
|
): string | null {
|
||||||
|
// Validate default_tier
|
||||||
|
if (defaultTier && !VALID_TIERS.includes(defaultTier as (typeof VALID_TIERS)[number])) {
|
||||||
|
return `Invalid default_tier '${defaultTier}': must be one of ${VALID_TIERS.join(', ')}`;
|
||||||
|
}
|
||||||
|
// Validate each tier
|
||||||
|
for (const tier of VALID_TIERS) {
|
||||||
|
if (
|
||||||
|
!tiers[tier] ||
|
||||||
|
typeof tiers[tier].provider !== 'string' ||
|
||||||
|
typeof tiers[tier].model !== 'string'
|
||||||
|
) {
|
||||||
|
return `Invalid tier config for '${tier}': requires 'provider' and 'model' strings`;
|
||||||
|
}
|
||||||
|
if (!CLIPROXY_SUPPORTED_PROVIDERS.includes(tiers[tier].provider as CLIProxyProvider)) {
|
||||||
|
return `Invalid provider '${tiers[tier].provider}' for tier '${tier}': must be one of ${CLIPROXY_SUPPORTED_PROVIDERS.join(', ')}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /api/cliproxy - List cliproxy variants
|
* GET /api/cliproxy - List cliproxy variants
|
||||||
* Uses variant-service for consistent behavior with CLI
|
* Uses variant-service for consistent behavior with CLI
|
||||||
@@ -75,19 +103,11 @@ router.post('/', (req: Request, res: Response): void => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate tiers shape
|
// Validate tiers shape, providers, and default_tier
|
||||||
const requiredTiers = ['opus', 'sonnet', 'haiku'] as const;
|
const tierError = validateCompositeTiers(tiers, default_tier);
|
||||||
for (const tier of requiredTiers) {
|
if (tierError) {
|
||||||
if (
|
res.status(400).json({ error: tierError });
|
||||||
!tiers[tier] ||
|
return;
|
||||||
typeof tiers[tier].provider !== 'string' ||
|
|
||||||
typeof tiers[tier].model !== 'string'
|
|
||||||
) {
|
|
||||||
res.status(400).json({
|
|
||||||
error: `Invalid tier config for '${tier}': requires 'provider' and 'model' strings`,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = createCompositeVariant({ name, defaultTier: default_tier, tiers });
|
const result = createCompositeVariant({ name, defaultTier: default_tier, tiers });
|
||||||
@@ -162,21 +182,21 @@ router.put('/:name', (req: Request, res: Response): void => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate tiers shape if provided
|
// Validate tiers shape, providers, and default_tier if provided
|
||||||
if (tiers) {
|
if (tiers) {
|
||||||
const requiredTiers = ['opus', 'sonnet', 'haiku'] as const;
|
const tierError = validateCompositeTiers(tiers, default_tier);
|
||||||
for (const tier of requiredTiers) {
|
if (tierError) {
|
||||||
if (
|
res.status(400).json({ error: tierError });
|
||||||
!tiers[tier] ||
|
return;
|
||||||
typeof tiers[tier].provider !== 'string' ||
|
|
||||||
typeof tiers[tier].model !== 'string'
|
|
||||||
) {
|
|
||||||
res.status(400).json({
|
|
||||||
error: `Invalid tier config for '${tier}': requires 'provider' and 'model' strings`,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else if (
|
||||||
|
default_tier &&
|
||||||
|
!VALID_TIERS.includes(default_tier as (typeof VALID_TIERS)[number])
|
||||||
|
) {
|
||||||
|
res.status(400).json({
|
||||||
|
error: `Invalid default_tier '${default_tier}': must be one of ${VALID_TIERS.join(', ')}`,
|
||||||
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = updateCompositeVariant(name, { defaultTier: default_tier, tiers });
|
const result = updateCompositeVariant(name, { defaultTier: default_tier, tiers });
|
||||||
|
|||||||
Reference in New Issue
Block a user