import type { NextRequest } from 'next/server'; import { getServerSideConfig } from '@/app/config/server'; import { ApiPath, GEMINI_BASE_URL, ModelProvider } from '@/app/constant'; import { prettyObject } from '@/app/utils/format'; import { NextResponse } from 'next/server'; import { auth } from './auth'; const serverConfig = getServerSideConfig(); export async function handle( req: NextRequest, { params }: { params: { provider: string; path: string[] } }, ) { console.log('[Google Route] params ', params); if (req.method === 'OPTIONS') { return NextResponse.json({ body: 'OK' }, { status: 200 }); } const authResult = auth(req, ModelProvider.GeminiPro); if (authResult.error) { return NextResponse.json(authResult, { status: 401, }); } const bearToken = req.headers.get('x-goog-api-key') || req.headers.get('Authorization') || ''; const token = bearToken.trim().replaceAll('Bearer ', '').trim(); const apiKey = token || serverConfig.googleApiKey; if (!apiKey) { return NextResponse.json( { error: true, message: `missing GOOGLE_API_KEY in server env vars`, }, { status: 401, }, ); } try { const response = await request(req, apiKey); return response; } catch (e) { console.error('[Google] ', e); return NextResponse.json(prettyObject(e)); } } export const GET = handle; export const POST = handle; export const runtime = 'edge'; export const preferredRegion = [ 'bom1', 'cle1', 'cpt1', 'gru1', 'hnd1', 'iad1', 'icn1', 'kix1', 'pdx1', 'sfo1', 'sin1', 'syd1', ]; async function request(req: NextRequest, apiKey: string) { const controller = new AbortController(); let baseUrl = serverConfig.googleUrl || GEMINI_BASE_URL; const path = `${req.nextUrl.pathname}`.replaceAll(ApiPath.Google, ''); if (!baseUrl.startsWith('http')) { baseUrl = `https://${baseUrl}`; } if (baseUrl.endsWith('/')) { baseUrl = baseUrl.slice(0, -1); } console.log('[Proxy] ', path); console.log('[Base Url]', baseUrl); const timeoutId = setTimeout( () => { controller.abort(); }, 10 * 60 * 1000, ); const fetchUrl = `${baseUrl}${path}${ req?.nextUrl?.searchParams?.get('alt') === 'sse' ? '?alt=sse' : '' }`; console.log('[Fetch Url] ', fetchUrl); const fetchOptions: RequestInit = { headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', 'x-goog-api-key': req.headers.get('x-goog-api-key') || (req.headers.get('Authorization') ?? '').replace('Bearer ', ''), }, method: req.method, body: req.body, // to fix #2485: https://stackoverflow.com/questions/55920957/cloudflare-worker-typeerror-one-time-use-body redirect: 'manual', // @ts-ignore duplex: 'half', signal: controller.signal, }; try { const res = await fetch(fetchUrl, fetchOptions); // to prevent browser prompt for credentials const newHeaders = new Headers(res.headers); newHeaders.delete('www-authenticate'); // to disable nginx buffering newHeaders.set('X-Accel-Buffering', 'no'); return new Response(res.body, { status: res.status, statusText: res.statusText, headers: newHeaders, }); } finally { clearTimeout(timeoutId); } }