import { z } from "zod"; import { requireAdmin } from "@/lib/middleware/admin"; import { ChatSDKError } from "@/lib/errors"; import { db } from "@/lib/db"; import { aiModelHealthEvent } from "@/lib/db/schema"; import { and, desc, eq, sql } from "drizzle-orm"; import { restoreAiModelHealth, } from "@/lib/ai/runtime/model-health"; const restoreSchema = z.object({ modelId: z.string().uuid(), }); export async function GET(request: Request) { try { await requireAdmin(); const { searchParams } = new URL(request.url); const modelId = searchParams.get("modelId"); const providerId = searchParams.get("providerId"); const rawLimit = Number(searchParams.get("limit") || 20); const limit = Number.isFinite(rawLimit) ? Math.max(1, Math.min(200, Math.floor(rawLimit))) : 20; const rawPage = Number(searchParams.get("page") || 1); const page = Number.isFinite(rawPage) ? Math.max(1, Math.floor(rawPage)) : 1; const filters = []; if (modelId) filters.push(eq(aiModelHealthEvent.modelId, modelId)); if (providerId) filters.push(eq(aiModelHealthEvent.providerId, providerId)); if (searchParams.get("eventType")) filters.push(eq(aiModelHealthEvent.eventType, searchParams.get("eventType")!)); const start = searchParams.get("startDate"); const end = searchParams.get("endDate"); if (start && /^\d{4}-\d{2}-\d{2}$/.test(start)) filters.push(sql`${aiModelHealthEvent.createdAt}>=${start}::date`); if (end && /^\d{4}-\d{2}-\d{2}$/.test(end)) filters.push(sql`${aiModelHealthEvent.createdAt}<${end}::date+interval '1 day'`); const events = await db.select({ id: aiModelHealthEvent.id, modelId: aiModelHealthEvent.modelId, modelDisplayName: aiModelHealthEvent.modelDisplayName, modelRuntimeId: aiModelHealthEvent.modelRuntimeId, providerName: aiModelHealthEvent.providerName, providerId: aiModelHealthEvent.providerId, eventType: aiModelHealthEvent.eventType, errorMessage: aiModelHealthEvent.errorMessage, elapsedMs: aiModelHealthEvent.elapsedMs, consecutiveFailures: aiModelHealthEvent.consecutiveFailures, autoDisabled: aiModelHealthEvent.autoDisabled, counted: aiModelHealthEvent.counted, createdAt: aiModelHealthEvent.createdAt, }).from(aiModelHealthEvent).where(and(...filters)).orderBy(desc(aiModelHealthEvent.createdAt), desc(aiModelHealthEvent.id)) .limit(limit + 1).offset((page - 1) * limit); return Response.json({ events: events.slice(0, limit), hasMore: events.length > limit, page }); } catch (error) { if (error instanceof ChatSDKError) { return error.toResponse(); } return new ChatSDKError("offline:admin").toResponse(); } } export async function POST(request: Request) { try { await requireAdmin(); const body = await request.json(); const data = restoreSchema.parse(body); const models = await restoreAiModelHealth(data.modelId); if (models.length === 0) { return new ChatSDKError("bad_request:api", "Model not found").toResponse(); } return Response.json({ model: models[0] }); } catch (error) { if (error instanceof ChatSDKError) { return error.toResponse(); } if (error instanceof z.ZodError) { return new ChatSDKError("bad_request:api", error.message).toResponse(); } return new ChatSDKError("offline:admin").toResponse(); } }