From ab06aa4bf53fb3cd28242413e07a3526869106a0 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Wed, 11 Sep 2024 22:36:50 +0300 Subject: [PATCH 1/6] Add support for outgoing request proxying Closes #2824 --- default/config.yaml | 10 + package-lock.json | 484 ++++++++++++++++++++++++++++++++++++++++++- package.json | 1 + server.js | 4 + src/request-proxy.js | 52 +++++ 5 files changed, 549 insertions(+), 2 deletions(-) create mode 100644 src/request-proxy.js diff --git a/default/config.yaml b/default/config.yaml index 58db04a49..5f3e0ce9a 100644 --- a/default/config.yaml +++ b/default/config.yaml @@ -37,6 +37,16 @@ basicAuthUser: password: "password" # Enables CORS proxy middleware enableCorsProxy: false +# -- REQUEST PROXY CONFIGURATION -- +requestProxy: + # If a proxy is enabled, all outgoing HTTP/HTTPS requests will be routed through it. + enabled: false + # Proxy URL. Possible protocols: http, https, socks, socks5, socks4, pac + url: "socks5://username:password@example.com:1080" + # Proxy bypass list. Requests to these hosts won't be routed through the proxy. + bypass: + - localhost + - 127.0.0.1 # Enable multi-user mode enableUserAccounts: false # Enable discreet login mode: hides user list on the login screen diff --git a/package-lock.json b/package-lock.json index 58e0034e6..f66c0991e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,7 @@ "png-chunk-text": "^1.0.0", "png-chunks-encode": "^1.0.0", "png-chunks-extract": "^1.0.0", + "proxy-agent": "^6.4.0", "rate-limiter-flexible": "^5.0.0", "response-time": "^2.3.2", "sanitize-filename": "^1.6.3", @@ -941,6 +942,12 @@ "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", "license": "MIT" }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "license": "MIT" + }, "node_modules/@types/cacheable-request": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", @@ -1080,6 +1087,41 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/agentkeepalive": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", @@ -1382,6 +1424,18 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/async": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", @@ -1445,6 +1499,15 @@ ], "license": "MIT" }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/bing-translate-api": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/bing-translate-api/-/bing-translate-api-2.9.1.tgz", @@ -2250,6 +2313,15 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -2316,6 +2388,20 @@ "node": ">=8" } }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2528,6 +2614,27 @@ "node": ">=0.8.0" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, "node_modules/eslint": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", @@ -2683,6 +2790,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", @@ -2713,7 +2833,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -2723,7 +2842,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -3071,6 +3189,20 @@ "node": ">= 0.6" } }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -3130,6 +3262,44 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-uri": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", + "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4", + "fs-extra": "^11.2.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/get-uri/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/get-uri/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/gifwrap": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/gifwrap/-/gifwrap-0.10.1.tgz", @@ -3369,6 +3539,42 @@ "node": ">= 0.8" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/http2-wrapper": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", @@ -3382,6 +3588,42 @@ "node": ">=10.19.0" } }, + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", @@ -3485,6 +3727,19 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/ip-matching": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ip-matching/-/ip-matching-2.1.2.tgz", @@ -3669,6 +3924,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "license": "MIT" + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -3761,6 +4022,18 @@ "dev": true, "license": "MIT" }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/keygrip": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", @@ -4053,6 +4326,15 @@ "node": ">= 0.6" } }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -4323,6 +4605,61 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pac-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", + "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.5", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/pac-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -4618,6 +4955,57 @@ "node": ">= 0.10" } }, + "node_modules/proxy-agent": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", + "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.3", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.0.1", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -5165,6 +5553,83 @@ "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==", "license": "MIT" }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", + "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socks-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -5378,6 +5843,12 @@ "utf8-byte-length": "^1.0.1" } }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, "node_modules/tsscmp": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", @@ -5437,6 +5908,15 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "license": "MIT" }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/package.json b/package.json index 93c240873..c04e406ef 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "png-chunk-text": "^1.0.0", "png-chunks-encode": "^1.0.0", "png-chunks-extract": "^1.0.0", + "proxy-agent": "^6.4.0", "rate-limiter-flexible": "^5.0.0", "response-time": "^2.3.2", "sanitize-filename": "^1.6.3", diff --git a/server.js b/server.js index a32b9da3d..03b678e3c 100644 --- a/server.js +++ b/server.js @@ -37,6 +37,7 @@ util.inspect.defaultOptions.depth = 4; const userModule = require('./src/users'); const basicAuthMiddleware = require('./src/middleware/basicAuth'); const whitelistMiddleware = require('./src/middleware/whitelist'); +const initRequestProxy = require('./src/request-proxy'); const contentManager = require('./src/endpoints/content-manager'); const { getVersion, @@ -662,6 +663,9 @@ const preSetupTasks = async function () { console.error('Uncaught exception:', err); exitProcess(); }); + + // Add request proxy. + initRequestProxy(); }; /** diff --git a/src/request-proxy.js b/src/request-proxy.js new file mode 100644 index 000000000..002745584 --- /dev/null +++ b/src/request-proxy.js @@ -0,0 +1,52 @@ +const http = require('node:http'); +const https = require('node:https'); + +const { getConfigValue, isValidUrl, color } = require('./util.js'); + +const LOG_HEADER = '[Request Proxy]'; + +function initRequestProxy() { + try { + const { ProxyAgent } = require('proxy-agent'); + const proxyEnabled = getConfigValue('requestProxy.enabled', false); + + // No proxy is enabled, so return + if (!proxyEnabled) { + return; + } + + const proxyUrl = getConfigValue('requestProxy.url', ''); + + if (!proxyUrl) { + console.error(color.red(LOG_HEADER), 'No proxy URL provided'); + return; + } + + if (!isValidUrl(proxyUrl)) { + console.error(color.red(LOG_HEADER), 'Invalid proxy URL provided'); + return; + } + + // ProxyAgent uses proxy-from-env under the hood + // Reference: https://github.com/Rob--W/proxy-from-env + process.env.all_proxy = proxyUrl; + + const proxyBypass = getConfigValue('requestProxy.bypass', []); + + if (Array.isArray(proxyBypass) && proxyBypass.length > 0) { + process.env.no_proxy = proxyBypass.join(','); + } + + const proxyAgent = new ProxyAgent(); + http.globalAgent = proxyAgent; + https.globalAgent = proxyAgent; + + console.log(); + console.log(color.green(LOG_HEADER), 'Proxy URL is used:', color.blue(proxyUrl)); + console.log(); + } catch (error) { + console.error(color.red(LOG_HEADER), 'Failed to initialize request proxy:', error); + } +} + +module.exports = initRequestProxy; From 81cd38d4f70bfbb31641618382afd6d0d8df99aa Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 12 Sep 2024 20:13:05 +0300 Subject: [PATCH 2/6] #2832 /ask text non-required --- public/scripts/slash-commands.js | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/public/scripts/slash-commands.js b/public/scripts/slash-commands.js index 577009208..68f001359 100644 --- a/public/scripts/slash-commands.js +++ b/public/scripts/slash-commands.js @@ -440,7 +440,7 @@ export function initDefaultSlashCommands() { ], unnamedArgumentList: [ new SlashCommandArgument( - 'prompt', [ARGUMENT_TYPE.STRING], true, false, + 'prompt', [ARGUMENT_TYPE.STRING], false, false, ), ], helpString: 'Asks a specified character card a prompt. Character name must be provided in a named argument.', @@ -2469,31 +2469,21 @@ async function askCharacter(args, text) { // Not supported in group chats // TODO: Maybe support group chats? if (selected_group) { - toastr.error('Cannot run this command in a group chat!'); - return ''; - } - - if (!text) { - console.warn('WARN: No text provided for /ask command'); - toastr.warning('No text provided for /ask command'); + toastr.error('Cannot run /ask command in a group chat!'); return ''; } let name = ''; - let mesText = ''; if (args?.name) { name = args.name.trim(); - mesText = text.trim(); - if (!name && !mesText) { - toastr.warning('You must specify a name and text to ask.'); + if (!name) { + toastr.warning('You must specify a name of the character to ask.'); return ''; } } - mesText = getRegexedString(mesText, regex_placement.SLASH_COMMAND); - const prevChId = this_chid; // Find the character @@ -2503,9 +2493,12 @@ async function askCharacter(args, text) { return ''; } - // Sending a message implicitly saves the chat, so this needs to be done before changing the character - // Otherwise, a corruption will occur - await sendMessageAsUser(mesText, ''); + if (text) { + const mesText = getRegexedString(text.trim(), regex_placement.SLASH_COMMAND); + // Sending a message implicitly saves the chat, so this needs to be done before changing the character + // Otherwise, a corruption will occur + await sendMessageAsUser(mesText, ''); + } // Override character and send a user message setCharacterId(String(chId)); From b9d5c61b2ed5832c7f243b2e9a3c6aac5e338211 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 12 Sep 2024 20:25:58 +0300 Subject: [PATCH 3/6] Add CLI for request proxy Closes #364, #377, #1756 --- server.js | 22 +++++++++++++++++++++- src/request-proxy.js | 30 +++++++++++++++++------------- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/server.js b/server.js index 03b678e3c..309ce0fc0 100644 --- a/server.js +++ b/server.js @@ -76,6 +76,10 @@ const DEFAULT_AVOID_LOCALHOST = false; const DEFAULT_AUTORUN_HOSTNAME = 'auto'; const DEFAULT_AUTORUN_PORT = -1; +const DEFAULT_PROXY_ENABLED = false; +const DEFAULT_PROXY_URL = ''; +const DEFAULT_PROXY_BYPASS = []; + const cliArguments = yargs(hideBin(process.argv)) .usage('Usage: [options]') .option('enableIPv6', { @@ -146,6 +150,18 @@ const cliArguments = yargs(hideBin(process.argv)) type: 'boolean', default: null, describe: 'Enables basic authentication', + }).option('requestProxyEnabled', { + type: 'boolean', + default: null, + describe: 'Enables request proxy', + }).option('requestProxyUrl', { + type: 'string', + default: null, + describe: 'Request proxy URL', + }).option('requestProxyBypasss', { + type: 'array', + default: null, + describe: 'Request proxy bypass', }).parseSync(); // change all relative paths @@ -182,6 +198,10 @@ const dnsPreferIPv6 = cliArguments.dnsPreferIPv6 ?? getConfigValue('dnsPreferIPv const avoidLocalhost = cliArguments.avoidLocalhost ?? getConfigValue('avoidLocalhost', DEFAULT_AVOID_LOCALHOST); +const proxyEnabled = cliArguments.requestProxyEnabled ?? getConfigValue('requestProxy.enabled', DEFAULT_PROXY_ENABLED); +const proxyUrl = cliArguments.requestProxyUrl ?? getConfigValue('requestProxy.url', DEFAULT_PROXY_URL); +const proxyBypass = cliArguments.requestProxyBypass ?? getConfigValue('requestProxy.bypass', DEFAULT_PROXY_BYPASS); + if (dnsPreferIPv6) { // Set default DNS resolution order to IPv6 first dns.setDefaultResultOrder('ipv6first'); @@ -665,7 +685,7 @@ const preSetupTasks = async function () { }); // Add request proxy. - initRequestProxy(); + initRequestProxy({ enabled: proxyEnabled, url: proxyUrl, bypass: proxyBypass }); }; /** diff --git a/src/request-proxy.js b/src/request-proxy.js index 002745584..815fa76a3 100644 --- a/src/request-proxy.js +++ b/src/request-proxy.js @@ -1,40 +1,44 @@ const http = require('node:http'); const https = require('node:https'); -const { getConfigValue, isValidUrl, color } = require('./util.js'); +const { isValidUrl, color } = require('./util.js'); const LOG_HEADER = '[Request Proxy]'; -function initRequestProxy() { +/** + * Initialize request proxy. + * @param {ProxySettings} settings Proxy settings. + * @typedef {object} ProxySettings + * @property {boolean} enabled Whether proxy is enabled. + * @property {string} url Proxy URL. + * @property {string[]} bypass List of URLs to bypass proxy. + */ +function initRequestProxy({ enabled, url, bypass }) { try { const { ProxyAgent } = require('proxy-agent'); - const proxyEnabled = getConfigValue('requestProxy.enabled', false); // No proxy is enabled, so return - if (!proxyEnabled) { + if (!enabled) { return; } - const proxyUrl = getConfigValue('requestProxy.url', ''); - - if (!proxyUrl) { + if (!url) { console.error(color.red(LOG_HEADER), 'No proxy URL provided'); return; } - if (!isValidUrl(proxyUrl)) { + if (!isValidUrl(url)) { console.error(color.red(LOG_HEADER), 'Invalid proxy URL provided'); return; } // ProxyAgent uses proxy-from-env under the hood // Reference: https://github.com/Rob--W/proxy-from-env - process.env.all_proxy = proxyUrl; + process.env.all_proxy = url; - const proxyBypass = getConfigValue('requestProxy.bypass', []); - if (Array.isArray(proxyBypass) && proxyBypass.length > 0) { - process.env.no_proxy = proxyBypass.join(','); + if (Array.isArray(bypass) && bypass.length > 0) { + process.env.no_proxy = bypass.join(','); } const proxyAgent = new ProxyAgent(); @@ -42,7 +46,7 @@ function initRequestProxy() { https.globalAgent = proxyAgent; console.log(); - console.log(color.green(LOG_HEADER), 'Proxy URL is used:', color.blue(proxyUrl)); + console.log(color.green(LOG_HEADER), 'Proxy URL is used:', color.blue(url)); console.log(); } catch (error) { console.error(color.red(LOG_HEADER), 'Failed to initialize request proxy:', error); From edd6dfa81989874449963bd00b0ed6736c87f11d Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 12 Sep 2024 20:29:46 +0300 Subject: [PATCH 4/6] Fix typo --- server.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server.js b/server.js index 309ce0fc0..101078a03 100644 --- a/server.js +++ b/server.js @@ -157,11 +157,11 @@ const cliArguments = yargs(hideBin(process.argv)) }).option('requestProxyUrl', { type: 'string', default: null, - describe: 'Request proxy URL', - }).option('requestProxyBypasss', { + describe: 'Request proxy URL (HTTP or SOCKS protocols)', + }).option('requestProxyBypass', { type: 'array', default: null, - describe: 'Request proxy bypass', + describe: 'Request proxy bypass list (space separated list of hosts)', }).parseSync(); // change all relative paths From 2efb637863ac2bc28e5a3de3909c9b3b3e081a32 Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 12 Sep 2024 20:30:52 +0300 Subject: [PATCH 5/6] Better option description --- server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server.js b/server.js index 101078a03..b9fff9bc7 100644 --- a/server.js +++ b/server.js @@ -153,7 +153,7 @@ const cliArguments = yargs(hideBin(process.argv)) }).option('requestProxyEnabled', { type: 'boolean', default: null, - describe: 'Enables request proxy', + describe: 'Enables a use of proxy for outgoing requests', }).option('requestProxyUrl', { type: 'string', default: null, From 8acacf9e0ff21a7cfb8ef5508364d569c9aacf7f Mon Sep 17 00:00:00 2001 From: Cohee <18619528+Cohee1207@users.noreply.github.com> Date: Thu, 12 Sep 2024 20:35:30 +0300 Subject: [PATCH 6/6] Document cmd args in README --- .github/readme.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/.github/readme.md b/.github/readme.md index 36d387166..e40bc2d47 100644 --- a/.github/readme.md +++ b/.github/readme.md @@ -276,6 +276,46 @@ In order to enable viewing your keys by clicking a button in the API block: 1. Set the value of `allowKeysExposure` to `true` in `config.yaml` file. 2. Restart the SillyTavern server. +## Command-line arguments + +You can pass command-line arguments to SillyTavern server startup to override some settings in `config.yaml`. + +### Examples + +```shell +node server.js --port 8000 --listen false +# or +npm run start -- --port 8000 --listen false +# or (Windows only) +Start.bat --port 8000 --listen false +``` + +### Supported arguments + +| Option | Description | Type | Default | +|-------------------------|------------------------------------------------------------------------------------------------------|----------|------------------------------| +| `--version` | Show version number | boolean | | +| `--enableIPv6` | Enables IPv6. | boolean | false | +| `--enableIPv4` | Enables IPv4. | boolean | true | +| `--port` | Sets the port under which SillyTavern will run. If not provided falls back to yaml config 'port'. | number | 8000 | +| `--dnsPreferIPv6` | Prefers IPv6 for dns. If not provided falls back to yaml config 'preferIPv6'. | boolean | false | +| `--autorun` | Automatically launch SillyTavern in the browser. If not provided falls back to yaml config 'autorun'.| boolean | false | +| `--autorunHostname` | The autorun hostname, probably best left on 'auto'. | string | null | +| `--autorunPortOverride` | Overrides the port for autorun. | string | null | +| `--listen` | SillyTavern is listening on all network interfaces. If not provided falls back to yaml config 'listen'.| boolean | false | +| `--corsProxy` | Enables CORS proxy. If not provided falls back to yaml config 'enableCorsProxy'. | boolean | false | +| `--disableCsrf` | Disables CSRF protection | boolean | null | +| `--ssl` | Enables SSL | boolean | false | +| `--certPath` | Path to your certificate file. | string | "certs/cert.pem" | +| `--keyPath` | Path to your private key file. | string | "certs/privkey.pem" | +| `--whitelist` | Enables whitelist mode | boolean | null | +| `--dataRoot` | Root directory for data storage | string | null | +| `--avoidLocalhost` | Avoids using 'localhost' for autorun in auto mode. | boolean | null | +| `--basicAuthMode` | Enables basic authentication | boolean | null | +| `--requestProxyEnabled` | Enables a use of proxy for outgoing requests | boolean | null | +| `--requestProxyUrl` | Request proxy URL (HTTP or SOCKS protocols) | string | null | +| `--requestProxyBypass` | Request proxy bypass list (space separated list of hosts) | array | null | + ## Remote connections Most often this is for people who want to use SillyTavern on their mobile phones while their PC runs the ST server on the same wifi network.