新增自定义模型
This commit is contained in:
@@ -1,15 +1,149 @@
|
||||
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()],
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user