swadhinbiswas Allow configuring local AI endpoint and key · 2/19/2026
37142e2
87 lines · 3.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import type { APIRoute } from "astro";
import { getDatabase, schema } from "@/db";
import { eq } from "drizzle-orm";
import type { NodePgDatabase } from "drizzle-orm/node-postgres";
import {
    buildStoredAIConfig,
    mergeAIConfig,
    parseAIConfigFromStorage,
    sanitizeAIConfigForClient,
} from "@/lib/ai-config";

export const POST: APIRoute = async ({ request, locals }) => {
    const user = locals.user;
    if (!user) {
        return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401 });
    }

    try {
        const data = await request.json();
        const { provider, apiKeys, model } = data;

        // Basic validation
        if (!["openai", "groq", "bytez", "openrouter", "together", "google", "external_agent", "local", "anthropic"].includes(provider)) {
            return new Response(JSON.stringify({ error: "Invalid provider" }), { status: 400 });
        }

        const db = getDatabase() as NodePgDatabase<typeof schema>;

        const currentUser = await db.query.users.findFirst({
            where: eq(schema.users.id, user.id),
            columns: { aiConfig: true },
        });

        const existing = parseAIConfigFromStorage(currentUser?.aiConfig);
        const merged = mergeAIConfig(existing, {
            provider,
            model: model || existing.model,
            apiKeys: {
                openai: typeof apiKeys?.openai === "string" && apiKeys.openai.trim() !== "" ? apiKeys.openai.trim() : undefined,
                anthropic: typeof apiKeys?.anthropic === "string" && apiKeys.anthropic.trim() !== "" ? apiKeys.anthropic.trim() : undefined,
                groq: typeof apiKeys?.groq === "string" && apiKeys.groq.trim() !== "" ? apiKeys.groq.trim() : undefined,
                bytez: typeof apiKeys?.bytez === "string" && apiKeys.bytez.trim() !== "" ? apiKeys.bytez.trim() : undefined,
                openrouter: typeof apiKeys?.openrouter === "string" && apiKeys.openrouter.trim() !== "" ? apiKeys.openrouter.trim() : undefined,
                together: typeof apiKeys?.together === "string" && apiKeys.together.trim() !== "" ? apiKeys.together.trim() : undefined,
                google: typeof apiKeys?.google === "string" && apiKeys.google.trim() !== "" ? apiKeys.google.trim() : undefined,
                local: typeof apiKeys?.local === "string" && apiKeys.local.trim() !== "" ? apiKeys.local.trim() : undefined,
                externalAgent: typeof apiKeys?.externalAgent === "string" && apiKeys.externalAgent.trim() !== "" ? apiKeys.externalAgent.trim() : undefined,
            },
            localBaseUrl:
                typeof data?.localBaseUrl === "string" && data.localBaseUrl.trim() !== ""
                    ? data.localBaseUrl.trim()
                    : undefined,
            externalAgentWebhookUrl:
                typeof data?.externalAgentWebhookUrl === "string" && data.externalAgentWebhookUrl.trim() !== ""
                    ? data.externalAgentWebhookUrl.trim()
                    : undefined,
        });

        await db.update(schema.users)
            .set({
                aiConfig: buildStoredAIConfig(merged),
                updatedAt: new Date()
            })
            .where(eq(schema.users.id, user.id));

        return new Response(JSON.stringify({ success: true }), { status: 200 });
    } catch (e: any) {
        return new Response(JSON.stringify({ error: e.message }), { status: 500 });
    }
};

export const GET: APIRoute = async ({ locals }) => {
    const user = locals.user;
    if (!user) {
        return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401 });
    }

    let config = null;
    try {
        if (user.aiConfig) {
            config = sanitizeAIConfigForClient(parseAIConfigFromStorage(user.aiConfig));
        }
    } catch (e) { }

    return new Response(JSON.stringify({ config }), { status: 200 });
}