import { and, eq, gt } from "drizzle-orm"; import * as z from "zod"; import { providers, sessions } from "~~/drizzle/schema"; import { lockAccount } from "~~/server/lib/account-security"; import { db } from "~~/server/lib/db"; export default defineEventHandler(async (event) => { const { user, session } = await protectRoute(event); const result = await readValidatedBody(event, (body) => z.object({ config: z.object({ apiKey: z.string().optional(), apiProxyUrl: z.string().optional(), }).optional(), expectedUpdatedAt: z.iso.datetime().optional(), enabled: z.boolean().optional(), }).refine(body => body.config === undefined || body.expectedUpdatedAt !== undefined, { message: "Provider config updates require a revision", }).safeParse(body)); if (!result.success) { throw createError({ statusCode: 400, message: result.error.issues[0]?.message }); } const providerId = getRouterParam(event, "providerId"); if (!providerId) { throw createError({ statusCode: 400, statusMessage: "Invalid provider" }); } const { config, enabled, expectedUpdatedAt } = result.data; return await db.transaction(async (tx) => { await lockAccount(tx, user.id); // A request may have waited behind a password rotation or recovery. const [activeSession] = await tx.select({ id: sessions.id }).from(sessions).where(and( eq(sessions.id, session.id), eq(sessions.userId, user.id), gt(sessions.expiresAt, new Date()), )); if (!activeSession) { throw createError({ statusCode: 401, statusMessage: "Unauthorized" }); } const [existing] = await tx.select().from(providers).where(and( eq(providers.id, providerId), eq(providers.userId, user.id), )); if (!existing) { throw createError({ statusCode: 404, statusMessage: "Invalid provider" }); } if (config !== undefined && existing.updatedAt.getTime() !== new Date(expectedUpdatedAt ?? "").getTime()) { throw createError({ statusCode: 409, statusMessage: "Provider config out of date", data: { code: "PROVIDER_KEYS_OUT_OF_DATE" }, }); } const updatedAt = new Date(Math.max(Date.now(), existing.updatedAt.getTime() + 1)); await tx.update(providers).set({ config: config === undefined ? undefined : { ...existing.config, ...config }, enabled, updatedAt, }).where(and(eq(providers.id, providerId), eq(providers.userId, user.id))); return { ok: true, updatedAt }; }); });