-
Notifications
You must be signed in to change notification settings - Fork 514
Expand file tree
/
Copy pathroute.ts
More file actions
71 lines (57 loc) · 2.23 KB
/
route.ts
File metadata and controls
71 lines (57 loc) · 2.23 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
import { handleApiRequest } from "@/route-handlers/smart-route-handler";
import { getEnvVariable } from "@stackframe/stack-shared/dist/utils/env";
import { StatusError } from "@stackframe/stack-shared/dist/utils/errors";
import { NextRequest } from "next/server";
const OPENROUTER_BASE_URL = "https://openrouter.ai/api";
const OPENROUTER_MODEL = "anthropic/claude-sonnet-4.6";
function getApiKey(): string {
const apiKey = getEnvVariable("STACK_OPENROUTER_API_KEY", "");
if (!apiKey) {
throw new StatusError(503, "AI proxy not configured");
}
return apiKey;
}
function sanitizeBody(raw: ArrayBuffer): Uint8Array {
const text = new TextDecoder().decode(raw);
const parsed = JSON.parse(text);
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
throw new StatusError(400, "Request body must be a JSON object");
}
parsed.model = OPENROUTER_MODEL;
// OpenRouter limits metadata.user_id to 128 characters
if (parsed.metadata?.user_id && parsed.metadata.user_id.length > 128) {
parsed.metadata.user_id = parsed.metadata.user_id.slice(0, 128);
}
return new TextEncoder().encode(JSON.stringify(parsed));
}
async function proxyToOpenRouter(req: NextRequest, options: { params: Promise<{ path?: string[] }> }) {
const apiKey = getApiKey();
const params = await options.params;
const subpath = params.path?.join("/") ?? "";
const targetUrl = `${OPENROUTER_BASE_URL}/${subpath}${req.nextUrl.search}`;
const headers: Record<string, string> = {
"Authorization": `Bearer ${apiKey}`,
"anthropic-version": "2023-06-01",
};
const contentType = req.headers.get("Content-Type");
if (contentType) {
headers["Content-Type"] = contentType;
}
const body = req.method !== "GET" && req.method !== "HEAD"
? Buffer.from(sanitizeBody(await req.arrayBuffer()))
: undefined;
const response = await fetch(targetUrl, {
method: req.method,
headers,
body,
});
return new Response(response.body, {
status: response.status,
headers: {
"Content-Type": response.headers.get("Content-Type") ?? "application/json",
"Cache-Control": "no-cache",
},
});
}
export const GET = handleApiRequest(proxyToOpenRouter);
export const POST = handleApiRequest(proxyToOpenRouter);