refactor: update provider API — presets endpoint, simplified activate, official route

This commit is contained in:
程序员阿江(Relakkes) 2026-04-07 11:24:22 +08:00
parent 747b45b14f
commit e584df1d28

View File

@ -1,30 +1,29 @@
/**
* Providers REST API
*
* GET /api/providers provider
* GET /api/providers/:id provider
* POST /api/providers provider
* PUT /api/providers/:id provider
* DELETE /api/providers/:id provider
* POST /api/providers/:id/activate provider
* POST /api/providers/:id/test provider
* POST /api/providers/test
* GET /api/providers list all saved providers + activeId
* GET /api/providers/presets list available presets
* POST /api/providers add a provider
* PUT /api/providers/:id update a provider
* DELETE /api/providers/:id delete a provider
* POST /api/providers/:id/activate activate a saved provider
* POST /api/providers/official activate official (clear env)
* POST /api/providers/:id/test test a saved provider
* POST /api/providers/test test unsaved config
*/
import { z } from 'zod'
import { ProviderService } from '../services/providerService.js'
import { PROVIDER_PRESETS } from '../config/providerPresets.js'
import {
CreateProviderSchema,
UpdateProviderSchema,
TestProviderSchema,
ActivateProviderSchema,
} from '../types/provider.js'
import { ApiError, errorResponse } from '../middleware/errorHandler.js'
const providerService = new ProviderService()
// ─── Sanitization ─────────────────────────────────────────────────────────────
function maskApiKey(key: string): string {
if (key.length <= 8) return '****'
return key.slice(0, 4) + '****' + key.slice(-4)
@ -37,27 +36,36 @@ function sanitizeProvider(provider: Record<string, unknown>): Record<string, unk
return provider
}
// ─── Router ───────────────────────────────────────────────────────────────────
export async function handleProvidersApi(
req: Request,
url: URL,
_url: URL,
segments: string[],
): Promise<Response> {
try {
const id = segments[2] // provider ID or 'test'
const action = segments[3] // 'activate' | 'test' | undefined
const id = segments[2]
const action = segments[3]
// ── POST /api/providers/test (test unsaved configuration) ──────────
// POST /api/providers/test
if (id === 'test' && req.method === 'POST') {
return await handleTestUnsaved(req)
}
// ── /api/providers (no ID) ────────────────────────────────────────
// GET /api/providers/presets
if (id === 'presets' && req.method === 'GET') {
return Response.json({ presets: PROVIDER_PRESETS })
}
// POST /api/providers/official
if (id === 'official' && req.method === 'POST') {
await providerService.activateOfficial()
return Response.json({ ok: true })
}
// /api/providers (no ID)
if (!id) {
if (req.method === 'GET') {
const providers = await providerService.listProviders()
return Response.json({ providers: providers.map(sanitizeProvider) })
const { providers, activeId } = await providerService.listProviders()
return Response.json({ providers: providers.map(sanitizeProvider), activeId })
}
if (req.method === 'POST') {
return await handleCreate(req)
@ -65,20 +73,21 @@ export async function handleProvidersApi(
throw methodNotAllowed(req.method)
}
// ── /api/providers/:id/activate ───────────────────────────────────
// /api/providers/:id/activate
if (action === 'activate') {
if (req.method !== 'POST') throw methodNotAllowed(req.method)
return await handleActivate(req, id)
await providerService.activateProvider(id)
return Response.json({ ok: true })
}
// ── /api/providers/:id/test ───────────────────────────────────────
// /api/providers/:id/test
if (action === 'test') {
if (req.method !== 'POST') throw methodNotAllowed(req.method)
const result = await providerService.testProvider(id)
return Response.json({ result })
}
// ── /api/providers/:id (no action) ────────────────────────────────
// /api/providers/:id
if (req.method === 'GET') {
const provider = await providerService.getProvider(id)
return Response.json({ provider: sanitizeProvider(provider) })
@ -97,8 +106,6 @@ export async function handleProvidersApi(
}
}
// ─── Handlers ─────────────────────────────────────────────────────────────────
async function handleCreate(req: Request): Promise<Response> {
const body = await parseJsonBody(req)
try {
@ -106,9 +113,7 @@ async function handleCreate(req: Request): Promise<Response> {
const provider = await providerService.addProvider(input)
return Response.json({ provider }, { status: 201 })
} catch (err) {
if (err instanceof z.ZodError) {
throw ApiError.badRequest(err.issues.map((i) => i.message).join('; '))
}
if (err instanceof z.ZodError) throw ApiError.badRequest(err.issues.map((i) => i.message).join('; '))
throw err
}
}
@ -120,23 +125,7 @@ async function handleUpdate(req: Request, id: string): Promise<Response> {
const provider = await providerService.updateProvider(id, input)
return Response.json({ provider })
} catch (err) {
if (err instanceof z.ZodError) {
throw ApiError.badRequest(err.issues.map((i) => i.message).join('; '))
}
throw err
}
}
async function handleActivate(req: Request, id: string): Promise<Response> {
const body = await parseJsonBody(req)
try {
const input = ActivateProviderSchema.parse(body)
await providerService.activateProvider(id, input.modelId)
return Response.json({ ok: true })
} catch (err) {
if (err instanceof z.ZodError) {
throw ApiError.badRequest(err.issues.map((i) => i.message).join('; '))
}
if (err instanceof z.ZodError) throw ApiError.badRequest(err.issues.map((i) => i.message).join('; '))
throw err
}
}
@ -148,15 +137,11 @@ async function handleTestUnsaved(req: Request): Promise<Response> {
const result = await providerService.testProviderConfig(input)
return Response.json({ result })
} catch (err) {
if (err instanceof z.ZodError) {
throw ApiError.badRequest(err.issues.map((i) => i.message).join('; '))
}
if (err instanceof z.ZodError) throw ApiError.badRequest(err.issues.map((i) => i.message).join('; '))
throw err
}
}
// ─── Helpers ──────────────────────────────────────────────────────────────────
async function parseJsonBody(req: Request): Promise<Record<string, unknown>> {
try {
return (await req.json()) as Record<string, unknown>