const http = require('http');
const https = require('https');
const querystring = require('querystring');
const DEEPL_API_KEY = process.env.DEEPL_API_KEY || '';
const DEEPL_API_HOST = process.env.DEEPL_API_HOST || 'api.deeplx.org';
const DEEPL_API_PATH = process.env.DEEPL_API_PATH || '/translate';
const DEBUG = process.env.DEBUG || false;
const PORT = process.env.PORT || 8080;
const server = http.createServer((req, res) => {
try {
if (req.method === 'POST' && req.url === '/translate') {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
try {
let data;
const contentType = req.headers['content-type'];
if (contentType === 'application/json') {
data = JSON.parse(body); // Assuming the input is JSON
} else if (contentType === 'application/x-www-form-urlencoded') {
data = querystring.parse(body);
} else {
res.writeHead(400, {'Content-Type': 'application/json'});
res.end(JSON.stringify({error: 'Invalid Content-Type. Only application/json and application/x-www-form-urlencoded are supported.'}));
return;
}
const libreParams = data;
// Map LibreTranslate parameters to DeepL parameters
const deeplParams = {
//auth_key: DEEPL_API_KEY,
text: libreParams.q,
target_lang: libreParams.target.toUpperCase(), // DeepL expects target language codes in uppercase
source_lang: libreParams.source ? libreParams.source.toUpperCase() : undefined,
// Additional parameters like preserving formatting for HTML can be included here if DeepL supports them
content_type: libreParams.format === 'html' ? 'text/html' : 'text/plain',
};
if (DEEPL_API_KEY) deeplParams.auth_key = DEEPL_API_KEY;
if (deeplParams.target_lang === 'ZH-CN') {
deeplParams.target_lang = 'ZH';
}
if (DEBUG) console.log(libreParams, deeplParams);
const deeplRequestBody = JSON.stringify(deeplParams);
const deeplRequestOptions = {
hostname: DEEPL_API_HOST,
port: 443,
path: DEEPL_API_PATH,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(deeplRequestBody),
},
};
const deeplRequest = https.request(deeplRequestOptions, deeplResponse => {
let deeplResponseData = '';
deeplResponse.on('data', chunk => {
deeplResponseData += chunk;
});
deeplResponse.on('end', () => {
try {
const deepLResponse = JSON.parse(deeplResponseData);
res.writeHead(200, {'Content-Type': 'application/json'});
if (DEBUG) console.log(deeplResponseData);
if (DEBUG) console.log(deepLResponse.data);
res.end(JSON.stringify({
translatedText: deepLResponse.data,
detectedLanguage: {language: deeplParams.source_lang},
}));
} catch (e) {
console.error('Error writing response');
}
});
});
deeplRequest.on('error', error => {
try {
console.error('Error communicating with DeepL API:', error);
res.writeHead(502); // Bad Gateway
res.end('Error communicating with upstream server');
} catch (e) {
console.error('Error writing error');
}
});
deeplRequest.write(deeplRequestBody);
deeplRequest.end();
} catch (e) {
console.error('Unexpected error:', error);
try {
res.writeHead(500);
res.end('Internal server error');
} catch (e) {
console.error('Error writing error');
}
}
});
} else {
res.writeHead(404);
res.end('Not Found');
}
} catch (e) {
console.error('Unexpected error:', error);
try {
res.writeHead(500);
res.end('Internal server error');
} catch (e) {
console.error('Error writing error');
}
}
});
// Global handler for uncaught exceptions
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
});
server.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`);
});
6 个赞
顶一下
增加了负载均衡
const http = require('http');
const https = require('https');
const querystring = require('querystring');
const DEEPL_API_KEY = process.env.DEEPL_API_KEY || '';
const DEEPL_API_HOST = process.env.DEEPL_API_HOST || 'api.deeplx.org';
const DEEPL_API_PATH = process.env.DEEPL_API_PATH || '/translate';
const DEBUG = process.env.DEBUG || false;
const PORT = process.env.PORT || 1573;
const TIMEOUT = process.env.TIMEOUT || 1000;
const DEEPL_USE_LB = process.env.DEEPL_USE_LB === 'false' ? false : true;
const LB_URLS = `
https://deeplx.papercar.top/translate
https://dlx.bitjss.com/translate
https://deeplx.ychinfo.com/translate
https://free-deepl.speedcow.top/translate
https://deeplx.keyrotate.com/translate
https://deepx.dumpit.top/translate
https://deepl.wuyongx.uk/translate
https://ghhosa.zzaning.com/translate
https://deeplx.he-sb.top/translate
https://deepl.aimoyu.tech/translate
https://deepl.tr1ck.cn/translate
https://translate.dftianyi.com/translate
https://deeplx.2077000.xyz:2087/translate
https://api.deeplx.org/translate
https://deeplx.vercel.app/translate
https://deeplxpro.vercel.app/translate
https://deeplx.llleman.com/translate`.trim().split('\n').map(e=>new URL(e));
const circuitBreakerData = LB_URLS.map(() => ({
lastBreakTime: 0,
breakCount: 0,
}))
function circuitBreaker(index) {
let current = circuitBreakerData[index];
if (current.breakCount <= 1) {
return false;
}
return Date.now() - current.lastBreakTime <= current.breakCount * 1000;
}
function circuitBreakerUpdate(index, isGood) {
let current = circuitBreakerData[index];
if (isGood) {
current.breakCount = 0;
} else {
++current.breakCount;
current.lastBreakTime = Date.now();
}
}
let index = Math.round(Math.random() * LB_URLS.length) % LB_URLS.length;
function makeRequest(deeplRequestOptions, deeplRequestBody) {
return new Promise((resolve, reject) => {
const deeplRequest = https.request(deeplRequestOptions, deeplResponse => {
let deeplResponseData = [];
deeplResponse.on('data', chunk => {
deeplResponseData.push(chunk);
});
deeplResponse.on('end', () => {
let bufferData;
try {
bufferData = Buffer.concat(deeplResponseData).toString();
const deepLResponse = JSON.parse(bufferData);
if (deepLResponse && deepLResponse.code > 399) {
reject(deepLResponse);
} else {
resolve(deepLResponse);
}
} catch (e) {
console.error('Error parsing response', e, bufferData);
reject(e);
}
});
});
deeplRequest.setTimeout(TIMEOUT);
deeplRequest.on('error', error => {
reject(error);
});
deeplRequest.write(deeplRequestBody);
deeplRequest.end();
})
}
async function makeRequestLb(deeplRequestBody) {
const beginIndex = index;
const len = LB_URLS.length;
let currentIndex = (beginIndex + 1) % len;
index = currentIndex;
do {
if (circuitBreaker(currentIndex)) {
console.log('circuitBreaker', currentIndex);
currentIndex = (currentIndex + 1) % len;
index = currentIndex;
continue;
}
try {
const url = LB_URLS[currentIndex];
const deeplRequestOptions = {
hostname: url.hostname,
port: url.port,
path: url.pathname,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(deeplRequestBody),
},
};
let resp = await makeRequest(deeplRequestOptions, deeplRequestBody);
circuitBreakerUpdate(currentIndex, true);
return resp;
} catch (e) {
if (beginIndex === currentIndex) {
throw e;
}
console.log('on error resume next', currentIndex, e);
currentIndex = (currentIndex + 1) % len;
index = currentIndex;
circuitBreakerUpdate(currentIndex, false);
}
} while (true);
}
const server = http.createServer((req, res) => {
try {
if (req.method === 'POST' && req.url === '/translate') {
let body = [];
req.on('data', chunk => {
body.push(chunk);
});
req.on('end', () => {
try {
let data;
const contentType = req.headers['content-type'];
if (contentType === 'application/json') {
data = JSON.parse(Buffer.concat(body).toString()); // Assuming the input is JSON
} else if (contentType === 'application/x-www-form-urlencoded') {
data = querystring.parse(Buffer.concat(body).toString());
} else {
res.writeHead(400, {'Content-Type': 'application/json'});
res.end(JSON.stringify({error: 'Invalid Content-Type. Only application/json and application/x-www-form-urlencoded are supported.'}));
return;
}
const libreParams = data;
// Map LibreTranslate parameters to DeepL parameters
const deeplParams = {
//auth_key: DEEPL_API_KEY,
text: libreParams.q,
target_lang: libreParams.target.toUpperCase(), // DeepL expects target language codes in uppercase
source_lang: libreParams.source ? libreParams.source.toUpperCase() : undefined,
// Additional parameters like preserving formatting for HTML can be included here if DeepL supports them
content_type: libreParams.format === 'html' ? 'text/html' : 'text/plain',
};
if (DEEPL_API_KEY) deeplParams.auth_key = DEEPL_API_KEY;
if (deeplParams.target_lang === 'ZH-CN') {
deeplParams.target_lang = 'ZH';
}
if (DEBUG) console.log(libreParams, deeplParams);
const deeplRequestBody = JSON.stringify(deeplParams);
makeRequestLb(deeplRequestBody).then(resp => {
try {
const deepLResponse = resp;
res.writeHead(200, {'Content-Type': 'application/json'});
if (DEBUG) console.log(deepLResponse);
res.end(JSON.stringify({
translatedText: deepLResponse.data,
detectedLanguage: {language: deeplParams.source_lang},
}));
} catch (e) {
console.error('Error writing response', e);
}
}, error => {
try {
console.error('Error communicating with DeepL API:', error);
res.writeHead(502); // Bad Gateway
res.end('Error communicating with upstream server');
} catch (e) {
console.error('Error writing error', e);
}
})
} catch (e) {
console.error('Unexpected error:', error);
try {
res.writeHead(500);
res.end('Internal server error');
} catch (e) {
console.error('Error writing error', e);
}
}
});
} else {
res.writeHead(404);
res.end('Not Found');
}
} catch (e) {
console.error('Unexpected error:', error);
try {
res.writeHead(500);
res.end('Internal server error');
} catch (e) {
console.error('Error writing error', e);
}
}
});
// Global handler for uncaught exceptions
process.on('uncaughtException', (error) => {
console.error('Uncaught Exception:', error);
});
server.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`);
});
1 个赞
mark
1234567