From c30b4b906bac2784332ae43e328a68a18b8e7d6f Mon Sep 17 00:00:00 2001 From: Jesse_Chen Date: Fri, 7 Aug 2026 23:23:00 +0800 Subject: [PATCH] fix(admin): remove model reauthentication --- .../app/api/admin/models/discover/route.ts | 4 +-- frontend/src/app/api/admin/models/route.ts | 6 +--- .../src/components/admin/model-management.tsx | 25 ++++---------- ...admin-model-management-ui-contract.test.ts | 13 +++++-- .../admin-ui-permission-contract.test.ts | 34 +++++++++---------- .../high-risk-billing-routes-contract.test.ts | 9 +++-- 6 files changed, 42 insertions(+), 49 deletions(-) diff --git a/frontend/src/app/api/admin/models/discover/route.ts b/frontend/src/app/api/admin/models/discover/route.ts index ff89146e..d0e8e1c4 100644 --- a/frontend/src/app/api/admin/models/discover/route.ts +++ b/frontend/src/app/api/admin/models/discover/route.ts @@ -1,6 +1,6 @@ import { z } from "zod"; import { queryAdminRows } from "@/lib/admin/database"; -import { adminErrorResponse, invalidQueryResponse, requireHighRiskAdminMutation } from "@/lib/admin/http"; +import { adminErrorResponse, invalidQueryResponse, requireAdminMutation } from "@/lib/admin/http"; import { requestAllowedModelProvider } from "@/lib/epay/gateway-policy"; import { decryptModelProviderApiKey, modelProviderModelsUrl, modelProviderRequestHeaders, type ModelProviderType } from "@/lib/model-provider-policy"; @@ -8,4 +8,4 @@ export const runtime="nodejs"; const schema=z.object({providerId:z.string().uuid()}).strict(); type Row={provider_type:ModelProviderType;base_url:string|null;encrypted_api_key:string|null;enabled:boolean}; function normalize(payload:unknown){const data=payload&&typeof payload==="object"&&"data" in payload?(payload as {data?:unknown}).data:null;if(!Array.isArray(data))return[];const seen=new Set();const out:{id:string;label:string}[]=[];for(const item of data){if(out.length>=200)break;if(!item||typeof item!=="object")continue;const raw="id" in item?(item as {id?:unknown}).id:undefined;if(typeof raw!=="string")continue;const id=raw.trim();if(!id||id.length>160||seen.has(id))continue;seen.add(id);const display="display_name" in item?(item as {display_name?:unknown}).display_name:undefined;out.push({id,label:typeof display==="string"&&display.trim()?display.trim().slice(0,160):id});}return out;} -export async function POST(request:Request){try{await requireHighRiskAdminMutation(request,"models.test");const parsed=schema.safeParse(await request.json().catch(()=>null));if(!parsed.success)return invalidQueryResponse(parsed.error.flatten());const rows=await queryAdminRows("select provider_type,base_url,encrypted_api_key,enabled from public.model_providers where id=$1",[parsed.data.providerId]);const p=rows[0];if(!p||!p.enabled)return Response.json({error:"模型供应商不可用"},{status:404});let key="";try{key=decryptModelProviderApiKey({encryptedApiKey:p.encrypted_api_key});}catch{return Response.json({error:"模型供应商配置不可用"},{status:409});}const upstream=await requestAllowedModelProvider(modelProviderModelsUrl({providerType:p.provider_type,baseUrl:p.base_url}),modelProviderRequestHeaders(p.provider_type,key),process.env,{timeoutMs:8000,maxResponseBytes:256*1024});if(upstream.status<200||upstream.status>299)return Response.json({error:"模型发现失败"},{status:502});let payload:unknown;try{payload=JSON.parse(upstream.body.toString("utf8"));}catch{return Response.json({error:"模型发现失败"},{status:502});}return Response.json({data:normalize(payload)});}catch(e){return adminErrorResponse(e);}} +export async function POST(request:Request){try{await requireAdminMutation(request,"models.test");const parsed=schema.safeParse(await request.json().catch(()=>null));if(!parsed.success)return invalidQueryResponse(parsed.error.flatten());const rows=await queryAdminRows("select provider_type,base_url,encrypted_api_key,enabled from public.model_providers where id=$1",[parsed.data.providerId]);const p=rows[0];if(!p||!p.enabled)return Response.json({error:"模型供应商不可用"},{status:404});let key="";try{key=decryptModelProviderApiKey({encryptedApiKey:p.encrypted_api_key});}catch{return Response.json({error:"模型供应商配置不可用"},{status:409});}const upstream=await requestAllowedModelProvider(modelProviderModelsUrl({providerType:p.provider_type,baseUrl:p.base_url}),modelProviderRequestHeaders(p.provider_type,key),process.env,{timeoutMs:8000,maxResponseBytes:256*1024});if(upstream.status<200||upstream.status>299)return Response.json({error:"模型发现失败"},{status:502});let payload:unknown;try{payload=JSON.parse(upstream.body.toString("utf8"));}catch{return Response.json({error:"模型发现失败"},{status:502});}return Response.json({data:normalize(payload)});}catch(e){return adminErrorResponse(e);}} diff --git a/frontend/src/app/api/admin/models/route.ts b/frontend/src/app/api/admin/models/route.ts index 90c0ce0d..c9a735d3 100644 --- a/frontend/src/app/api/admin/models/route.ts +++ b/frontend/src/app/api/admin/models/route.ts @@ -9,7 +9,6 @@ import { parseListQuery, requestId, requireAdminMutation, - requireHighRiskAdminMutation, } from "@/lib/admin/http"; import { assertAllowedModelProviderUrl, probeAllowedModelProvider } from "@/lib/epay/gateway-policy"; import { handleAdminModelMutation, type AdminModelMutation } from "@/lib/admin/model-mutation-handler"; @@ -177,10 +176,7 @@ export async function POST(request: Request) { : body.data.action === "test" ? "models.test" : "models.write"; - const highRisk = body.data.action === "saveProvider" || body.data.action === "publish" || body.data.action === "rollback"; - const session = highRisk - ? await requireHighRiskAdminMutation(request, permission) - : await requireAdminMutation(request, permission); + const session = await requireAdminMutation(request, permission); const rid = requestId(request); return await handleAdminModelMutation( body.data as AdminModelMutation, diff --git a/frontend/src/components/admin/model-management.tsx b/frontend/src/components/admin/model-management.tsx index f551600e..3b51113a 100644 --- a/frontend/src/components/admin/model-management.tsx +++ b/frontend/src/components/admin/model-management.tsx @@ -118,7 +118,6 @@ export default function ModelManagement() { const [editingModel, setEditingModel] = useState(null); const [saving, setSaving] = useState(false); const [discovering, setDiscovering] = useState(false); - const [discoverProviderId, setDiscoverProviderId] = useState(null); const [discoveredModels, setDiscoveredModels] = useState([]); const [actingId, setActingId] = useState(null); const [versionAction, setVersionAction] = useState(null); @@ -240,17 +239,18 @@ export default function ModelManagement() { } } - async function discoverModels() { - if (!discoverProviderId) return; + async function discoverModels(providerId?: string) { + if (!providerId) return; setDiscovering(true); try { const payload = await adminRequestJson("/api/admin/models/discover", { method: "POST", - body: JSON.stringify({ providerId: discoverProviderId }), + body: JSON.stringify({ providerId }), }); setDiscoveredModels(payload.data); if (!payload.data.length) message.info("未发现可用模型,可继续手工输入"); - setDiscoverProviderId(null); + } catch (error) { + message.error(error instanceof Error ? error.message : "获取模型列表失败"); } finally { setDiscovering(false); } @@ -426,7 +426,7 @@ export default function ModelManagement() { - setDiscoveredModels([])} options={providers.map((item) => ({ value: item.id, label: `${item.name} (${item.code})` }))} />