import { NextResponse } from "next/server"; import { prisma } from "@/lib/db"; import { apiRouteErrorResponse } from "@/lib/auth/api"; import { requireSessionUser } from "@/lib/auth/session"; import { getOrCreateSettingsForUser } from "@/lib/user-settings"; import { getApiKeyStatus, getMaskedProviderApiKeys, mergeProviderApiKeys, parseProviderApiKeys, serializeProviderApiKeys, type ProviderApiKeys, } from "@/lib/ai/keys"; import { getAppBaseUrl } from "@/lib/threads-api"; import type { Setting } from "@prisma/client"; function toPublicSettings(settings: Setting, request?: Request) { const apiKeys = parseProviderApiKeys(settings.providerApiKeys); return { id: settings.id, persona: settings.persona, aiProvider: settings.aiProvider, aiModel: settings.aiModel, researchAiProvider: settings.researchAiProvider, researchAiModel: settings.researchAiModel, draftsPerScan: settings.draftsPerScan, matrixRows: settings.matrixRows, scanCron: settings.scanCron, appUrl: settings.appUrl, threadsOAuthRedirectUri: request ? `${getAppBaseUrl(request, settings.appUrl)}/api/threads/oauth/callback` : null, apiKeys: getMaskedProviderApiKeys(apiKeys), apiKeysConfigured: getApiKeyStatus(apiKeys), }; } export async function GET(request: Request) { try { const user = await requireSessionUser(); const settings = await getOrCreateSettingsForUser(user.id); return NextResponse.json(toPublicSettings(settings, request)); } catch (error) { return apiRouteErrorResponse(error, "settings"); } } export async function PATCH(request: Request) { try { const user = await requireSessionUser(); const body = await request.json(); const current = await getOrCreateSettingsForUser(user.id); const existingKeys = parseProviderApiKeys(current.providerApiKeys); let providerApiKeys = existingKeys; if (body.apiKeys && typeof body.apiKeys === "object") { providerApiKeys = mergeProviderApiKeys(existingKeys, body.apiKeys as ProviderApiKeys); } const settings = await prisma.setting.update({ where: { userId: user.id }, data: { ...(body.persona !== undefined && { persona: body.persona }), ...(body.aiProvider !== undefined && { aiProvider: body.aiProvider }), ...(body.aiModel !== undefined && { aiModel: body.aiModel }), ...(body.researchAiProvider !== undefined && { researchAiProvider: body.researchAiProvider }), ...(body.researchAiModel !== undefined && { researchAiModel: body.researchAiModel }), ...(body.draftsPerScan !== undefined && { draftsPerScan: body.draftsPerScan }), ...(body.matrixRows !== undefined && { matrixRows: body.matrixRows }), ...(body.scanCron !== undefined && { scanCron: body.scanCron }), ...(body.appUrl !== undefined && { appUrl: (() => { const trimmed = body.appUrl?.trim(); if (!trimmed) return null; try { const url = new URL(trimmed); if (!["http:", "https:"].includes(url.protocol)) return null; return `${url.protocol}//${url.host}`; } catch { return null; } })(), }), ...(body.apiKeys !== undefined && { providerApiKeys: serializeProviderApiKeys(providerApiKeys), }), }, }); return NextResponse.json(toPublicSettings(settings, request)); } catch (error) { return apiRouteErrorResponse(error, "settings/update"); } }