LibreTranslate api to deeplx proxy

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