From 36bf69adbb5dde87eb7bdebe22b1bce69527cdbf Mon Sep 17 00:00:00 2001 From: sunday <306419886@qq.com> Date: Wed, 16 Jul 2025 02:52:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20Redis=20TLS=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 .env.example 中新增 REDIS_ENABLE_TLS 配置项 - 更新 config.example.js 以支持 TLS 连接 - 修改 RedisClient 类以根据配置启用或禁用 TLS --- .env.example | 1 + config/config.example.js | 3 +- package-lock.json | 406 +-------------------------------------- src/models/redis.js | 89 ++++----- 4 files changed, 49 insertions(+), 450 deletions(-) diff --git a/.env.example b/.env.example index adeade9d..4b3c3226 100644 --- a/.env.example +++ b/.env.example @@ -16,6 +16,7 @@ REDIS_HOST=localhost REDIS_PORT=6379 REDIS_PASSWORD= REDIS_DB=0 +REDIS_ENABLE_TLS= # 🎯 Claude API 配置 CLAUDE_API_URL=https://api.anthropic.com/v1/messages diff --git a/config/config.example.js b/config/config.example.js index 166f643a..b9725332 100644 --- a/config/config.example.js +++ b/config/config.example.js @@ -28,7 +28,8 @@ const config = { commandTimeout: 5000, retryDelayOnFailover: 100, maxRetriesPerRequest: 3, - lazyConnect: true + lazyConnect: true, + enableTLS: process.env.REDIS_ENABLE_TLS || false, }, // 🎯 Claude API配置 diff --git a/package-lock.json b/package-lock.json index 73cb01e9..9e9894a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,21 +15,15 @@ "commander": "^11.1.0", "compression": "^1.7.4", "cors": "^2.8.5", - "crypto": "^1.0.1", "dotenv": "^16.3.1", "express": "^4.18.2", - "express-validator": "^7.0.1", "helmet": "^7.1.0", "https-proxy-agent": "^7.0.2", "inquirer": "^9.2.15", "ioredis": "^5.3.2", - "jsonwebtoken": "^9.0.2", "morgan": "^1.10.0", - "multer": "^1.4.5-lts.1", - "node-cron": "^3.0.3", "ora": "^5.4.1", "rate-limiter-flexible": "^5.0.5", - "redis": "^4.6.10", "socks-proxy-agent": "^8.0.2", "table": "^6.8.1", "uuid": "^9.0.1", @@ -1309,71 +1303,6 @@ "@noble/hashes": "^1.1.5" } }, - "node_modules/@redis/bloom": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/@redis/bloom/-/bloom-1.2.0.tgz", - "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/client": { - "version": "1.6.1", - "resolved": "https://registry.npmmirror.com/@redis/client/-/client-1.6.1.tgz", - "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", - "license": "MIT", - "dependencies": { - "cluster-key-slot": "1.1.2", - "generic-pool": "3.9.0", - "yallist": "4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@redis/client/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, - "node_modules/@redis/graph": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/@redis/graph/-/graph-1.1.1.tgz", - "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/json": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/@redis/json/-/json-1.0.7.tgz", - "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/search": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/@redis/search/-/search-1.2.0.tgz", - "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/time-series": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/@redis/time-series/-/time-series-1.1.0.tgz", - "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmmirror.com/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -1666,12 +1595,6 @@ "node": ">= 8" } }, - "node_modules/append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", - "license": "MIT" - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", @@ -2044,29 +1967,13 @@ "ieee754": "^1.1.13" } }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" - }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, "license": "MIT" }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmmirror.com/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz", @@ -2472,21 +2379,6 @@ "dev": true, "license": "MIT" }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmmirror.com/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "engines": [ - "node >= 0.8" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz", @@ -2537,12 +2429,6 @@ "dev": true, "license": "MIT" }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmmirror.com/cors/-/cors-2.8.5.tgz", @@ -2593,13 +2479,6 @@ "node": ">= 8" } }, - "node_modules/crypto": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/crypto/-/crypto-1.0.1.tgz", - "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", - "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.", - "license": "ISC" - }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", @@ -2760,15 +2639,6 @@ "node": ">= 0.4" } }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmmirror.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz", @@ -3195,19 +3065,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/express-validator": { - "version": "7.2.1", - "resolved": "https://registry.npmmirror.com/express-validator/-/express-validator-7.2.1.tgz", - "integrity": "sha512-CjNE6aakfpuwGaHQZ3m8ltCG2Qvivd7RHtVMS/6nVxOM7xVGqr4bhflsm4+N5FP5zI7Zxp+Hae+9RE+o8e3ZOQ==", - "license": "MIT", - "dependencies": { - "lodash": "^4.17.21", - "validator": "~13.12.0" - }, - "engines": { - "node": ">= 8.0.0" - } - }, "node_modules/external-editor": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/external-editor/-/external-editor-3.1.0.tgz", @@ -3490,15 +3347,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/generic-pool": { - "version": "3.9.0", - "resolved": "https://registry.npmmirror.com/generic-pool/-/generic-pool-3.9.0.tgz", - "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -4119,12 +3967,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", @@ -4917,67 +4759,6 @@ "node": ">=6" } }, - "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmmirror.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "license": "MIT", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jsonwebtoken/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/jsonwebtoken/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jwa": { - "version": "1.4.2", - "resolved": "https://registry.npmmirror.com/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmmirror.com/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "license": "MIT", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", @@ -5051,60 +4832,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, "node_modules/lodash.defaults": { "version": "4.2.0", "resolved": "https://registry.npmmirror.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz", "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", "license": "MIT" }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "license": "MIT" - }, "node_modules/lodash.isarguments": { "version": "3.1.0", "resolved": "https://registry.npmmirror.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", "license": "MIT" }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "license": "MIT" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmmirror.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "license": "MIT" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -5112,12 +4851,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "license": "MIT" - }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmmirror.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -5333,27 +5066,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, "node_modules/moment": { "version": "2.30.1", "resolved": "https://registry.npmmirror.com/moment/-/moment-2.30.1.tgz", @@ -5397,25 +5109,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, - "node_modules/multer": { - "version": "1.4.5-lts.2", - "resolved": "https://registry.npmmirror.com/multer/-/multer-1.4.5-lts.2.tgz", - "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==", - "deprecated": "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.", - "license": "MIT", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.0.0", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", - "object-assign": "^4.1.1", - "type-is": "^1.6.4", - "xtend": "^4.0.0" - }, - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/mute-stream": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/mute-stream/-/mute-stream-1.0.0.tgz", @@ -5441,27 +5134,6 @@ "node": ">= 0.6" } }, - "node_modules/node-cron": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/node-cron/-/node-cron-3.0.3.tgz", - "integrity": "sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==", - "license": "ISC", - "dependencies": { - "uuid": "8.3.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/node-cron/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmmirror.com/node-int64/-/node-int64-0.4.0.tgz", @@ -5987,12 +5659,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmmirror.com/prompts/-/prompts-2.4.2.tgz", @@ -6133,27 +5799,6 @@ "dev": true, "license": "MIT" }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", @@ -6167,23 +5812,6 @@ "node": ">=8.10.0" } }, - "node_modules/redis": { - "version": "4.7.1", - "resolved": "https://registry.npmmirror.com/redis/-/redis-4.7.1.tgz", - "integrity": "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==", - "license": "MIT", - "workspaces": [ - "./packages/*" - ], - "dependencies": { - "@redis/bloom": "1.2.0", - "@redis/client": "1.6.1", - "@redis/graph": "1.1.1", - "@redis/json": "1.0.7", - "@redis/search": "1.2.0", - "@redis/time-series": "1.1.0" - } - }, "node_modules/redis-errors": { "version": "1.2.0", "resolved": "https://registry.npmmirror.com/redis-errors/-/redis-errors-1.2.0.tgz", @@ -6787,14 +6415,6 @@ "node": ">= 0.8" } }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz", @@ -7178,12 +6798,6 @@ "node": ">= 0.6" } }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmmirror.com/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "license": "MIT" - }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmmirror.com/undefsafe/-/undefsafe-2.0.5.tgz", @@ -7291,15 +6905,6 @@ "node": ">=10.12.0" } }, - "node_modules/validator": { - "version": "13.12.0", - "resolved": "https://registry.npmmirror.com/validator/-/validator-13.12.0.tgz", - "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmmirror.com/vary/-/vary-1.1.2.tgz", @@ -7471,15 +7076,6 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", diff --git a/src/models/redis.js b/src/models/redis.js index 5036b354..70c0fb43 100644 --- a/src/models/redis.js +++ b/src/models/redis.js @@ -17,7 +17,8 @@ class RedisClient { db: config.redis.db, retryDelayOnFailover: config.redis.retryDelayOnFailover, maxRetriesPerRequest: config.redis.maxRetriesPerRequest, - lazyConnect: config.redis.lazyConnect + lazyConnect: config.redis.lazyConnect, + tls: config.redis.enableTLS ? {} : false }); this.client.on('connect', () => { @@ -71,13 +72,13 @@ class RedisClient { async setApiKey(keyId, keyData, hashedKey = null) { const key = `apikey:${keyId}`; const client = this.getClientSafe(); - + // 维护哈希映射表(用于快速查找) // hashedKey参数是实际的哈希值,用于建立映射 if (hashedKey) { await client.hset('apikey:hash_map', hashedKey, keyId); } - + await client.hset(key, keyData); await client.expire(key, 86400 * 365); // 1年过期 } @@ -89,14 +90,14 @@ class RedisClient { async deleteApiKey(keyId) { const key = `apikey:${keyId}`; - + // 获取要删除的API Key哈希值,以便从映射表中移除 const keyData = await this.client.hgetall(key); if (keyData && keyData.apiKey) { // keyData.apiKey现在存储的是哈希值,直接从映射表删除 await this.client.hdel('apikey:hash_map', keyData.apiKey); } - + return await this.client.del(key); } @@ -108,7 +109,7 @@ class RedisClient { if (key === 'apikey:hash_map') { continue; } - + const keyData = await this.client.hgetall(key); if (keyData && Object.keys(keyData).length > 0) { apiKeys.push({ id: key.replace('apikey:', ''), ...keyData }); @@ -124,12 +125,12 @@ class RedisClient { if (!keyId) { return null; } - + const keyData = await this.client.hgetall(`apikey:${keyId}`); if (keyData && Object.keys(keyData).length > 0) { return { id: keyId, ...keyData }; } - + // 如果数据不存在,清理映射表 await this.client.hdel('apikey:hash_map', hashedKey); return null; @@ -142,26 +143,26 @@ class RedisClient { const currentMonth = `${new Date().getFullYear()}-${String(new Date().getMonth() + 1).padStart(2, '0')}`; const daily = `usage:daily:${keyId}:${today}`; const monthly = `usage:monthly:${keyId}:${currentMonth}`; - + // 按模型统计的键 const modelDaily = `usage:model:daily:${model}:${today}`; const modelMonthly = `usage:model:monthly:${model}:${currentMonth}`; - + // API Key级别的模型统计 const keyModelDaily = `usage:${keyId}:model:daily:${model}:${today}`; const keyModelMonthly = `usage:${keyId}:model:monthly:${model}:${currentMonth}`; - + // 智能处理输入输出token分配 const finalInputTokens = inputTokens || 0; const finalOutputTokens = outputTokens || (finalInputTokens > 0 ? 0 : tokens); const finalCacheCreateTokens = cacheCreateTokens || 0; const finalCacheReadTokens = cacheReadTokens || 0; - + // 重新计算真实的总token数(包括缓存token) const totalTokens = finalInputTokens + finalOutputTokens + finalCacheCreateTokens + finalCacheReadTokens; // 核心token(不包括缓存)- 用于与历史数据兼容 const coreTokens = finalInputTokens + finalOutputTokens; - + await Promise.all([ // 核心token统计(保持向后兼容) this.client.hincrby(key, 'totalTokens', coreTokens), @@ -245,10 +246,10 @@ class RedisClient { const createdAt = keyData.createdAt ? new Date(keyData.createdAt) : new Date(); const now = new Date(); const daysSinceCreated = Math.max(1, Math.ceil((now - createdAt) / (1000 * 60 * 60 * 24))); - + const totalTokens = parseInt(total.totalTokens) || 0; const totalRequests = parseInt(total.totalRequests) || 0; - + // 计算平均RPM (requests per minute) 和 TPM (tokens per minute) const totalMinutes = Math.max(1, daysSinceCreated * 24 * 60); const avgRPM = totalRequests / totalMinutes; @@ -261,14 +262,14 @@ class RedisClient { const inputTokens = parseInt(data.totalInputTokens) || parseInt(data.inputTokens) || 0; const outputTokens = parseInt(data.totalOutputTokens) || parseInt(data.outputTokens) || 0; const requests = parseInt(data.totalRequests) || parseInt(data.requests) || 0; - + // 新增缓存token字段 const cacheCreateTokens = parseInt(data.totalCacheCreateTokens) || parseInt(data.cacheCreateTokens) || 0; const cacheReadTokens = parseInt(data.totalCacheReadTokens) || parseInt(data.cacheReadTokens) || 0; const allTokens = parseInt(data.totalAllTokens) || parseInt(data.allTokens) || 0; - + const totalFromSeparate = inputTokens + outputTokens; - + if (totalFromSeparate === 0 && tokens > 0) { // 旧数据:没有输入输出分离 return { @@ -325,7 +326,7 @@ class RedisClient { // 获取所有API Key ID const apiKeyIds = []; const apiKeyKeys = await client.keys('apikey:*'); - + for (const key of apiKeyKeys) { if (key === 'apikey:hash_map') continue; // 跳过哈希映射表 const keyId = key.replace('apikey:', ''); @@ -444,7 +445,7 @@ class RedisClient { // 🔗 OAuth会话管理 async setOAuthSession(sessionId, sessionData, ttl = 600) { // 10分钟过期 const key = `oauth:${sessionId}`; - + // 序列化复杂对象,特别是 proxy 配置 const serializedData = {}; for (const [dataKey, value] of Object.entries(sessionData)) { @@ -454,7 +455,7 @@ class RedisClient { serializedData[dataKey] = value; } } - + await this.client.hset(key, serializedData); await this.client.expire(key, ttl); } @@ -462,7 +463,7 @@ class RedisClient { async getOAuthSession(sessionId) { const key = `oauth:${sessionId}`; const data = await this.client.hgetall(key); - + // 反序列化 proxy 字段 if (data.proxy) { try { @@ -472,7 +473,7 @@ class RedisClient { data.proxy = null; } } - + return data; } @@ -485,11 +486,11 @@ class RedisClient { async checkRateLimit(identifier, limit = 100, window = 60) { const key = `ratelimit:${identifier}`; const current = await this.client.incr(key); - + if (current === 1) { await this.client.expire(key, window); } - + return { allowed: current <= limit, current, @@ -518,34 +519,34 @@ class RedisClient { try { const today = new Date().toISOString().split('T')[0]; const dailyKeys = await this.client.keys(`usage:daily:*:${today}`); - + let totalRequestsToday = 0; let totalTokensToday = 0; let totalInputTokensToday = 0; let totalOutputTokensToday = 0; let totalCacheCreateTokensToday = 0; let totalCacheReadTokensToday = 0; - + // 批量获取所有今日数据,提高性能 if (dailyKeys.length > 0) { const pipeline = this.client.pipeline(); dailyKeys.forEach(key => pipeline.hgetall(key)); const results = await pipeline.exec(); - + for (const [error, dailyData] of results) { if (error || !dailyData) continue; - + totalRequestsToday += parseInt(dailyData.requests) || 0; const currentDayTokens = parseInt(dailyData.tokens) || 0; totalTokensToday += currentDayTokens; - + // 处理旧数据兼容性:如果有总token但没有输入输出分离,则使用总token作为输出token const inputTokens = parseInt(dailyData.inputTokens) || 0; const outputTokens = parseInt(dailyData.outputTokens) || 0; const cacheCreateTokens = parseInt(dailyData.cacheCreateTokens) || 0; const cacheReadTokens = parseInt(dailyData.cacheReadTokens) || 0; const totalTokensFromSeparate = inputTokens + outputTokens; - + if (totalTokensFromSeparate === 0 && currentDayTokens > 0) { // 旧数据:没有输入输出分离,假设70%为输出,30%为输入(基于一般对话比例) totalOutputTokensToday += Math.round(currentDayTokens * 0.7); @@ -555,7 +556,7 @@ class RedisClient { totalInputTokensToday += inputTokens; totalOutputTokensToday += outputTokens; } - + // 添加cache token统计 totalCacheCreateTokensToday += cacheCreateTokens; totalCacheReadTokensToday += cacheReadTokens; @@ -565,12 +566,12 @@ class RedisClient { // 获取今日创建的API Key数量(批量优化) const allApiKeys = await this.client.keys('apikey:*'); let apiKeysCreatedToday = 0; - + if (allApiKeys.length > 0) { const pipeline = this.client.pipeline(); allApiKeys.forEach(key => pipeline.hget(key, 'createdAt')); const results = await pipeline.exec(); - + for (const [error, createdAt] of results) { if (!error && createdAt && createdAt.startsWith(today)) { apiKeysCreatedToday++; @@ -610,40 +611,40 @@ class RedisClient { let totalInputTokens = 0; let totalOutputTokens = 0; let oldestCreatedAt = new Date(); - + // 批量获取所有usage数据和key数据,提高性能 const usageKeys = allApiKeys.map(key => `usage:${key.replace('apikey:', '')}`); const pipeline = this.client.pipeline(); - + // 添加所有usage查询 usageKeys.forEach(key => pipeline.hgetall(key)); // 添加所有key数据查询 allApiKeys.forEach(key => pipeline.hgetall(key)); - + const results = await pipeline.exec(); const usageResults = results.slice(0, usageKeys.length); const keyResults = results.slice(usageKeys.length); - + for (let i = 0; i < allApiKeys.length; i++) { const totalData = usageResults[i][1] || {}; const keyData = keyResults[i][1] || {}; - + totalRequests += parseInt(totalData.totalRequests) || 0; totalTokens += parseInt(totalData.totalTokens) || 0; totalInputTokens += parseInt(totalData.totalInputTokens) || 0; totalOutputTokens += parseInt(totalData.totalOutputTokens) || 0; - + const createdAt = keyData.createdAt ? new Date(keyData.createdAt) : new Date(); if (createdAt < oldestCreatedAt) { oldestCreatedAt = createdAt; } } - + const now = new Date(); // 保持与个人API Key计算一致的算法:按天计算然后转换为分钟 const daysSinceOldest = Math.max(1, Math.ceil((now - oldestCreatedAt) / (1000 * 60 * 60 * 24))); const totalMinutes = daysSinceOldest * 24 * 60; - + return { systemRPM: Math.round((totalRequests / totalMinutes) * 100) / 100, systemTPM: Math.round((totalTokens / totalMinutes) * 100) / 100, @@ -693,7 +694,7 @@ class RedisClient { for (const pattern of patterns) { const keys = await this.client.keys(pattern); const pipeline = this.client.pipeline(); - + for (const key of keys) { const ttl = await this.client.ttl(key); if (ttl === -1) { // 没有设置过期时间的键 @@ -704,7 +705,7 @@ class RedisClient { } } } - + await pipeline.exec(); }