Files
Prisma/prisma/vite.config.ts
2026-01-08 20:47:59 +08:00

158 lines
4.8 KiB
TypeScript

import path from 'path';
import { defineConfig, loadEnv } from 'vite';
import react from '@vitejs/plugin-react';
import type { Connect } from 'vite';
// Custom middleware to handle dynamic proxy for /custom-api
function customApiProxyMiddleware(): Connect.NextHandleFunction {
return async (req, res, next) => {
if (!req.url?.startsWith('/custom-api')) {
return next();
}
const targetUrl = req.headers['x-target-url'] as string;
if (!targetUrl) {
res.statusCode = 400;
res.end(JSON.stringify({ error: 'Missing X-Target-URL header' }));
return;
}
try {
const url = new URL(targetUrl);
const targetPath = req.url.replace(/^\/custom-api/, '');
const fullUrl = `${url.origin}${targetPath}`;
console.log('[Custom Proxy] Forwarding:', req.method, req.url, '->', fullUrl);
// Collect request body
const chunks: Buffer[] = [];
for await (const chunk of req) {
chunks.push(chunk as Buffer);
}
const body = Buffer.concat(chunks);
// Forward headers (excluding hop-by-hop headers)
const forwardHeaders: Record<string, string> = {};
const skipHeaders = ['host', 'connection', 'x-target-url', 'transfer-encoding'];
for (const [key, value] of Object.entries(req.headers)) {
if (!skipHeaders.includes(key.toLowerCase()) && value) {
forwardHeaders[key] = Array.isArray(value) ? value[0] : value;
}
}
forwardHeaders['host'] = url.host;
// Make the request
const response = await fetch(fullUrl, {
method: req.method,
headers: forwardHeaders,
body: ['GET', 'HEAD'].includes(req.method || '') ? undefined : body,
});
// Forward response status and headers
res.statusCode = response.status;
response.headers.forEach((value, key) => {
if (!['transfer-encoding', 'connection'].includes(key.toLowerCase())) {
res.setHeader(key, value);
}
});
// Stream the response body
if (response.body) {
const reader = response.body.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
res.write(value);
}
} finally {
reader.releaseLock();
}
}
res.end();
} catch (error: any) {
console.error('[Custom Proxy] Error:', error.message);
res.statusCode = 502;
res.end(JSON.stringify({ error: 'Proxy error', message: error.message }));
}
};
}
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, '.', '');
return {
server: {
port: 3000,
host: '0.0.0.0',
proxy: {
// Proxy for OpenAI API
'/openai/v1': {
target: 'https://api.openai.com',
changeOrigin: true,
secure: true,
rewrite: (path) => path.replace(/^\/openai\/v1/, '/v1'),
},
// Proxy for DeepSeek API
'/deepseek/v1': {
target: 'https://api.deepseek.com',
changeOrigin: true,
secure: true,
rewrite: (path) => path.replace(/^\/deepseek\/v1/, '/v1'),
},
// Proxy for Anthropic API
'/anthropic/v1': {
target: 'https://api.anthropic.com',
changeOrigin: true,
secure: true,
rewrite: (path) => path.replace(/^\/anthropic\/v1/, '/v1'),
},
// Proxy for xAI API
'/xai/v1': {
target: 'https://api.x.ai',
changeOrigin: true,
secure: true,
rewrite: (path) => path.replace(/^\/xai\/v1/, '/v1'),
},
// Proxy for Mistral API
'/mistral/v1': {
target: 'https://api.mistral.ai',
changeOrigin: true,
secure: true,
rewrite: (path) => path.replace(/^\/mistral\/v1/, '/v1'),
},
// Proxy for Google Gemini API
'/v1beta/models': {
target: 'https://generativelanguage.googleapis.com',
changeOrigin: true,
secure: true,
},
'/v1/models': {
target: 'https://generativelanguage.googleapis.com',
changeOrigin: true,
secure: true,
},
}
},
plugins: [
react(),
{
name: 'custom-api-proxy',
configureServer(server) {
server.middlewares.use(customApiProxyMiddleware());
},
},
],
define: {
'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY),
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY)
},
resolve: {
alias: {
'@': path.resolve(__dirname, '.'),
}
}
};
});