Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: server.js #2

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 26 additions & 54 deletions server.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,33 @@
const http = require('http');
const https = require('https');
const { URL } = require('url');
const config = require('./config.json');

const apiKeys = config.apiKeys;

const { apiKeys } = config;
let currentApiKeyIndex = 0;
let attemptCount = 0;

function getNextApiKey() {
const numKeys = apiKeys.length;
currentApiKeyIndex = (currentApiKeyIndex + 1) % numKeys;
currentApiKeyIndex = (currentApiKeyIndex + 1) % apiKeys.length;
return apiKeys[currentApiKeyIndex];
}

function forwardToOpenAI(apiKeyInfo, url, data, res) {
const { key, url: apiUrl } = apiKeyInfo;
const openaiUrl = apiUrl + url;

const parsedUrl = new URL(openaiUrl);

const transport = parsedUrl.protocol === 'https:' ? https : http;
const openaiUrl = new URL(url, apiUrl);
const transport = openaiUrl.protocol === 'https:' ? https : http;

const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${encodeURIComponent(key)}`,
'Authorization': `Bearer ${key}`,
},
};

const req = transport.request(parsedUrl, options, (proxyRes) => {
const req = transport.request(openaiUrl, options, (proxyRes) => {
console.log('Received response from the reverse proxy. Status:', proxyRes.statusCode);

if (proxyRes.statusCode === 429 || proxyRes.statusCode === 418 || proxyRes.statusCode === 502 || proxyRes.statusCode === 400) {
if ([400, 418, 429, 502].includes(proxyRes.statusCode)) {
handleReverseProxyError(res, url, data);
} else {
res.writeHead(proxyRes.statusCode, proxyRes.headers);
Expand All @@ -45,84 +40,61 @@ function forwardToOpenAI(apiKeyInfo, url, data, res) {
handleReverseProxyError(res, url, data);
});

getNextApiKey();
req.write(data);
req.end();
}


function checkModel(apiKey, model, url, data, res) {
if (apiKey === undefined) {
res.statusCode = 500;
res.end(JSON.stringify({ error: 'No API key found' }));
function checkModel(apiKey, model, url, data, res, attemptCount = 0) {
if (attemptCount >= apiKeys.length) {
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'No API key available for this model' }));
return;
}

const apiKeyInfo = apiKeys.find((info) => info.key === apiKey);

if (!apiKeyInfo) {
res.statusCode = 500;
res.writeHead(500, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid API key' }));
} else if (apiKeyInfo.models.includes(model)) {
attemptCount = 0;
console.log(`Forwarding to ${apiKeyInfo.url} with API key: ${apiKey}`);
forwardToOpenAI(apiKeyInfo, url, data, res);
} else {
console.log('Model not supported by this API key');
modelNotSupported(apiKey, model, url, data, res);
const newApiKeyInfo = getNextApiKey();
checkModel(newApiKeyInfo.key, model, url, data, res, attemptCount + 1);
}
}

function handleReverseProxyError(res, url, data) {
console.log('Error from the reverse proxy. Changing API key and retrying.');
const newApiKeyInfo = getNextApiKey();
forwardToOpenAI(newApiKeyInfo, url, data, res);
console.log('Forwarding to', newApiKeyInfo.url, 'with API key:', newApiKeyInfo.key);
}

async function modelNotSupported(apiKey, model, url, data, res) {
if (attemptCount >= apiKeys.length) {
// All API keys have been tried and none support the model
res.statusCode = 500;
res.end(JSON.stringify({ error: 'No API key available for this model' }));
return;
}

attemptCount++; // Increment the count of attempts
const newApiKeyInfo = getNextApiKey();
checkModel(newApiKeyInfo.key, model, url, data, res);
forwardToOpenAI(newApiKeyInfo, url, data, res);
}

http.createServer((req, res) => {
res.setHeader('Content-Type', 'application/json');
res.setHeader('Access-Control-Allow-Origin', '*');
console.log('Received POST request:', req.url);

if (req.method !== 'POST') {
res.statusCode = 405; // Method Not Allowed
res.end();
res.writeHead(405, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Method Not Allowed' }));
return;
}

console.log('Received POST request:', req.url);

let data = '';
req.on('data', (chunk) => {
data += chunk;
});

req.on('data', (chunk) => { data += chunk; });
req.on('end', () => {
try {
const payload = JSON.parse(data);
const model = payload.model;

const { model } = JSON.parse(data);
const apiKeyInfo = apiKeys[currentApiKeyIndex];
const apiKey = apiKeyInfo.key;
checkModel(apiKey, model, req.url, data, res);
checkModel(apiKeyInfo.key, model, req.url, data, res);
} catch (error) {
console.error('Error processing request:', error);
res.statusCode = 400; // Bad Request
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid JSON payload' }));
}
});
}).listen(3456, 'localhost', () => {
console.log('Server running at http://localhost:3000/');
console.log('Server running at http://localhost:3456/');
});