From 6070ac06e168656024717a9ddd79046570149c69 Mon Sep 17 00:00:00 2001 From: guobao2333 Date: Sun, 15 Sep 2024 17:17:40 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E5=8F=98=E5=8C=96=EF=BC=9A=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=B7=A5=E4=BD=9C=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-image.yaml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index 14dcb60..ff40f34 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -21,7 +21,7 @@ env: # Use docker.io for Docker Hub if empty REGISTRY: ghcr.io # github.repository as / - IMAGE_NAME: ${{ github.repository }} + IMAGE_NAME: deeplxs jobs: @@ -64,16 +64,10 @@ jobs: type=ref,event=branch type=ref,event=pr type=sha + type=raw,value=${{ github.ref }},enable=${{ github.event.ref_type == 'tag' && github.ref =~ refs/tags/v }} + type=raw,value=${{ github.event_time }},enable=${{ github.event.ref_type != 'tag' }} type=raw,value=latest,enable=${{ github.event.ref_type == 'tag' }} - - name: Add tag based on condition - run: | - if [[ ${{ github.ref }} =~ refs/tags/v ]]; then - echo "::set-output name=tag::${{ github.event.ref }}" - else - echo "::set-output name=tag::${{ github.event_time }}" - fi - # Build and push Docker image with Buildx (don't push on PR) # https://github.com/docker/build-push-action - name: Build and push Docker image From b8d9b60b0dfd3427d2c5bae9300cdd111e7c397a Mon Sep 17 00:00:00 2001 From: guobao2333 Date: Sun, 15 Sep 2024 17:33:37 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E5=8F=98=E5=8C=96=EF=BC=9A=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0eacd16..d1efc96 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ 你需要: - 一双灵活的小手🙌🏻 - 一个聪明的小脑袋瓜🧠 -- 支持 `Nodejs ≥16.13` 或 `Docker` 或 `Serverless Function` 的服务器💻 +- 支持 `Nodejs ≥18` 或 `Docker` 或 `Serverless Function` 的服务器💻 - (可选) 拥有[Vercel](https://vercel.com)或[Netlify](https://netlify.com)的账号 ## Deploy | 部署 @@ -34,11 +34,6 @@ 因维护者我有多个仓库需要维护,短时间内将无法对多平台部署方案进行兼容,您可以打开一个`issues📌`或打开一个`Pull Request📎`贡献您的代码。 ✨项目当前没有任何需要填写的变量值,但在后续可能会添加。 -> [!IMPORTANT] -> 如果您需要跨域访问,请在`server.js`中配置`CORS` -> Vercel这类平台支持更便捷的方式配置,详请查阅其平台文档。 - - ### Docker | 容器部署 🐳直接运行预构建容器: @@ -48,8 +43,8 @@ docker run -d -p 9000:9000 ghcr.io/guobao2333/deeplx-serverless 🚧你还可以自己构建后运行: ```bash -docker build -t DeepLXS . -docker run -d -p 9000:9000 DeepLXS +docker build -t deeplxs . +docker run -d -p 9000:9000 deeplxs ``` @@ -73,6 +68,24 @@ git clone https://github.com/guobao2333/DeepLX-Serverless && cd DeepLX-Serverles 如果部署完成了,就可以开始使用啦!🎉 +#### startup parameters | 启动参数 +> [!IMPORTANT] +> 此功能在 v1.2.0 及以上版本中可用 + +如果你想的话,可以在启动时添加参数: +```bash +npm run start -- -p 8888 +``` + +你可以添加一个`--no`前缀来禁止备选翻译: +```bash +npm run start -- --no-a +``` + +使用`-h`来获取所有参数: +```bash +npm run start -- -h +``` ## How To Use | 如何使用 > [!IMPORTANT] @@ -121,7 +134,7 @@ curl --location --request POST 'http://localhost:9000/translate' --header 'Conte 如果你是第一次贡献,并且真的想贡献点什么,那么请查看[《如何为开源做贡献》](https://opensource.guide/how-to-contribute/),不过我喜欢叫它「开源贡献指南」,那里有你需要知道的一切。 ## License | 开源许可 -本项目遵循[GNU/AGPLv3 许可证](./LICENSE)的条款发布。 +本项目遵循[GNU/AGPLv3](./LICENSE)许可证的条款发布。 DeepL free translate API for Serverless Copyright (C) 2024 shiguobaona @@ -142,5 +155,5 @@ curl --location --request POST 'http://localhost:9000/translate' --header 'Conte ## Acknowledgments | 鸣谢 1. [OwO-Network/DeepLX](https://github.com/OwO-Network/DeepLX) - 感谢OwO-Network的优秀项目(•̀ᴗ•́)و̑̑ -2. [LegendLeo/deeplx-serverless](https://github.com/LegendLeo/deeplx-serverless) - 本项目的重构前项目,接过维护的大旗(ง •̀_•́)ง +2. [LegendLeo/deeplx-serverless](https://github.com/LegendLeo/deeplx-serverless) - 原项目 3. [bropines/Deeplx-vercel](https://github.com/bropines/Deeplx-vercel) - 参考部分实现 From 2d39393eeec43f42af44ce2a99f9fc841b904f0f Mon Sep 17 00:00:00 2001 From: guobao2333 Date: Sun, 15 Sep 2024 17:36:57 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E5=8F=98=E5=8C=96=EF=BC=9A=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E7=89=88=E6=9C=AC=E5=8F=B7=E5=92=8C=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 8628698..afaca62 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,18 @@ { "name": "deeplx-serverless", - "version": "1.1.2", + "version": "2.0.0", "description": "DeepL free API for Serverless", "type": "module", "main": "server.js", "engines": { - "node": ">=16.13" + "node": ">=18" }, "scripts": { "start": "node server.js", "test": "node test.js" }, "keywords": [ + "deeplxs", "deeplx", "deepl", "translate", @@ -25,6 +26,7 @@ "express": "^4.18.2", "lodash": "^4.17.21", "node-fetch": "^3.3.2", - "random-int": "^3.0.0" + "random-int": "^3.0.0", + "yargs": "^17.7.2" } } From 44c4fde4c02fd21a4a8c390c62406a82b0895eb6 Mon Sep 17 00:00:00 2001 From: guobao2333 Date: Sun, 15 Sep 2024 17:39:26 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E5=8F=98=E5=8C=96=EF=BC=9A=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=8F=82=E6=95=B0=E6=8E=A7=E5=88=B6=E5=92=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server.js | 84 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 16 deletions(-) diff --git a/server.js b/server.js index c136884..b72f2dd 100644 --- a/server.js +++ b/server.js @@ -1,15 +1,41 @@ import express from 'express'; import cors from 'cors'; import bodyParser from 'body-parser'; +import yargs from 'yargs/yargs'; +import { hideBin } from 'yargs/helpers'; import { translate } from './translate.js'; +// 解析参数 +const argv = yargs(hideBin(process.argv)) + .option('port', { + alias: 'p', + describe: '端口号', + coerce: coerce_port, + default: 9000 + }) + .option('alt', { + alias: 'a', + describe: '请求备选翻译', + type: 'boolean', + default: true + }) + .option('cors', { + alias: 'c', + describe: '允许跨域访问的源(origin)', + coerce: coerce_cors, + default: false + }) + .help().alias('help', 'h') + .argv; + +// 定义配置 const app = express(), - PORT = 9000, - allowAlternative = true, + PORT = argv.port, + allowAlternative = argv.alt, CORS = { - origin: false, // 默认关闭跨域访问,还支持指定多个域名或正则表达式 + origin: argv.cors, methods: 'GET,POST', - allowedHeaders: 'Content-Type', + allowedHeaders: ['Content-Type', 'Authorization'], preflightContinue: false }; @@ -24,24 +50,33 @@ app.get('/', async (req, res) => await get(req, res)); async function post(req, res) { const startTime = Date.now(); // 记录开始时间 - // 检查请求方法和请求体 - if (req.method !== 'POST' || !req.body || !req.body.text) { + // 检查请求体 + if (!req.body || Object.keys(req.body).length === 0) { const duration = Date.now() - startTime; - console.log(`[LOG] ${new Date().toISOString()} | 404 | ${duration}ms | POST "translate"`); - return res.status(404).json({ - "code": 404, - "message": "Path not found" + console.log(`[LOG] ${new Date().toISOString()} | POST "translate" | 400 | Request body does not exist | ${duration}ms`); + return res.status(400).json({ + "code": 400, + "message": "Bad Request" }); } - // 是否允许备选翻译 - if (!allowAlternative) alt_count = 0; const { text, source_lang, target_lang, alt_count } = req.body; + // 是否允许备选翻译 + if (!allowAlternative && alt_count !== undefined) { + const duration = Date.now() - startTime; + console.log(`[LOG] ${new Date().toISOString()} | POST "translate" | 405 | Alt Not Allowed | ${duration}ms`); + return res.status(405).json({ + "code": 405, + "message": "Alternative Translate Not Allowed" + }); + alt_count = 0; + } + try { const result = await translate(text, source_lang, target_lang, alt_count); const duration = Date.now() - startTime; // 计算处理时间 - console.log(`[LOG] ${new Date().toISOString()} | 200 | ${duration}ms | POST "translate"`); + console.log(`[LOG] ${new Date().toISOString()} | POST "translate" | 200 | ${duration}ms`); const responseData = { code: 200, @@ -54,9 +89,10 @@ async function post(req, res) { }; res.json(responseData); + } catch (error) { const duration = Date.now() - startTime; - console.error(`[ERROR] ${new Date().toISOString()} | 500 | ${duration}ms | POST "translate" | ${error.message}`); + console.error(`[ERR] ${new Date().toISOString()} | POST "translate" | 500 | ${error.message} | ${duration}ms`); res.status(500).json({ code: 500, message: 'Translation failed', @@ -72,9 +108,25 @@ async function get(req, res) { }); }; +function coerce_cors(arg) { + if (typeof arg === 'string' || typeof arg === 'boolean') { + return arg; + } + console.error("ParamTypeError: \x1b[33m'"+arg+"'\x1b[31m, origin should be Boolean or String.\n\x1b[0meg: \x1b[32m'*' or true or RegExp"); + process.exit(1); +} + +function coerce_port(arg) { + if (typeof arg === 'number' && !isNaN(arg) && Number.isInteger(arg) && arg >= 0 && arg <= 65535) { + return arg; + } + console.warn("WARNING:\x1b[0m port should be >= 0 and < 65536.\nUsed default value instead: 9000\n"); + return 9000; +} + // 启动本地服务器 app.listen(PORT, () => { - console.log(`Server is running on http://localhost:${PORT}`); + console.log(`Server is running and listening on http://localhost:${PORT}`); }); -export { post, get }; \ No newline at end of file +export { post, get }; From e01e0ede01aa478fd249c5bdbeb6f6b56f3267aa Mon Sep 17 00:00:00 2001 From: guobao2333 Date: Sun, 15 Sep 2024 17:48:26 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9A=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=B5=81=E7=A8=8B=E8=AF=AD=E6=B3=95=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index ff40f34..eb41fc8 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -64,7 +64,7 @@ jobs: type=ref,event=branch type=ref,event=pr type=sha - type=raw,value=${{ github.ref }},enable=${{ github.event.ref_type == 'tag' && github.ref =~ refs/tags/v }} + type=raw,value=${{ github.ref }},enable=${{ github.event.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/v') }} type=raw,value=${{ github.event_time }},enable=${{ github.event.ref_type != 'tag' }} type=raw,value=latest,enable=${{ github.event.ref_type == 'tag' }} From 1925a41a96887033558faa8de1d6e21f26ca7c80 Mon Sep 17 00:00:00 2001 From: guobao2333 Date: Sun, 15 Sep 2024 17:53:42 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9A=E4=BF=AE?= =?UTF-8?q?=E4=BA=86=E4=B8=80=E4=B8=AA=E6=84=9A=E8=A0=A2=E7=9A=84=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-image.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-image.yaml b/.github/workflows/docker-image.yaml index eb41fc8..a02ae05 100644 --- a/.github/workflows/docker-image.yaml +++ b/.github/workflows/docker-image.yaml @@ -21,7 +21,7 @@ env: # Use docker.io for Docker Hub if empty REGISTRY: ghcr.io # github.repository as / - IMAGE_NAME: deeplxs + IMAGE_NAME: ${{ github.repository }} jobs: From f7fd45ad0ddce2361694bce5a23a20c64e4af1f1 Mon Sep 17 00:00:00 2001 From: guobao2333 Date: Sun, 15 Sep 2024 18:08:26 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=EF=BC=9A=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=92=8C=E4=BF=AE=E5=A4=8D=E6=96=87=E6=A1=A3=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d1efc96..fe3d0ca 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # DeepLX Serverless [本项目](https://github.com/guobao2333/DeepLX-Serverless)是一个基于 DeepL翻译API 的无服务器函数部署项目,旨在提供一个可直接对接的免费翻译解决方案,很适合需要在无服务器环境中集成DeepL翻译服务的开发者使用。 -与原项目[DeepLX](https://github.com/OwO-Network/DeepLX)的区别在于**利用了无服务器函数(边缘函数)请求IP不固定的特性**,有效避免了`Error 429`请求太频繁的报错(不过嘛凡事总有例外¯\\_(ツ)_/¯) +与原项目[DeepLX](https://github.com/OwO-Network/DeepLX)的区别在于**利用了无服务器函数(边缘函数)请求IP不固定的特性**,有效避免了`Error 429`请求太频繁的报错(不过嘛凡事总有例外¯\\\_(ツ)_/¯) 虽然因此不会立马被*暂时封禁*,但也请不要滥用!目前我部署在vercel上会有流量限制,如果您有大量内容需要翻译,请购买DeepL的付费版,当前项目使用的free接口会受到DeepL政策限制 由于目前项目未对pro接口进行适配,所以现在还只能使用free接口,不过在将来会完善,敬请期待~ („• ֊ •„)੭ @@ -70,22 +70,15 @@ git clone https://github.com/guobao2333/DeepLX-Serverless && cd DeepLX-Serverles #### startup parameters | 启动参数 > [!IMPORTANT] -> 此功能在 v1.2.0 及以上版本中可用 +> 此功能在 v2.0.0 及以上版本中可用 -如果你想的话,可以在启动时添加参数: -```bash -npm run start -- -p 8888 -``` - -你可以添加一个`--no`前缀来禁止备选翻译: +如果你想的话,可以在启动时添加参数。 +添加一个`--no`前缀来禁止备选翻译: ```bash npm run start -- --no-a ``` -使用`-h`来获取所有参数: -```bash -npm run start -- -h -``` +💡使用`-h`来获取所有参数。 ## How To Use | 如何使用 > [!IMPORTANT]