mirror of
https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 00:52:40 +00:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef4d0a228b | ||
|
|
e816e5b3c0 | ||
|
|
005051c4ac | ||
|
|
61078b10f3 | ||
|
|
bfa1b11a0e | ||
|
|
7d60aed50d | ||
|
|
e20d0c1dc9 | ||
|
|
c5660024fb | ||
|
|
76e12bd6a0 | ||
|
|
3a33446422 | ||
|
|
17b12711b4 | ||
|
|
c266635ba1 | ||
|
|
f34eac9568 | ||
|
|
aa5b51a3cc | ||
|
|
b8897dd94a | ||
|
|
71958e6bb1 | ||
|
|
fa5f88ae85 | ||
|
|
212aa7730d | ||
|
|
4c5c9baa3e | ||
|
|
25dcbdc4dd | ||
|
|
282780b791 | ||
|
|
cde09541cf | ||
|
|
6731c42edb | ||
|
|
64b9505035 | ||
|
|
b0347637bc | ||
|
|
ab67ce9f5a | ||
|
|
cacc106c68 | ||
|
|
542fcc44a1 | ||
|
|
dca3d2f79c | ||
|
|
3e14f91347 | ||
|
|
4aafdaaddb | ||
|
|
e4f646af0c | ||
|
|
532be2ff8c | ||
|
|
37fc7ac88e | ||
|
|
9e0028219d | ||
|
|
54750d552b |
0
.gitmodules
vendored
0
.gitmodules
vendored
@@ -26,13 +26,14 @@ Core functionalities:
|
|||||||
|
|
||||||
### Supported Input Formats
|
### Supported Input Formats
|
||||||
|
|
||||||
> ⚠️ Do not use `Shadowrocket` or `NekoBox` to export URI and then import it as input. The URIs exported in this way may not be standard URIs.
|
> ⚠️ Do not use `Shadowrocket` or `NekoBox` to export URI and then import it as input. The URIs exported in this way may not be standard URIs. However, we have already supported some very common non-standard URIs (such as VMess, VLESS).
|
||||||
|
|
||||||
- [x] Proxy URI Scheme(`socks5`, `socks5+tls`, `http`, `https`(it's ok))
|
- [x] Proxy URI Scheme(`socks5`, `socks5+tls`, `http`, `https`(it's ok))
|
||||||
|
|
||||||
example: `socks5+tls://user:pass@ip:port#name`
|
example: `socks5+tls://user:pass@ip:port#name`
|
||||||
|
|
||||||
- [x] URI(AnyTLS, SOCKS, SS, SSR, VMess, VLESS, Trojan, Hysteria, Hysteria 2, TUIC v5, WireGuard)
|
- [x] URI(AnyTLS, SOCKS, SS, SSR, VMess, VLESS, Trojan, Hysteria, Hysteria 2, TUIC v5, WireGuard)
|
||||||
|
> Please note, HTTP(s) does not have a standard URI format, so it is not supported. Please use other formats.
|
||||||
- [x] Clash Proxies YAML
|
- [x] Clash Proxies YAML
|
||||||
- [x] Clash Proxy JSON(single line)
|
- [x] Clash Proxy JSON(single line)
|
||||||
- [x] QX (SS, SSR, VMess, Trojan, HTTP, SOCKS5, VLESS)
|
- [x] QX (SS, SSR, VMess, Trojan, HTTP, SOCKS5, VLESS)
|
||||||
@@ -42,7 +43,7 @@ Core functionalities:
|
|||||||
- [x] Clash.Meta (Direct, SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard, Hysteria, Hysteria 2, TUIC, SSH, mieru, AnyTLS)
|
- [x] Clash.Meta (Direct, SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard, Hysteria, Hysteria 2, TUIC, SSH, mieru, AnyTLS)
|
||||||
- [x] Stash (SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard, Hysteria, TUIC, Juicity, SSH)
|
- [x] Stash (SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard, Hysteria, TUIC, Juicity, SSH)
|
||||||
|
|
||||||
Deprecated:
|
Deprecated(The frontend doesn't show it, but the backend still supports it, with the query parameter `target=Clash`):
|
||||||
|
|
||||||
- [x] Clash (SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard)
|
- [x] Clash (SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sub-store",
|
"name": "sub-store",
|
||||||
"version": "2.19.49",
|
"version": "2.19.83",
|
||||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
|
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
|
||||||
"main": "src/main.js",
|
"main": "src/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -27,17 +27,19 @@
|
|||||||
"automerge": "1.0.1-preview.7",
|
"automerge": "1.0.1-preview.7",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"dotenv": "^16.4.7",
|
|
||||||
"connect-history-api-fallback": "^2.0.0",
|
"connect-history-api-fallback": "^2.0.0",
|
||||||
"cron": "^3.1.6",
|
"cron": "^3.1.6",
|
||||||
"dns-packet": "^5.6.1",
|
"dns-packet": "^5.6.1",
|
||||||
|
"dotenv": "^16.4.7",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"mime-types": "^2.1.35",
|
"fetch-socks": "^1.3.2",
|
||||||
"http-proxy-middleware": "^3.0.3",
|
"http-proxy-middleware": "^3.0.3",
|
||||||
"ip-address": "^9.0.5",
|
"ip-address": "^9.0.5",
|
||||||
"js-base64": "^3.7.2",
|
"js-base64": "^3.7.2",
|
||||||
|
"json5": "^2.2.3",
|
||||||
"jsrsasign": "^11.1.0",
|
"jsrsasign": "^11.1.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
"mime-types": "^2.1.35",
|
||||||
"ms": "^2.1.3",
|
"ms": "^2.1.3",
|
||||||
"nanoid": "^3.3.3",
|
"nanoid": "^3.3.3",
|
||||||
"semver": "^7.6.3",
|
"semver": "^7.6.3",
|
||||||
|
|||||||
41
backend/pnpm-lock.yaml
generated
41
backend/pnpm-lock.yaml
generated
@@ -34,9 +34,15 @@ importers:
|
|||||||
dns-packet:
|
dns-packet:
|
||||||
specifier: ^5.6.1
|
specifier: ^5.6.1
|
||||||
version: 5.6.1
|
version: 5.6.1
|
||||||
|
dotenv:
|
||||||
|
specifier: ^16.4.7
|
||||||
|
version: 16.5.0
|
||||||
express:
|
express:
|
||||||
specifier: ^4.17.1
|
specifier: ^4.17.1
|
||||||
version: 4.21.2
|
version: 4.21.2
|
||||||
|
fetch-socks:
|
||||||
|
specifier: ^1.3.2
|
||||||
|
version: 1.3.2
|
||||||
http-proxy-middleware:
|
http-proxy-middleware:
|
||||||
specifier: ^3.0.3
|
specifier: ^3.0.3
|
||||||
version: 3.0.3
|
version: 3.0.3
|
||||||
@@ -46,12 +52,18 @@ importers:
|
|||||||
js-base64:
|
js-base64:
|
||||||
specifier: ^3.7.2
|
specifier: ^3.7.2
|
||||||
version: 3.7.7
|
version: 3.7.7
|
||||||
|
json5:
|
||||||
|
specifier: ^2.2.3
|
||||||
|
version: 2.2.3
|
||||||
jsrsasign:
|
jsrsasign:
|
||||||
specifier: ^11.1.0
|
specifier: ^11.1.0
|
||||||
version: 11.1.0
|
version: 11.1.0
|
||||||
lodash:
|
lodash:
|
||||||
specifier: ^4.17.21
|
specifier: ^4.17.21
|
||||||
version: 4.17.21
|
version: 4.17.21
|
||||||
|
mime-types:
|
||||||
|
specifier: ^2.1.35
|
||||||
|
version: 2.1.35
|
||||||
ms:
|
ms:
|
||||||
specifier: ^2.1.3
|
specifier: ^2.1.3
|
||||||
version: 2.1.3
|
version: 2.1.3
|
||||||
@@ -1655,6 +1667,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==}
|
resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==}
|
||||||
engines: {node: '>=0.4', npm: '>=1.2'}
|
engines: {node: '>=0.4', npm: '>=1.2'}
|
||||||
|
|
||||||
|
dotenv@16.5.0:
|
||||||
|
resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
dunder-proto@1.0.1:
|
dunder-proto@1.0.1:
|
||||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -1961,6 +1977,9 @@ packages:
|
|||||||
fastq@1.18.0:
|
fastq@1.18.0:
|
||||||
resolution: {integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==}
|
resolution: {integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==}
|
||||||
|
|
||||||
|
fetch-socks@1.3.2:
|
||||||
|
resolution: {integrity: sha512-vkH5+Zgj2yEbU57Cei0iyLgTZ4OkEKJj56Xu3ViB5dpsl599JgEooQ3x6NVagIFRHWnWJ+7K0MO0aIV1TMgvnw==}
|
||||||
|
|
||||||
file-entry-cache@6.0.1:
|
file-entry-cache@6.0.1:
|
||||||
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
||||||
engines: {node: ^10.12.0 || >=12.0.0}
|
engines: {node: ^10.12.0 || >=12.0.0}
|
||||||
@@ -3623,6 +3642,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==}
|
resolution: {integrity: sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
smart-buffer@4.2.0:
|
||||||
|
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
|
||||||
|
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
|
||||||
|
|
||||||
snapdragon-node@2.1.1:
|
snapdragon-node@2.1.1:
|
||||||
resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==}
|
resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -3635,6 +3658,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==}
|
resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
socks@2.8.6:
|
||||||
|
resolution: {integrity: sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==}
|
||||||
|
engines: {node: '>= 10.0.0', npm: '>= 3.0.0'}
|
||||||
|
|
||||||
source-map-generator@0.8.0:
|
source-map-generator@0.8.0:
|
||||||
resolution: {integrity: sha512-psgxdGMwl5MZM9S3FWee4EgsEaIjahYV5AzGnwUvPhWeITz/j6rKpysQHlQ4USdxvINlb8lKfWGIXwfkrgtqkA==}
|
resolution: {integrity: sha512-psgxdGMwl5MZM9S3FWee4EgsEaIjahYV5AzGnwUvPhWeITz/j6rKpysQHlQ4USdxvINlb8lKfWGIXwfkrgtqkA==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
@@ -6057,6 +6084,8 @@ snapshots:
|
|||||||
|
|
||||||
domain-browser@1.2.0: {}
|
domain-browser@1.2.0: {}
|
||||||
|
|
||||||
|
dotenv@16.5.0: {}
|
||||||
|
|
||||||
dunder-proto@1.0.1:
|
dunder-proto@1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind-apply-helpers: 1.0.1
|
call-bind-apply-helpers: 1.0.1
|
||||||
@@ -6538,6 +6567,11 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
reusify: 1.0.4
|
reusify: 1.0.4
|
||||||
|
|
||||||
|
fetch-socks@1.3.2:
|
||||||
|
dependencies:
|
||||||
|
socks: 2.8.6
|
||||||
|
undici: 7.4.0
|
||||||
|
|
||||||
file-entry-cache@6.0.1:
|
file-entry-cache@6.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
flat-cache: 3.2.0
|
flat-cache: 3.2.0
|
||||||
@@ -8409,6 +8443,8 @@ snapshots:
|
|||||||
|
|
||||||
slash@1.0.0: {}
|
slash@1.0.0: {}
|
||||||
|
|
||||||
|
smart-buffer@4.2.0: {}
|
||||||
|
|
||||||
snapdragon-node@2.1.1:
|
snapdragon-node@2.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
define-property: 1.0.0
|
define-property: 1.0.0
|
||||||
@@ -8432,6 +8468,11 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
socks@2.8.6:
|
||||||
|
dependencies:
|
||||||
|
ip-address: 9.0.5
|
||||||
|
smart-buffer: 4.2.0
|
||||||
|
|
||||||
source-map-generator@0.8.0: {}
|
source-map-generator@0.8.0: {}
|
||||||
|
|
||||||
source-map-resolve@0.5.3:
|
source-map-resolve@0.5.3:
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { getFlag, removeFlag, getISO, MMDB } from '@/utils/geo';
|
|||||||
import Gist from '@/utils/gist';
|
import Gist from '@/utils/gist';
|
||||||
import { isPresent } from './producers/utils';
|
import { isPresent } from './producers/utils';
|
||||||
import { doh } from '@/utils/dns';
|
import { doh } from '@/utils/dns';
|
||||||
|
import JSON5 from 'json5';
|
||||||
|
|
||||||
function preprocess(raw) {
|
function preprocess(raw) {
|
||||||
for (const processor of PROXY_PREPROCESSORS) {
|
for (const processor of PROXY_PREPROCESSORS) {
|
||||||
@@ -142,9 +143,9 @@ async function processFn(
|
|||||||
? `#${rawArgs[1]}`
|
? `#${rawArgs[1]}`
|
||||||
: ''
|
: ''
|
||||||
}`;
|
}`;
|
||||||
const downloadUrlMatch = url.match(
|
const downloadUrlMatch = url
|
||||||
/^\/api\/(file|module)\/(.+)/,
|
.split('#')[0]
|
||||||
);
|
.match(/^\/api\/(file|module)\/(.+)/);
|
||||||
if (downloadUrlMatch) {
|
if (downloadUrlMatch) {
|
||||||
let type = '';
|
let type = '';
|
||||||
try {
|
try {
|
||||||
@@ -174,6 +175,17 @@ async function processFn(
|
|||||||
);
|
);
|
||||||
throw new Error(`无法加载 ${type}: ${url}`);
|
throw new Error(`无法加载 ${type}: ${url}`);
|
||||||
}
|
}
|
||||||
|
} else if (url?.startsWith('/')) {
|
||||||
|
try {
|
||||||
|
const fs = eval(`require("fs")`);
|
||||||
|
script = fs.readFileSync(url.split('#')[0], 'utf8');
|
||||||
|
// $.info(`Script loaded: >>>\n ${script}`);
|
||||||
|
} catch (err) {
|
||||||
|
$.error(
|
||||||
|
`Error when reading local script: ${item.args.content}.\n Reason: ${err}`,
|
||||||
|
);
|
||||||
|
throw new Error(`无法从该路径读取脚本文件: ${url}`);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// if this is a remote script, download it
|
// if this is a remote script, download it
|
||||||
try {
|
try {
|
||||||
@@ -336,6 +348,7 @@ export const ProxyUtils = {
|
|||||||
doh,
|
doh,
|
||||||
Buffer,
|
Buffer,
|
||||||
Base64,
|
Base64,
|
||||||
|
JSON5,
|
||||||
};
|
};
|
||||||
|
|
||||||
function tryParse(parser, line) {
|
function tryParse(parser, line) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import getLoonParser from './peggy/loon';
|
|||||||
import getQXParser from './peggy/qx';
|
import getQXParser from './peggy/qx';
|
||||||
import getTrojanURIParser from './peggy/trojan-uri';
|
import getTrojanURIParser from './peggy/trojan-uri';
|
||||||
import $ from '@/core/app';
|
import $ from '@/core/app';
|
||||||
|
import JSON5 from 'json5';
|
||||||
|
|
||||||
import { Base64 } from 'js-base64';
|
import { Base64 } from 'js-base64';
|
||||||
|
|
||||||
@@ -439,7 +440,16 @@ function URI_VMess() {
|
|||||||
type: 'vmess',
|
type: 'vmess',
|
||||||
server,
|
server,
|
||||||
port,
|
port,
|
||||||
cipher: getIfPresent(params.scy, 'auto'),
|
// https://github.com/2dust/v2rayN/wiki/Description-of-VMess-share-link
|
||||||
|
// https://github.com/XTLS/Xray-core/issues/91
|
||||||
|
cipher: [
|
||||||
|
'auto',
|
||||||
|
'aes-128-gcm',
|
||||||
|
'chacha20-poly1305',
|
||||||
|
'none',
|
||||||
|
].includes(params.scy)
|
||||||
|
? params.scy
|
||||||
|
: 'auto',
|
||||||
uuid: params.id,
|
uuid: params.id,
|
||||||
alterId: parseInt(
|
alterId: parseInt(
|
||||||
getIfPresent(params.aid ?? params.alterId, 0),
|
getIfPresent(params.aid ?? params.alterId, 0),
|
||||||
@@ -473,8 +483,8 @@ function URI_VMess() {
|
|||||||
['http'].includes(params.type)
|
['http'].includes(params.type)
|
||||||
) {
|
) {
|
||||||
proxy.network = 'http';
|
proxy.network = 'http';
|
||||||
} else if (['grpc'].includes(params.net)) {
|
} else if (['grpc', 'kcp', 'quic'].includes(params.net)) {
|
||||||
proxy.network = 'grpc';
|
proxy.network = params.net;
|
||||||
} else if (
|
} else if (
|
||||||
params.net === 'httpupgrade' ||
|
params.net === 'httpupgrade' ||
|
||||||
proxy.network === 'httpupgrade'
|
proxy.network === 'httpupgrade'
|
||||||
@@ -524,13 +534,28 @@ function URI_VMess() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 传输层应该有配置, 暂时不考虑兼容不给配置的节点
|
// 传输层应该有配置, 暂时不考虑兼容不给配置的节点
|
||||||
if (transportPath || transportHost) {
|
if (
|
||||||
|
transportPath ||
|
||||||
|
transportHost ||
|
||||||
|
['kcp', 'quic'].includes(proxy.network)
|
||||||
|
) {
|
||||||
if (['grpc'].includes(proxy.network)) {
|
if (['grpc'].includes(proxy.network)) {
|
||||||
proxy[`${proxy.network}-opts`] = {
|
proxy[`${proxy.network}-opts`] = {
|
||||||
'grpc-service-name': getIfNotBlank(transportPath),
|
'grpc-service-name': getIfNotBlank(transportPath),
|
||||||
'_grpc-type': getIfNotBlank(params.type),
|
'_grpc-type': getIfNotBlank(params.type),
|
||||||
'_grpc-authority': getIfNotBlank(params.authority),
|
'_grpc-authority': getIfNotBlank(params.authority),
|
||||||
};
|
};
|
||||||
|
} else if (['kcp', 'quic'].includes(proxy.network)) {
|
||||||
|
proxy[`${proxy.network}-opts`] = {
|
||||||
|
[`_${proxy.network}-type`]: getIfNotBlank(
|
||||||
|
params.type,
|
||||||
|
),
|
||||||
|
[`_${proxy.network}-host`]: getIfNotBlank(
|
||||||
|
getIfNotBlank(transportHost),
|
||||||
|
),
|
||||||
|
[`_${proxy.network}-path`]:
|
||||||
|
getIfNotBlank(transportPath),
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
const opts = {
|
const opts = {
|
||||||
path: getIfNotBlank(transportPath),
|
path: getIfNotBlank(transportPath),
|
||||||
@@ -546,6 +571,12 @@ function URI_VMess() {
|
|||||||
delete proxy.network;
|
delete proxy.network;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proxy['client-fingerprint'] = params.fp;
|
||||||
|
proxy.alpn = params.alpn ? params.alpn.split(',') : undefined;
|
||||||
|
// 然而 wiki 和 app 实测中都没有字段表示这个
|
||||||
|
// proxy['skip-cert-verify'] = /(TRUE)|1/i.test(params.allowInsecure);
|
||||||
|
|
||||||
return proxy;
|
return proxy;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1100,14 +1131,14 @@ function Clash_All() {
|
|||||||
const name = 'Clash Parser';
|
const name = 'Clash Parser';
|
||||||
const test = (line) => {
|
const test = (line) => {
|
||||||
try {
|
try {
|
||||||
JSON.parse(line);
|
JSON5.parse(line);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
const parse = (line) => {
|
const parse = (line) => {
|
||||||
const proxy = JSON.parse(line);
|
const proxy = JSON5.parse(line);
|
||||||
if (
|
if (
|
||||||
![
|
![
|
||||||
'anytls',
|
'anytls',
|
||||||
|
|||||||
@@ -105,11 +105,11 @@ wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/under
|
|||||||
proxy.type = "wireguard-surge";
|
proxy.type = "wireguard-surge";
|
||||||
handleShadowTLS();
|
handleShadowTLS();
|
||||||
}
|
}
|
||||||
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/fast_open/tfo/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/port_hopping_interval/others)* {
|
hysteria2 = tag equals "hysteria2" address (no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/sni/tls_verification/passwordk/tls_fingerprint/download_bandwidth/ecn/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/port_hopping_interval/others)* {
|
||||||
proxy.type = "hysteria2";
|
proxy.type = "hysteria2";
|
||||||
handleShadowTLS();
|
handleShadowTLS();
|
||||||
}
|
}
|
||||||
socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (udp_relay/no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/tfo/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
socks5 = tag equals "socks5" address (username password)? (usernamek passwordk)? (udp_relay/no_error_alert/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/fast_open/tfo/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||||
proxy.type = "socks5";
|
proxy.type = "socks5";
|
||||||
handleShadowTLS();
|
handleShadowTLS();
|
||||||
}
|
}
|
||||||
@@ -121,7 +121,6 @@ socks5_tls = tag equals "socks5-tls" address (username password)? (usernamek pas
|
|||||||
direct = tag equals "direct" (udp_relay/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/tfo/block_quic/others)* {
|
direct = tag equals "direct" (udp_relay/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/fast_open/tfo/block_quic/others)* {
|
||||||
proxy.type = "direct";
|
proxy.type = "direct";
|
||||||
}
|
}
|
||||||
|
|
||||||
address = comma server:server comma port:port {
|
address = comma server:server comma port:port {
|
||||||
proxy.server = server;
|
proxy.server = server;
|
||||||
proxy.port = port;
|
proxy.port = port;
|
||||||
@@ -179,8 +178,8 @@ username = & {
|
|||||||
peg$currPos = end;
|
peg$currPos = end;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} { proxy.username = $.username; }
|
} { proxy.username = $.username.trim().replace(/^"(.*?)"$/, '$1').replace(/^'(.*?)'$/, '$1'); }
|
||||||
password = comma match:[^,]+ { proxy.password = match.join(""); }
|
password = comma match:[^,]+ { proxy.password = match.join("").replace(/^"(.*)"$/, '$1').replace(/^'(.*?)'$/, '$1'); }
|
||||||
|
|
||||||
tls = comma "tls" equals flag:bool { proxy.tls = flag; }
|
tls = comma "tls" equals flag:bool { proxy.tls = flag; }
|
||||||
sni = comma "sni" equals sni:("off"/domain) {
|
sni = comma "sni" equals sni:("off"/domain) {
|
||||||
@@ -196,7 +195,7 @@ tls_fingerprint = comma "server-cert-fingerprint-sha256" equals tls_fingerprint:
|
|||||||
snell_psk = comma "psk" equals match:[^,]+ { proxy.psk = match.join(""); }
|
snell_psk = comma "psk" equals match:[^,]+ { proxy.psk = match.join(""); }
|
||||||
snell_version = comma "version" equals match:$[0-9]+ { proxy.version = parseInt(match.trim()); }
|
snell_version = comma "version" equals match:$[0-9]+ { proxy.version = parseInt(match.trim()); }
|
||||||
|
|
||||||
usernamek = comma "username" equals match:[^,]+ { proxy.username = match.join(""); }
|
usernamek = comma "username" equals match:[^,]+ { proxy.username = match.join("").replace(/^"(.*?)"$/, '$1').replace(/^'(.*?)'$/, '$1'); }
|
||||||
passwordk = comma "password" equals match:[^,]+ { proxy.password = match.join("").replace(/^"(.*?)"$/, '$1').replace(/^'(.*?)'$/, '$1'); }
|
passwordk = comma "password" equals match:[^,]+ { proxy.password = match.join("").replace(/^"(.*?)"$/, '$1').replace(/^'(.*?)'$/, '$1'); }
|
||||||
vmess_uuid = comma "username" equals match:[^,]+ { proxy.uuid = match.join(""); }
|
vmess_uuid = comma "username" equals match:[^,]+ { proxy.uuid = match.join(""); }
|
||||||
vmess_aead = comma "vmess-aead" equals flag:bool { proxy.aead = flag; }
|
vmess_aead = comma "vmess-aead" equals flag:bool { proxy.aead = flag; }
|
||||||
@@ -212,11 +211,11 @@ ws_headers = comma "ws-headers" equals headers:$[^,]+ {
|
|||||||
const result = {};
|
const result = {};
|
||||||
pairs.forEach(pair => {
|
pairs.forEach(pair => {
|
||||||
const [key, value] = pair.trim().split(":");
|
const [key, value] = pair.trim().split(":");
|
||||||
result[key.trim()] = value.trim();
|
result[key.trim()] = value.trim().replace(/^"(.*?)"$/, '$1').replace(/^'(.*?)'$/, '$1');
|
||||||
})
|
})
|
||||||
obfs["ws-headers"] = result;
|
obfs["ws-headers"] = result;
|
||||||
}
|
}
|
||||||
ws_path = comma "ws-path" equals path:uri { obfs.path = path; }
|
ws_path = comma "ws-path" equals path:uri { obfs.path = path.trim().replace(/^"(.*?)"$/, '$1').replace(/^'(.*?)'$/, '$1'); }
|
||||||
|
|
||||||
obfs = comma "obfs" equals type:("http"/"tls") { obfs.type = type; }
|
obfs = comma "obfs" equals type:("http"/"tls") { obfs.type = type; }
|
||||||
obfs_host = comma "obfs-host" equals host:domain { obfs.host = host; };
|
obfs_host = comma "obfs-host" equals host:domain { obfs.host = host; };
|
||||||
|
|||||||
@@ -176,8 +176,8 @@ username = & {
|
|||||||
peg$currPos = end;
|
peg$currPos = end;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} { proxy.username = $.username; }
|
} { proxy.username = $.username.trim().replace(/^"(.*?)"$/, '$1').replace(/^'(.*?)'$/, '$1'); }
|
||||||
password = comma match:[^,]+ { proxy.password = match.join(""); }
|
password = comma match:[^,]+ { proxy.password = match.join("").replace(/^"(.*)"$/, '$1').replace(/^'(.*?)'$/, '$1'); }
|
||||||
|
|
||||||
tls = comma "tls" equals flag:bool { proxy.tls = flag; }
|
tls = comma "tls" equals flag:bool { proxy.tls = flag; }
|
||||||
sni = comma "sni" equals sni:("off"/domain) {
|
sni = comma "sni" equals sni:("off"/domain) {
|
||||||
@@ -193,7 +193,7 @@ tls_fingerprint = comma "server-cert-fingerprint-sha256" equals tls_fingerprint:
|
|||||||
snell_psk = comma "psk" equals match:[^,]+ { proxy.psk = match.join(""); }
|
snell_psk = comma "psk" equals match:[^,]+ { proxy.psk = match.join(""); }
|
||||||
snell_version = comma "version" equals match:$[0-9]+ { proxy.version = parseInt(match.trim()); }
|
snell_version = comma "version" equals match:$[0-9]+ { proxy.version = parseInt(match.trim()); }
|
||||||
|
|
||||||
usernamek = comma "username" equals match:[^,]+ { proxy.username = match.join(""); }
|
usernamek = comma "username" equals match:[^,]+ { proxy.username = match.join("").replace(/^"(.*?)"$/, '$1').replace(/^'(.*?)'$/, '$1'); }
|
||||||
passwordk = comma "password" equals match:[^,]+ { proxy.password = match.join("").replace(/^"(.*?)"$/, '$1').replace(/^'(.*?)'$/, '$1'); }
|
passwordk = comma "password" equals match:[^,]+ { proxy.password = match.join("").replace(/^"(.*?)"$/, '$1').replace(/^'(.*?)'$/, '$1'); }
|
||||||
vmess_uuid = comma "username" equals match:[^,]+ { proxy.uuid = match.join(""); }
|
vmess_uuid = comma "username" equals match:[^,]+ { proxy.uuid = match.join(""); }
|
||||||
vmess_aead = comma "vmess-aead" equals flag:bool { proxy.aead = flag; }
|
vmess_aead = comma "vmess-aead" equals flag:bool { proxy.aead = flag; }
|
||||||
@@ -209,11 +209,11 @@ ws_headers = comma "ws-headers" equals headers:$[^,]+ {
|
|||||||
const result = {};
|
const result = {};
|
||||||
pairs.forEach(pair => {
|
pairs.forEach(pair => {
|
||||||
const [key, value] = pair.trim().split(":");
|
const [key, value] = pair.trim().split(":");
|
||||||
result[key.trim()] = value.trim();
|
result[key.trim()] = value.trim().replace(/^"(.*?)"$/, '$1').replace(/^'(.*?)'$/, '$1');
|
||||||
})
|
})
|
||||||
obfs["ws-headers"] = result;
|
obfs["ws-headers"] = result;
|
||||||
}
|
}
|
||||||
ws_path = comma "ws-path" equals path:uri { obfs.path = path; }
|
ws_path = comma "ws-path" equals path:uri { obfs.path = path.trim().replace(/^"(.*?)"$/, '$1').replace(/^'(.*?)'$/, '$1'); }
|
||||||
|
|
||||||
obfs = comma "obfs" equals type:("http"/"tls") { obfs.type = type; }
|
obfs = comma "obfs" equals type:("http"/"tls") { obfs.type = type; }
|
||||||
obfs_host = comma "obfs-host" equals host:domain { obfs.host = host; };
|
obfs_host = comma "obfs-host" equals host:domain { obfs.host = host; };
|
||||||
|
|||||||
@@ -623,9 +623,11 @@ const DOMAIN_RESOLVERS = {
|
|||||||
const cached = resourceCache.get(id);
|
const cached = resourceCache.get(id);
|
||||||
if (!noCache && cached) return cached;
|
if (!noCache && cached) return cached;
|
||||||
const resp = await $.http.get({
|
const resp = await $.http.get({
|
||||||
url: `http://223.6.6.6/resolve?edns_client_subnet=${edns}/24&name=${encodeURIComponent(
|
url: `http://223.6.6.6/resolve?edns_client_subnet=${edns}/${
|
||||||
domain,
|
isIPv4(edns) ? 24 : 56
|
||||||
)}&type=${type === 'IPv6' ? 'AAAA' : 'A'}&short=1`,
|
}&name=${encodeURIComponent(domain)}&type=${
|
||||||
|
type === 'IPv6' ? 'AAAA' : 'A'
|
||||||
|
}&short=1`,
|
||||||
headers: {
|
headers: {
|
||||||
accept: 'application/dns-json',
|
accept: 'application/dns-json',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default function Clash_Producer() {
|
|||||||
'chacha20-ietf-poly1305',
|
'chacha20-ietf-poly1305',
|
||||||
'xchacha20-ietf-poly1305',
|
'xchacha20-ietf-poly1305',
|
||||||
].includes(proxy.cipher)) ||
|
].includes(proxy.cipher)) ||
|
||||||
(proxy.type === 'snell' && String(proxy.version) === '4') ||
|
(proxy.type === 'snell' && proxy.version >= 4) ||
|
||||||
(proxy.type === 'vless' &&
|
(proxy.type === 'vless' &&
|
||||||
(typeof proxy.flow !== 'undefined' ||
|
(typeof proxy.flow !== 'undefined' ||
|
||||||
proxy['reality-opts']))
|
proxy['reality-opts']))
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
import { isPresent } from '@/core/proxy-utils/producers/utils';
|
import { isPresent } from '@/core/proxy-utils/producers/utils';
|
||||||
|
|
||||||
|
const ipVersions = {
|
||||||
|
dual: 'dual',
|
||||||
|
'v4-only': 'ipv4',
|
||||||
|
'v6-only': 'ipv6',
|
||||||
|
'prefer-v4': 'ipv4-prefer',
|
||||||
|
'prefer-v6': 'ipv6-prefer',
|
||||||
|
};
|
||||||
|
|
||||||
export default function ClashMeta_Producer() {
|
export default function ClashMeta_Producer() {
|
||||||
const type = 'ALL';
|
const type = 'ALL';
|
||||||
const produce = (proxies, type, opts = {}) => {
|
const produce = (proxies, type, opts = {}) => {
|
||||||
const list = proxies
|
const list = proxies
|
||||||
.filter((proxy) => {
|
.filter((proxy) => {
|
||||||
if (opts['include-unsupported-proxy']) return true;
|
if (opts['include-unsupported-proxy']) return true;
|
||||||
if (proxy.type === 'snell' && String(proxy.version) === '4') {
|
if (proxy.type === 'snell' && proxy.version >= 4) {
|
||||||
return false;
|
return false;
|
||||||
} else if (['juicity'].includes(proxy.type)) {
|
} else if (['juicity'].includes(proxy.type)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -242,6 +250,11 @@ export default function ClashMeta_Producer() {
|
|||||||
delete proxy[`${proxy.network}-opts`]['_grpc-type'];
|
delete proxy[`${proxy.network}-opts`]['_grpc-type'];
|
||||||
delete proxy[`${proxy.network}-opts`]['_grpc-authority'];
|
delete proxy[`${proxy.network}-opts`]['_grpc-authority'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (proxy['ip-version']) {
|
||||||
|
proxy['ip-version'] =
|
||||||
|
ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||||
|
}
|
||||||
return proxy;
|
return proxy;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -377,6 +377,23 @@ export default function Egern_Producer() {
|
|||||||
delete proxy.id;
|
delete proxy.id;
|
||||||
delete proxy.resolved;
|
delete proxy.resolved;
|
||||||
delete proxy['no-resolve'];
|
delete proxy['no-resolve'];
|
||||||
|
|
||||||
|
if (proxy.transport) {
|
||||||
|
for (const key in proxy.transport) {
|
||||||
|
if (
|
||||||
|
Object.keys(proxy.transport[key]).length === 0 ||
|
||||||
|
Object.values(proxy.transport[key]).every(
|
||||||
|
(v) => v == null,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
delete proxy.transport[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(proxy.transport).length === 0) {
|
||||||
|
delete proxy.transport;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (type !== 'internal') {
|
if (type !== 'internal') {
|
||||||
for (const key in proxy) {
|
for (const key in proxy) {
|
||||||
if (proxy[key] == null || /^_/i.test(key)) {
|
if (proxy[key] == null || /^_/i.test(key)) {
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ export default function Shadowrocket_Producer() {
|
|||||||
const list = proxies
|
const list = proxies
|
||||||
.filter((proxy) => {
|
.filter((proxy) => {
|
||||||
if (opts['include-unsupported-proxy']) return true;
|
if (opts['include-unsupported-proxy']) return true;
|
||||||
if (proxy.type === 'snell' && String(proxy.version) === '4') {
|
if (proxy.type === 'snell' && proxy.version >= 4) {
|
||||||
return false;
|
return false;
|
||||||
} else if (['mieru', 'anytls'].includes(proxy.type)) {
|
} else if (['mieru'].includes(proxy.type)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -2,6 +2,26 @@ import ClashMeta_Producer from './clashmeta';
|
|||||||
import $ from '@/core/app';
|
import $ from '@/core/app';
|
||||||
import { isIPv4, isIPv6 } from '@/utils';
|
import { isIPv4, isIPv6 } from '@/utils';
|
||||||
|
|
||||||
|
const ipVersions = {
|
||||||
|
ipv4: 'ipv4_only',
|
||||||
|
ipv6: 'ipv6_only',
|
||||||
|
'v4-only': 'ipv4_only',
|
||||||
|
'v6-only': 'ipv6_only',
|
||||||
|
'ipv4-prefer': 'prefer_ipv4',
|
||||||
|
'ipv6-prefer': 'prefer_ipv6',
|
||||||
|
'prefer-v4': 'prefer_ipv4',
|
||||||
|
'prefer-v6': 'prefer_ipv6',
|
||||||
|
};
|
||||||
|
|
||||||
|
const ipVersionParser = (proxy, parsedProxy) => {
|
||||||
|
const strategy = ipVersions[proxy['ip-version']];
|
||||||
|
if (proxy._dns_server && strategy) {
|
||||||
|
parsedProxy.domain_resolver = {
|
||||||
|
server: proxy._dns_server,
|
||||||
|
strategy,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
const detourParser = (proxy, parsedProxy) => {
|
const detourParser = (proxy, parsedProxy) => {
|
||||||
parsedProxy.detour = proxy['dialer-proxy'] || proxy.detour;
|
parsedProxy.detour = proxy['dialer-proxy'] || proxy.detour;
|
||||||
};
|
};
|
||||||
@@ -278,6 +298,7 @@ const sshParser = (proxy = {}) => {
|
|||||||
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
||||||
tfoParser(proxy, parsedProxy);
|
tfoParser(proxy, parsedProxy);
|
||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
|
ipVersionParser(proxy, parsedProxy);
|
||||||
return parsedProxy;
|
return parsedProxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -305,6 +326,7 @@ const httpParser = (proxy = {}) => {
|
|||||||
tfoParser(proxy, parsedProxy);
|
tfoParser(proxy, parsedProxy);
|
||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
tlsParser(proxy, parsedProxy);
|
tlsParser(proxy, parsedProxy);
|
||||||
|
ipVersionParser(proxy, parsedProxy);
|
||||||
return parsedProxy;
|
return parsedProxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -327,6 +349,7 @@ const socks5Parser = (proxy = {}) => {
|
|||||||
networkParser(proxy, parsedProxy);
|
networkParser(proxy, parsedProxy);
|
||||||
tfoParser(proxy, parsedProxy);
|
tfoParser(proxy, parsedProxy);
|
||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
|
ipVersionParser(proxy, parsedProxy);
|
||||||
return parsedProxy;
|
return parsedProxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -338,6 +361,17 @@ const shadowTLSParser = (proxy = {}) => {
|
|||||||
password: proxy.password,
|
password: proxy.password,
|
||||||
detour: `${proxy.name}_shadowtls`,
|
detour: `${proxy.name}_shadowtls`,
|
||||||
};
|
};
|
||||||
|
if (proxy.uot) ssPart.udp_over_tcp = true;
|
||||||
|
if (proxy['udp-over-tcp']) {
|
||||||
|
ssPart.udp_over_tcp = {
|
||||||
|
enabled: true,
|
||||||
|
version:
|
||||||
|
!proxy['udp-over-tcp-version'] ||
|
||||||
|
proxy['udp-over-tcp-version'] === 1
|
||||||
|
? 1
|
||||||
|
: 2,
|
||||||
|
};
|
||||||
|
}
|
||||||
const stPart = {
|
const stPart = {
|
||||||
tag: `${proxy.name}_shadowtls`,
|
tag: `${proxy.name}_shadowtls`,
|
||||||
type: 'shadowtls',
|
type: 'shadowtls',
|
||||||
@@ -360,6 +394,7 @@ const shadowTLSParser = (proxy = {}) => {
|
|||||||
tfoParser(proxy, stPart);
|
tfoParser(proxy, stPart);
|
||||||
detourParser(proxy, stPart);
|
detourParser(proxy, stPart);
|
||||||
smuxParser(proxy.smux, ssPart);
|
smuxParser(proxy.smux, ssPart);
|
||||||
|
ipVersionParser(proxy, stPart);
|
||||||
return { type: 'ss-with-st', ssPart, stPart };
|
return { type: 'ss-with-st', ssPart, stPart };
|
||||||
};
|
};
|
||||||
const ssParser = (proxy = {}) => {
|
const ssParser = (proxy = {}) => {
|
||||||
@@ -389,6 +424,7 @@ const ssParser = (proxy = {}) => {
|
|||||||
tfoParser(proxy, parsedProxy);
|
tfoParser(proxy, parsedProxy);
|
||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
smuxParser(proxy.smux, parsedProxy);
|
smuxParser(proxy.smux, parsedProxy);
|
||||||
|
ipVersionParser(proxy, parsedProxy);
|
||||||
if (proxy.plugin) {
|
if (proxy.plugin) {
|
||||||
const optArr = [];
|
const optArr = [];
|
||||||
if (proxy.plugin === 'obfs') {
|
if (proxy.plugin === 'obfs') {
|
||||||
@@ -467,6 +503,7 @@ const ssrParser = (proxy = {}) => {
|
|||||||
tfoParser(proxy, parsedProxy);
|
tfoParser(proxy, parsedProxy);
|
||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
smuxParser(proxy.smux, parsedProxy);
|
smuxParser(proxy.smux, parsedProxy);
|
||||||
|
ipVersionParser(proxy, parsedProxy);
|
||||||
return parsedProxy;
|
return parsedProxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -505,6 +542,7 @@ const vmessParser = (proxy = {}) => {
|
|||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
tlsParser(proxy, parsedProxy);
|
tlsParser(proxy, parsedProxy);
|
||||||
smuxParser(proxy.smux, parsedProxy);
|
smuxParser(proxy.smux, parsedProxy);
|
||||||
|
ipVersionParser(proxy, parsedProxy);
|
||||||
return parsedProxy;
|
return parsedProxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -519,8 +557,10 @@ const vlessParser = (proxy = {}) => {
|
|||||||
};
|
};
|
||||||
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
|
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
|
||||||
throw 'invalid port';
|
throw 'invalid port';
|
||||||
|
if (proxy.xudp) parsedProxy.packet_encoding = 'xudp';
|
||||||
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
||||||
if (proxy.flow === 'xtls-rprx-vision') parsedProxy.flow = proxy.flow;
|
// if (['xtls-rprx-vision', ''].includes(proxy.flow)) parsedProxy.flow = proxy.flow;
|
||||||
|
if (proxy.flow != null) parsedProxy.flow = proxy.flow;
|
||||||
if (proxy.network === 'ws') wsParser(proxy, parsedProxy);
|
if (proxy.network === 'ws') wsParser(proxy, parsedProxy);
|
||||||
if (proxy.network === 'grpc') grpcParser(proxy, parsedProxy);
|
if (proxy.network === 'grpc') grpcParser(proxy, parsedProxy);
|
||||||
networkParser(proxy, parsedProxy);
|
networkParser(proxy, parsedProxy);
|
||||||
@@ -528,6 +568,7 @@ const vlessParser = (proxy = {}) => {
|
|||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
smuxParser(proxy.smux, parsedProxy);
|
smuxParser(proxy.smux, parsedProxy);
|
||||||
tlsParser(proxy, parsedProxy);
|
tlsParser(proxy, parsedProxy);
|
||||||
|
ipVersionParser(proxy, parsedProxy);
|
||||||
return parsedProxy;
|
return parsedProxy;
|
||||||
};
|
};
|
||||||
const trojanParser = (proxy = {}) => {
|
const trojanParser = (proxy = {}) => {
|
||||||
@@ -549,6 +590,7 @@ const trojanParser = (proxy = {}) => {
|
|||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
tlsParser(proxy, parsedProxy);
|
tlsParser(proxy, parsedProxy);
|
||||||
smuxParser(proxy.smux, parsedProxy);
|
smuxParser(proxy.smux, parsedProxy);
|
||||||
|
ipVersionParser(proxy, parsedProxy);
|
||||||
return parsedProxy;
|
return parsedProxy;
|
||||||
};
|
};
|
||||||
const hysteriaParser = (proxy = {}) => {
|
const hysteriaParser = (proxy = {}) => {
|
||||||
@@ -598,6 +640,7 @@ const hysteriaParser = (proxy = {}) => {
|
|||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
tfoParser(proxy, parsedProxy);
|
tfoParser(proxy, parsedProxy);
|
||||||
smuxParser(proxy.smux, parsedProxy);
|
smuxParser(proxy.smux, parsedProxy);
|
||||||
|
ipVersionParser(proxy, parsedProxy);
|
||||||
return parsedProxy;
|
return parsedProxy;
|
||||||
};
|
};
|
||||||
const hysteria2Parser = (proxy = {}) => {
|
const hysteria2Parser = (proxy = {}) => {
|
||||||
@@ -631,6 +674,7 @@ const hysteria2Parser = (proxy = {}) => {
|
|||||||
tfoParser(proxy, parsedProxy);
|
tfoParser(proxy, parsedProxy);
|
||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
smuxParser(proxy.smux, parsedProxy);
|
smuxParser(proxy.smux, parsedProxy);
|
||||||
|
ipVersionParser(proxy, parsedProxy);
|
||||||
return parsedProxy;
|
return parsedProxy;
|
||||||
};
|
};
|
||||||
const tuic5Parser = (proxy = {}) => {
|
const tuic5Parser = (proxy = {}) => {
|
||||||
@@ -662,6 +706,7 @@ const tuic5Parser = (proxy = {}) => {
|
|||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
tlsParser(proxy, parsedProxy);
|
tlsParser(proxy, parsedProxy);
|
||||||
smuxParser(proxy.smux, parsedProxy);
|
smuxParser(proxy.smux, parsedProxy);
|
||||||
|
ipVersionParser(proxy, parsedProxy);
|
||||||
return parsedProxy;
|
return parsedProxy;
|
||||||
};
|
};
|
||||||
const anytlsParser = (proxy = {}) => {
|
const anytlsParser = (proxy = {}) => {
|
||||||
@@ -677,8 +722,14 @@ const anytlsParser = (proxy = {}) => {
|
|||||||
parsedProxy.idle_session_check_interval = `${proxy['idle-session-check-interval']}s`;
|
parsedProxy.idle_session_check_interval = `${proxy['idle-session-check-interval']}s`;
|
||||||
if (/^\d+$/.test(proxy['idle-session-timeout']))
|
if (/^\d+$/.test(proxy['idle-session-timeout']))
|
||||||
parsedProxy.idle_session_timeout = `${proxy['idle-session-timeout']}s`;
|
parsedProxy.idle_session_timeout = `${proxy['idle-session-timeout']}s`;
|
||||||
|
if (/^\d+$/.test(proxy['min-idle-session']))
|
||||||
|
parsedProxy.min_idle_session = parseInt(
|
||||||
|
`${proxy['min-idle-session']}`,
|
||||||
|
10,
|
||||||
|
);
|
||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
tlsParser(proxy, parsedProxy);
|
tlsParser(proxy, parsedProxy);
|
||||||
|
ipVersionParser(proxy, parsedProxy);
|
||||||
return parsedProxy;
|
return parsedProxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -736,6 +787,7 @@ const wireguardParser = (proxy = {}) => {
|
|||||||
tfoParser(proxy, parsedProxy);
|
tfoParser(proxy, parsedProxy);
|
||||||
detourParser(proxy, parsedProxy);
|
detourParser(proxy, parsedProxy);
|
||||||
smuxParser(proxy.smux, parsedProxy);
|
smuxParser(proxy.smux, parsedProxy);
|
||||||
|
ipVersionParser(proxy, parsedProxy);
|
||||||
return parsedProxy;
|
return parsedProxy;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -43,12 +43,10 @@ export default function Stash_Producer() {
|
|||||||
'2022-blake3-aes-128-gcm',
|
'2022-blake3-aes-128-gcm',
|
||||||
'2022-blake3-aes-256-gcm',
|
'2022-blake3-aes-256-gcm',
|
||||||
].includes(proxy.cipher)) ||
|
].includes(proxy.cipher)) ||
|
||||||
(proxy.type === 'snell' && String(proxy.version) === '4') ||
|
(proxy.type === 'snell' && proxy.version >= 4) ||
|
||||||
(opts['include-unsupported-proxy']
|
(proxy.type === 'vless' &&
|
||||||
? proxy.type === 'vless' &&
|
proxy['reality-opts'] &&
|
||||||
proxy['reality-opts'] &&
|
!['xtls-rprx-vision'].includes(proxy.flow))
|
||||||
!['xtls-rprx-vision'].includes(proxy.flow)
|
|
||||||
: proxy.type === 'vless' && proxy['reality-opts'])
|
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
} else if (proxy['underlying-proxy'] || proxy['dialer-proxy']) {
|
} else if (proxy['underlying-proxy'] || proxy['dialer-proxy']) {
|
||||||
|
|||||||
@@ -370,9 +370,9 @@ function vmess(proxy, includeUnsupportedProxy) {
|
|||||||
function ssh(proxy) {
|
function ssh(proxy) {
|
||||||
const result = new Result(proxy);
|
const result = new Result(proxy);
|
||||||
result.append(`${proxy.name}=ssh,${proxy.server},${proxy.port}`);
|
result.append(`${proxy.name}=ssh,${proxy.server},${proxy.port}`);
|
||||||
result.appendIfPresent(`,${proxy.username}`, 'username');
|
result.appendIfPresent(`,username="${proxy.username}"`, 'username');
|
||||||
// 所有的类似的字段都有双引号的问题 暂不处理
|
// 所有的类似的字段都有双引号的问题 暂不处理
|
||||||
result.appendIfPresent(`,"${proxy.password}"`, 'password');
|
result.appendIfPresent(`,password="${proxy.password}"`, 'password');
|
||||||
|
|
||||||
// https://manual.nssurge.com/policy/ssh.html
|
// https://manual.nssurge.com/policy/ssh.html
|
||||||
// 需配合 Keystore
|
// 需配合 Keystore
|
||||||
@@ -439,8 +439,8 @@ function http(proxy) {
|
|||||||
const result = new Result(proxy);
|
const result = new Result(proxy);
|
||||||
const type = proxy.tls ? 'https' : 'http';
|
const type = proxy.tls ? 'https' : 'http';
|
||||||
result.append(`${proxy.name}=${type},${proxy.server},${proxy.port}`);
|
result.append(`${proxy.name}=${type},${proxy.server},${proxy.port}`);
|
||||||
result.appendIfPresent(`,${proxy.username}`, 'username');
|
result.appendIfPresent(`,username="${proxy.username}"`, 'username');
|
||||||
result.appendIfPresent(`,"${proxy.password}"`, 'password');
|
result.appendIfPresent(`,password="${proxy.password}"`, 'password');
|
||||||
|
|
||||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||||
@@ -565,8 +565,8 @@ function socks5(proxy) {
|
|||||||
const result = new Result(proxy);
|
const result = new Result(proxy);
|
||||||
const type = proxy.tls ? 'socks5-tls' : 'socks5';
|
const type = proxy.tls ? 'socks5-tls' : 'socks5';
|
||||||
result.append(`${proxy.name}=${type},${proxy.server},${proxy.port}`);
|
result.append(`${proxy.name}=${type},${proxy.server},${proxy.port}`);
|
||||||
result.appendIfPresent(`,${proxy.username}`, 'username');
|
result.appendIfPresent(`,username="${proxy.username}"`, 'username');
|
||||||
result.appendIfPresent(`,"${proxy.password}"`, 'password');
|
result.appendIfPresent(`,password="${proxy.password}"`, 'password');
|
||||||
|
|
||||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||||
|
|||||||
@@ -119,12 +119,17 @@ export default function URI_Producer() {
|
|||||||
v: '2',
|
v: '2',
|
||||||
ps: proxy.name,
|
ps: proxy.name,
|
||||||
add: proxy.server,
|
add: proxy.server,
|
||||||
port: proxy.port,
|
port: `${proxy.port}`,
|
||||||
id: proxy.uuid,
|
id: proxy.uuid,
|
||||||
type,
|
aid: `${proxy.alterId || 0}`,
|
||||||
aid: proxy.alterId || 0,
|
scy: proxy.cipher,
|
||||||
net,
|
net,
|
||||||
|
type,
|
||||||
tls: proxy.tls ? 'tls' : '',
|
tls: proxy.tls ? 'tls' : '',
|
||||||
|
alpn: Array.isArray(proxy.alpn)
|
||||||
|
? proxy.alpn.join(',')
|
||||||
|
: proxy.alpn,
|
||||||
|
fp: proxy['client-fingerprint'],
|
||||||
};
|
};
|
||||||
if (proxy.tls && proxy.sni) {
|
if (proxy.tls && proxy.sni) {
|
||||||
result.sni = proxy.sni;
|
result.sni = proxy.sni;
|
||||||
@@ -135,16 +140,7 @@ export default function URI_Producer() {
|
|||||||
proxy[`${proxy.network}-opts`]?.path;
|
proxy[`${proxy.network}-opts`]?.path;
|
||||||
let vmessTransportHost =
|
let vmessTransportHost =
|
||||||
proxy[`${proxy.network}-opts`]?.headers?.Host;
|
proxy[`${proxy.network}-opts`]?.headers?.Host;
|
||||||
if (vmessTransportPath) {
|
|
||||||
result.path = Array.isArray(vmessTransportPath)
|
|
||||||
? vmessTransportPath[0]
|
|
||||||
: vmessTransportPath;
|
|
||||||
}
|
|
||||||
if (vmessTransportHost) {
|
|
||||||
result.host = Array.isArray(vmessTransportHost)
|
|
||||||
? vmessTransportHost[0]
|
|
||||||
: vmessTransportHost;
|
|
||||||
}
|
|
||||||
if (['grpc'].includes(proxy.network)) {
|
if (['grpc'].includes(proxy.network)) {
|
||||||
result.path =
|
result.path =
|
||||||
proxy[`${proxy.network}-opts`]?.[
|
proxy[`${proxy.network}-opts`]?.[
|
||||||
@@ -156,6 +152,31 @@ export default function URI_Producer() {
|
|||||||
'gun';
|
'gun';
|
||||||
result.host =
|
result.host =
|
||||||
proxy[`${proxy.network}-opts`]?.['_grpc-authority'];
|
proxy[`${proxy.network}-opts`]?.['_grpc-authority'];
|
||||||
|
} else if (['kcp', 'quic'].includes(proxy.network)) {
|
||||||
|
// https://github.com/XTLS/Xray-core/issues/91
|
||||||
|
result.type =
|
||||||
|
proxy[`${proxy.network}-opts`]?.[
|
||||||
|
`_${proxy.network}-type`
|
||||||
|
] || 'none';
|
||||||
|
result.host =
|
||||||
|
proxy[`${proxy.network}-opts`]?.[
|
||||||
|
`_${proxy.network}-host`
|
||||||
|
];
|
||||||
|
result.path =
|
||||||
|
proxy[`${proxy.network}-opts`]?.[
|
||||||
|
`_${proxy.network}-path`
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
if (vmessTransportPath) {
|
||||||
|
result.path = Array.isArray(vmessTransportPath)
|
||||||
|
? vmessTransportPath[0]
|
||||||
|
: vmessTransportPath;
|
||||||
|
}
|
||||||
|
if (vmessTransportHost) {
|
||||||
|
result.host = Array.isArray(vmessTransportHost)
|
||||||
|
? vmessTransportHost[0]
|
||||||
|
: vmessTransportHost;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = 'vmess://' + Base64.encode(JSON.stringify(result));
|
result = 'vmess://' + Base64.encode(JSON.stringify(result));
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ async function downloadSubscription(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$arguments.noFlow) {
|
if (!$arguments.noFlow && /^https?/.test(url)) {
|
||||||
// forward flow headers
|
// forward flow headers
|
||||||
flowInfo = await getFlowHeaders(
|
flowInfo = await getFlowHeaders(
|
||||||
$arguments?.insecure ? `${url}#insecure` : url,
|
$arguments?.insecure ? `${url}#insecure` : url,
|
||||||
@@ -506,7 +506,7 @@ async function downloadCollection(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!$arguments.noFlow) {
|
if (!$arguments.noFlow && /^https?:/.test(url)) {
|
||||||
subUserInfoOfSub = await getFlowHeaders(
|
subUserInfoOfSub = await getFlowHeaders(
|
||||||
$arguments?.insecure ? `${url}#insecure` : url,
|
$arguments?.insecure ? `${url}#insecure` : url,
|
||||||
$arguments.flowUserAgent,
|
$arguments.flowUserAgent,
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ async function getFile(req, res) {
|
|||||||
proxy,
|
proxy,
|
||||||
noCache,
|
noCache,
|
||||||
produceType,
|
produceType,
|
||||||
|
all: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -184,9 +185,15 @@ async function getFile(req, res) {
|
|||||||
)}`,
|
)}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
res.set('Content-Type', 'text/plain; charset=utf-8').send(
|
res.set('Content-Type', 'text/plain; charset=utf-8');
|
||||||
output ?? '',
|
if (output?.$options?._res?.headers) {
|
||||||
);
|
Object.entries(output.$options._res.headers).forEach(
|
||||||
|
([key, value]) => {
|
||||||
|
res.set(key, value);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
res.send(output?.$content ?? '');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
$.notify(
|
$.notify(
|
||||||
`🌍 Sub-Store 下载文件失败`,
|
`🌍 Sub-Store 下载文件失败`,
|
||||||
|
|||||||
@@ -119,10 +119,11 @@ export default function serve() {
|
|||||||
$app.start();
|
$app.start();
|
||||||
|
|
||||||
if ($.env.isNode) {
|
if ($.env.isNode) {
|
||||||
// Deprecated: SUB_STORE_BACKEND_CRON
|
// Deprecated: SUB_STORE_BACKEND_CRON, SUB_STORE_CRON
|
||||||
const backend_sync_cron =
|
const backend_sync_cron = eval(
|
||||||
eval('process.env.SUB_STORE_BACKEND_SYNC_CRON') ||
|
'process.env.SUB_STORE_BACKEND_SYNC_CRON',
|
||||||
eval('process.env.SUB_STORE_BACKEND_CRON');
|
);
|
||||||
|
|
||||||
if (backend_sync_cron) {
|
if (backend_sync_cron) {
|
||||||
$.info(`[SYNC CRON] ${backend_sync_cron} enabled`);
|
$.info(`[SYNC CRON] ${backend_sync_cron} enabled`);
|
||||||
const { CronJob } = eval(`require("cron")`);
|
const { CronJob } = eval(`require("cron")`);
|
||||||
@@ -145,6 +146,17 @@ export default function serve() {
|
|||||||
true, // start
|
true, // start
|
||||||
// 'Asia/Shanghai' // timeZone
|
// 'Asia/Shanghai' // timeZone
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
if (eval('process.env.SUB_STORE_BACKEND_CRON')) {
|
||||||
|
$.error(
|
||||||
|
`[SYNC CRON] SUB_STORE_BACKEND_CRON 已弃用, 请使用 SUB_STORE_BACKEND_SYNC_CRON`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (eval('process.env.SUB_STORE_CRON')) {
|
||||||
|
$.error(
|
||||||
|
`[SYNC CRON] SUB_STORE_CRON 已弃用, 请使用 SUB_STORE_BACKEND_SYNC_CRON`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// 格式: 0 */2 * * *,sub,a;0 */3 * * *,col,b
|
// 格式: 0 */2 * * *,sub,a;0 */3 * * *,col,b
|
||||||
// 每 2 小时处理一次单条订阅 a, 每 3 小时处理一次组合订阅 b
|
// 每 2 小时处理一次单条订阅 a, 每 3 小时处理一次组合订阅 b
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
import $ from '@/core/app';
|
import $ from '@/core/app';
|
||||||
import { ENV } from '@/vendor/open-api';
|
import { ENV } from '@/vendor/open-api';
|
||||||
import { failed, success } from '@/restful/response';
|
import { failed, success } from '@/restful/response';
|
||||||
@@ -77,7 +78,19 @@ function getEnv(req, res) {
|
|||||||
if (req.query.share) {
|
if (req.query.share) {
|
||||||
env.feature.share = true;
|
env.feature.share = true;
|
||||||
}
|
}
|
||||||
success(res, env);
|
res.set('Content-Type', 'application/json;charset=UTF-8').send(
|
||||||
|
JSON.stringify(
|
||||||
|
{
|
||||||
|
status: 'success',
|
||||||
|
data: {
|
||||||
|
guide: '⚠️⚠️⚠️ 您当前看到的是后端的响应. 若想配合前端使用, 可访问官方前端 https://sub-store.vercel.app 后自行配置后端地址, 或一键配置后端 https://sub-store.vercel.app?api=https://a.com/xxx (假设 https://a.com 是你后端的域名, /xxx 是自定义路径). 需注意 HTTPS 前端无法请求非本地的 HTTP 后端(部分浏览器上也无法访问本地 HTTP 后端). 请配置反代或在局域网自建 HTTP 前端. 如果还有问题, 可查看此排查说明: https://t.me/zhetengsha/1068',
|
||||||
|
...env,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refresh(_, res) {
|
async function refresh(_, res) {
|
||||||
@@ -92,7 +105,7 @@ async function refresh(_, res) {
|
|||||||
success(res);
|
success(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function gistBackupAction(action) {
|
async function gistBackupAction(action, keep) {
|
||||||
// read token
|
// read token
|
||||||
const { gistToken, syncPlatform } = $.read(SETTINGS_KEY);
|
const { gistToken, syncPlatform } = $.read(SETTINGS_KEY);
|
||||||
if (!gistToken) throw new Error('GitHub Token is required for backup!');
|
if (!gistToken) throw new Error('GitHub Token is required for backup!');
|
||||||
@@ -102,6 +115,9 @@ async function gistBackupAction(action) {
|
|||||||
key: GIST_BACKUP_KEY,
|
key: GIST_BACKUP_KEY,
|
||||||
syncPlatform,
|
syncPlatform,
|
||||||
});
|
});
|
||||||
|
let currentContent = $.read('#sub-store');
|
||||||
|
currentContent = currentContent ? JSON.parse(currentContent) : {};
|
||||||
|
if ($.env.isNode) currentContent = JSON.parse(JSON.stringify($.cache));
|
||||||
let content;
|
let content;
|
||||||
const settings = $.read(SETTINGS_KEY);
|
const settings = $.read(SETTINGS_KEY);
|
||||||
const updated = settings.syncTime;
|
const updated = settings.syncTime;
|
||||||
@@ -109,7 +125,10 @@ async function gistBackupAction(action) {
|
|||||||
case 'upload':
|
case 'upload':
|
||||||
try {
|
try {
|
||||||
content = $.read('#sub-store');
|
content = $.read('#sub-store');
|
||||||
if ($.env.isNode) content = JSON.stringify($.cache, null, ` `);
|
content = content ? JSON.parse(content) : {};
|
||||||
|
if ($.env.isNode) content = JSON.parse(JSON.stringify($.cache));
|
||||||
|
content.settings.gistToken = '恢复后请重新设置 GitHub Token';
|
||||||
|
content = JSON.stringify(content, null, ` `);
|
||||||
$.info(`下载备份, 与本地内容对比...`);
|
$.info(`下载备份, 与本地内容对比...`);
|
||||||
const onlineContent = await gist.download(
|
const onlineContent = await gist.download(
|
||||||
GIST_BACKUP_FILE_NAME,
|
GIST_BACKUP_FILE_NAME,
|
||||||
@@ -126,7 +145,10 @@ async function gistBackupAction(action) {
|
|||||||
settings.syncTime = new Date().getTime();
|
settings.syncTime = new Date().getTime();
|
||||||
$.write(settings, SETTINGS_KEY);
|
$.write(settings, SETTINGS_KEY);
|
||||||
content = $.read('#sub-store');
|
content = $.read('#sub-store');
|
||||||
if ($.env.isNode) content = JSON.stringify($.cache, null, ` `);
|
content = content ? JSON.parse(content) : {};
|
||||||
|
if ($.env.isNode) content = JSON.parse(JSON.stringify($.cache));
|
||||||
|
content.settings.gistToken = '恢复后请重新设置 GitHub Token';
|
||||||
|
content = JSON.stringify(content, null, ` `);
|
||||||
$.info(`上传备份中...`);
|
$.info(`上传备份中...`);
|
||||||
try {
|
try {
|
||||||
await gist.upload({
|
await gist.upload({
|
||||||
@@ -144,7 +166,8 @@ async function gistBackupAction(action) {
|
|||||||
$.info(`还原备份中...`);
|
$.info(`还原备份中...`);
|
||||||
content = await gist.download(GIST_BACKUP_FILE_NAME);
|
content = await gist.download(GIST_BACKUP_FILE_NAME);
|
||||||
try {
|
try {
|
||||||
if (Object.keys(JSON.parse(content).settings).length === 0) {
|
content = JSON.parse(content);
|
||||||
|
if (Object.keys(content.settings).length === 0) {
|
||||||
throw new Error('备份文件应该至少包含 settings 字段');
|
throw new Error('备份文件应该至少包含 settings 字段');
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -155,10 +178,15 @@ async function gistBackupAction(action) {
|
|||||||
);
|
);
|
||||||
throw new Error('Gist 备份文件校验失败, 无法还原');
|
throw new Error('Gist 备份文件校验失败, 无法还原');
|
||||||
}
|
}
|
||||||
|
if (keep) {
|
||||||
|
$.info(`保留原有设置 ${keep}`);
|
||||||
|
keep.split(',').forEach((path) => {
|
||||||
|
_.set(content, path, _.get(currentContent, path));
|
||||||
|
});
|
||||||
|
}
|
||||||
// restore settings
|
// restore settings
|
||||||
$.write(content, '#sub-store');
|
$.write(JSON.stringify(content, null, ` `), '#sub-store');
|
||||||
if ($.env.isNode) {
|
if ($.env.isNode) {
|
||||||
content = JSON.parse(content);
|
|
||||||
$.cache = content;
|
$.cache = content;
|
||||||
$.persistCache();
|
$.persistCache();
|
||||||
}
|
}
|
||||||
@@ -170,7 +198,7 @@ async function gistBackupAction(action) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function gistBackup(req, res) {
|
async function gistBackup(req, res) {
|
||||||
const { action } = req.query;
|
const { action, keep } = req.query;
|
||||||
// read token
|
// read token
|
||||||
const { gistToken } = $.read(SETTINGS_KEY);
|
const { gistToken } = $.read(SETTINGS_KEY);
|
||||||
if (!gistToken) {
|
if (!gistToken) {
|
||||||
@@ -183,7 +211,7 @@ async function gistBackup(req, res) {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
await gistBackupAction(action);
|
await gistBackupAction(action, keep);
|
||||||
success(res);
|
success(res);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
$.error(
|
$.error(
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ async function getFlowInfo(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($arguments.noFlow) {
|
if ($arguments.noFlow || !/^https?/.test(url)) {
|
||||||
failed(
|
failed(
|
||||||
res,
|
res,
|
||||||
new RequestInvalidError(
|
new RequestInvalidError(
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ async function produceArtifact({
|
|||||||
$options,
|
$options,
|
||||||
proxy,
|
proxy,
|
||||||
noCache,
|
noCache,
|
||||||
|
all,
|
||||||
}) {
|
}) {
|
||||||
platform = platform || 'JSON';
|
platform = platform || 'JSON';
|
||||||
|
|
||||||
@@ -595,7 +596,7 @@ async function produceArtifact({
|
|||||||
)
|
)
|
||||||
: { $content: filesContent, $files: files, $options };
|
: { $content: filesContent, $files: files, $options };
|
||||||
|
|
||||||
return processed?.$content ?? '';
|
return (all ? processed : processed?.$content) ?? '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,16 @@ async function signToken(req, res) {
|
|||||||
try {
|
try {
|
||||||
const { payload, options } = req.body;
|
const { payload, options } = req.body;
|
||||||
const ms = eval(`require("ms")`);
|
const ms = eval(`require("ms")`);
|
||||||
|
const type = payload?.type;
|
||||||
|
const name = payload?.name;
|
||||||
|
if (!type || !name)
|
||||||
|
return failed(
|
||||||
|
res,
|
||||||
|
new RequestInvalidError(
|
||||||
|
'INVALID_PAYLOAD',
|
||||||
|
`payload type and name are required`,
|
||||||
|
),
|
||||||
|
);
|
||||||
let token = payload?.token;
|
let token = payload?.token;
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
if (typeof token !== 'string' || token.length < 1) {
|
if (typeof token !== 'string' || token.length < 1) {
|
||||||
@@ -65,7 +75,12 @@ async function signToken(req, res) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
const tokens = $.read(TOKENS_KEY) || [];
|
const tokens = $.read(TOKENS_KEY) || [];
|
||||||
if (tokens.find((t) => t.token === token)) {
|
if (
|
||||||
|
tokens.find(
|
||||||
|
(t) =>
|
||||||
|
t.token === token && t.type === type && t.name === name,
|
||||||
|
)
|
||||||
|
) {
|
||||||
return failed(
|
return failed(
|
||||||
res,
|
res,
|
||||||
new RequestInvalidError(
|
new RequestInvalidError(
|
||||||
@@ -75,16 +90,7 @@ async function signToken(req, res) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const type = payload?.type;
|
|
||||||
const name = payload?.name;
|
|
||||||
if (!type || !name)
|
|
||||||
return failed(
|
|
||||||
res,
|
|
||||||
new RequestInvalidError(
|
|
||||||
'INVALID_PAYLOAD',
|
|
||||||
`payload type and name are required`,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (type === 'col') {
|
if (type === 'col') {
|
||||||
const collections = $.read(COLLECTIONS_KEY) || [];
|
const collections = $.read(COLLECTIONS_KEY) || [];
|
||||||
const collection = collections.find((c) => c.name === name);
|
const collection = collections.find((c) => c.name === name);
|
||||||
@@ -153,7 +159,12 @@ async function signToken(req, res) {
|
|||||||
if (!token) {
|
if (!token) {
|
||||||
do {
|
do {
|
||||||
token = nanoid.customAlphabet(nanoid.urlAlphabet)();
|
token = nanoid.customAlphabet(nanoid.urlAlphabet)();
|
||||||
} while (tokens.find((t) => t.token === token));
|
} while (
|
||||||
|
tokens.find(
|
||||||
|
(t) =>
|
||||||
|
t.token === token && t.type === type && t.name === name,
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
tokens.push({
|
tokens.push({
|
||||||
...payload,
|
...payload,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { SETTINGS_KEY } from '@/constants';
|
import { SETTINGS_KEY, FILES_KEY, MODULES_KEY } from '@/constants';
|
||||||
import { HTTP, ENV } from '@/vendor/open-api';
|
import { HTTP, ENV } from '@/vendor/open-api';
|
||||||
import { hex_md5 } from '@/vendor/md5';
|
import { hex_md5 } from '@/vendor/md5';
|
||||||
import { getPolicyDescriptor } from '@/utils';
|
import { getPolicyDescriptor } from '@/utils';
|
||||||
@@ -11,7 +11,11 @@ import {
|
|||||||
validCheck,
|
validCheck,
|
||||||
} from '@/utils/flow';
|
} from '@/utils/flow';
|
||||||
import $ from '@/core/app';
|
import $ from '@/core/app';
|
||||||
|
import { findByName } from '@/utils/database';
|
||||||
|
import { produceArtifact } from '@/restful/sync';
|
||||||
import PROXY_PREPROCESSORS from '@/core/proxy-utils/preprocessors';
|
import PROXY_PREPROCESSORS from '@/core/proxy-utils/preprocessors';
|
||||||
|
import { ProxyUtils } from '@/core/proxy-utils';
|
||||||
|
|
||||||
const clashPreprocessor = PROXY_PREPROCESSORS.find(
|
const clashPreprocessor = PROXY_PREPROCESSORS.find(
|
||||||
(processor) => processor.name === 'Clash Pre-processor',
|
(processor) => processor.name === 'Clash Pre-processor',
|
||||||
);
|
);
|
||||||
@@ -130,22 +134,53 @@ export default async function download(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// const downloadUrlMatch = url.match(/^\/api\/(file|module)\/(.+)/);
|
const downloadUrlMatch = url
|
||||||
// if (downloadUrlMatch) {
|
.split('#')[0]
|
||||||
// let type = downloadUrlMatch?.[1];
|
.match(/^\/api\/(file|module)\/(.+)/);
|
||||||
// let name = downloadUrlMatch?.[2];
|
if (downloadUrlMatch) {
|
||||||
// if (name == null) {
|
let type = '';
|
||||||
// throw new Error(`本地 ${type} URL 无效: ${url}`);
|
try {
|
||||||
// }
|
type = downloadUrlMatch?.[1];
|
||||||
// name = decodeURIComponent(name);
|
let name = downloadUrlMatch?.[2];
|
||||||
// const key = type === 'module' ? MODULES_KEY : FILES_KEY;
|
if (name == null) {
|
||||||
// const item = findByName($.read(key), name);
|
throw new Error(`本地 ${type} URL 无效: ${url}`);
|
||||||
// if (!item) {
|
}
|
||||||
// throw new Error(`找不到本地 ${type}: ${name}`);
|
name = decodeURIComponent(name);
|
||||||
// }
|
const key = type === 'module' ? MODULES_KEY : FILES_KEY;
|
||||||
|
const item = findByName($.read(key), name);
|
||||||
|
if (!item) {
|
||||||
|
throw new Error(`找不到 ${type}: ${name}`);
|
||||||
|
}
|
||||||
|
|
||||||
// return item.content;
|
if (type === 'module') {
|
||||||
// }
|
return item.content;
|
||||||
|
} else {
|
||||||
|
return await produceArtifact({
|
||||||
|
type: 'file',
|
||||||
|
name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
$.error(
|
||||||
|
`Error when loading ${type}: ${
|
||||||
|
url.split('#')[0]
|
||||||
|
}.\n Reason: ${err}`,
|
||||||
|
);
|
||||||
|
throw new Error(`无法加载 ${type}: ${url}`);
|
||||||
|
}
|
||||||
|
} else if (url?.startsWith('/')) {
|
||||||
|
try {
|
||||||
|
const fs = eval(`require("fs")`);
|
||||||
|
return fs.readFileSync(url.split('#')[0], 'utf8');
|
||||||
|
} catch (err) {
|
||||||
|
$.error(
|
||||||
|
`Error when reading local file: ${
|
||||||
|
url.split('#')[0]
|
||||||
|
}.\n Reason: ${err}`,
|
||||||
|
);
|
||||||
|
throw new Error(`无法从该路径读取文本内容: ${url}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!isNode && tasks.has(id)) {
|
if (!isNode && tasks.has(id)) {
|
||||||
return tasks.get(id);
|
return tasks.get(id);
|
||||||
@@ -228,10 +263,34 @@ export default async function download(
|
|||||||
if (shouldCache) {
|
if (shouldCache) {
|
||||||
resourceCache.set(id, body);
|
resourceCache.set(id, body);
|
||||||
if (customCacheKey) {
|
if (customCacheKey) {
|
||||||
$.info(
|
let shouldWriteCustomCacheKey = true;
|
||||||
`URL ${url}\n写入自定义缓存 ${$arguments?.cacheKey}`,
|
if (preprocess) {
|
||||||
);
|
try {
|
||||||
$.write(body, customCacheKey);
|
const proxies = ProxyUtils.parse(body);
|
||||||
|
if (
|
||||||
|
!Array.isArray(proxies) ||
|
||||||
|
proxies.length === 0
|
||||||
|
) {
|
||||||
|
$.error(
|
||||||
|
`URL ${url} 不包含有效节点\n不写入自定义缓存 ${$arguments?.cacheKey}`,
|
||||||
|
);
|
||||||
|
shouldWriteCustomCacheKey = false;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
$.error(
|
||||||
|
`URL ${url} 尝试解析节点失败 ${
|
||||||
|
e.message ?? e
|
||||||
|
}\n不写入自定义缓存 ${$arguments?.cacheKey}`,
|
||||||
|
);
|
||||||
|
shouldWriteCustomCacheKey = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shouldWriteCustomCacheKey) {
|
||||||
|
$.info(
|
||||||
|
`URL ${url}\n写入自定义缓存 ${$arguments?.cacheKey}`,
|
||||||
|
);
|
||||||
|
$.write(body, customCacheKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export async function getFlowHeaders(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($arguments?.noFlow) {
|
if ($arguments?.noFlow || !/^https?/.test(url)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { isStash, isLoon, isShadowRocket, isQX } = ENV();
|
const { isStash, isLoon, isShadowRocket, isQX } = ENV();
|
||||||
@@ -334,7 +334,22 @@ export function normalizeFlowHeader(flowHeaders) {
|
|||||||
if (!kvMap.has(key)) {
|
if (!kvMap.has(key)) {
|
||||||
try {
|
try {
|
||||||
// 解码 URI 组件并保留原始值作为 fallback
|
// 解码 URI 组件并保留原始值作为 fallback
|
||||||
const decodedValue = decodeURIComponent(encodedValue);
|
let decodedValue = decodeURIComponent(encodedValue);
|
||||||
|
if (
|
||||||
|
['upload', 'download', 'total', 'expire'].includes(
|
||||||
|
key,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
decodedValue = Number(decodedValue).toFixed(0);
|
||||||
|
} catch (e) {
|
||||||
|
$.error(
|
||||||
|
`Failed to convert value for key "${key}=${encodedValue}": ${
|
||||||
|
e.message ?? e
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
kvMap.set(key, decodedValue);
|
kvMap.set(key, decodedValue);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
kvMap.set(key, encodedValue);
|
kvMap.set(key, encodedValue);
|
||||||
|
|||||||
@@ -61,41 +61,41 @@ export function getPlatformFromHeaders(headers) {
|
|||||||
return getPlatformFromUserAgent({ ua, UA, accept });
|
return getPlatformFromUserAgent({ ua, UA, accept });
|
||||||
}
|
}
|
||||||
export function shouldIncludeUnsupportedProxy(platform, ua) {
|
export function shouldIncludeUnsupportedProxy(platform, ua) {
|
||||||
try {
|
// try {
|
||||||
const target = getPlatformFromUserAgent({
|
// const target = getPlatformFromUserAgent({
|
||||||
UA: ua,
|
// UA: ua,
|
||||||
ua: ua.toLowerCase(),
|
// ua: ua.toLowerCase(),
|
||||||
});
|
// });
|
||||||
if (!['Stash', 'Egern', 'Loon'].includes(target)) {
|
// if (!['Stash', 'Egern', 'Loon'].includes(target)) {
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
const coerceVersion = coerce(ua);
|
// const coerceVersion = coerce(ua);
|
||||||
$.log(JSON.stringify(coerceVersion, null, 2));
|
// $.log(JSON.stringify(coerceVersion, null, 2));
|
||||||
const { version } = coerceVersion;
|
// const { version } = coerceVersion;
|
||||||
if (
|
// if (
|
||||||
platform === 'Stash' &&
|
// platform === 'Stash' &&
|
||||||
target === 'Stash' &&
|
// target === 'Stash' &&
|
||||||
gte(version, '3.1.0')
|
// gte(version, '3.1.0')
|
||||||
) {
|
// ) {
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
if (
|
// if (
|
||||||
platform === 'Egern' &&
|
// platform === 'Egern' &&
|
||||||
target === 'Egern' &&
|
// target === 'Egern' &&
|
||||||
gte(version, '1.29.0')
|
// gte(version, '1.29.0')
|
||||||
) {
|
// ) {
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
// Loon 的 UA 不规范, version 取出来是 build
|
// // Loon 的 UA 不规范, version 取出来是 build
|
||||||
if (
|
// if (
|
||||||
platform === 'Loon' &&
|
// platform === 'Loon' &&
|
||||||
target === 'Loon' &&
|
// target === 'Loon' &&
|
||||||
gte(version, '842.0.0')
|
// gte(version, '842.0.0')
|
||||||
) {
|
// ) {
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
} catch (e) {
|
// } catch (e) {
|
||||||
$.error(`获取版本号失败: ${e}`);
|
// $.error(`获取版本号失败: ${e}`);
|
||||||
}
|
// }
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
54
backend/src/vendor/open-api.js
vendored
54
backend/src/vendor/open-api.js
vendored
@@ -18,6 +18,26 @@ function isPlainObject(obj) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseSocks5Uri(uri) {
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
let [__, username, password, server, port, query, name] = uri.match(
|
||||||
|
/^socks5:\/\/(?:(.*?):(.*?)@)?(.*?)(?::(\d+?))?(\?.*?)?(?:#(.*?))?$/,
|
||||||
|
);
|
||||||
|
if (port) {
|
||||||
|
port = parseInt(port, 10);
|
||||||
|
} else {
|
||||||
|
$.error(`port is not present in line: ${uri}`);
|
||||||
|
throw new Error(`port is not present in line: ${uri}`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: 5,
|
||||||
|
host: server,
|
||||||
|
port,
|
||||||
|
|
||||||
|
userId: username != null ? decodeURIComponent(username) : undefined,
|
||||||
|
password: password != null ? decodeURIComponent(password) : undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
export class OpenAPI {
|
export class OpenAPI {
|
||||||
constructor(name = 'untitled', debug = false) {
|
constructor(name = 'untitled', debug = false) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
@@ -393,6 +413,7 @@ export function HTTP(defaultOptions = { baseURL: '' }) {
|
|||||||
}
|
}
|
||||||
if (isNode) {
|
if (isNode) {
|
||||||
const undici = eval("require('undici')");
|
const undici = eval("require('undici')");
|
||||||
|
const { socksDispatcher } = eval("require('fetch-socks')");
|
||||||
const {
|
const {
|
||||||
ProxyAgent,
|
ProxyAgent,
|
||||||
EnvHttpProxyAgent,
|
EnvHttpProxyAgent,
|
||||||
@@ -422,16 +443,34 @@ export function HTTP(defaultOptions = { baseURL: '' }) {
|
|||||||
).toString('base64')}`,
|
).toString('base64')}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
let dispatcher;
|
||||||
|
if (!opts.proxy) {
|
||||||
|
const allProxy =
|
||||||
|
eval('process.env.all_proxy') ||
|
||||||
|
eval('process.env.ALL_PROXY');
|
||||||
|
if (allProxy && /^socks5:\/\//.test(allProxy)) {
|
||||||
|
opts.proxy = allProxy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (opts.proxy) {
|
||||||
|
if (/^socks5:\/\//.test(opts.proxy)) {
|
||||||
|
dispatcher = socksDispatcher(
|
||||||
|
parseSocks5Uri(opts.proxy),
|
||||||
|
agentOpts,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
dispatcher = new ProxyAgent({
|
||||||
|
...agentOpts,
|
||||||
|
uri: opts.proxy,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dispatcher = new EnvHttpProxyAgent(agentOpts);
|
||||||
|
}
|
||||||
const response = await request(opts.url, {
|
const response = await request(opts.url, {
|
||||||
...opts,
|
...opts,
|
||||||
method: method.toUpperCase(),
|
method: method.toUpperCase(),
|
||||||
dispatcher: (opts.proxy
|
dispatcher: dispatcher.compose(
|
||||||
? new ProxyAgent({
|
|
||||||
...agentOpts,
|
|
||||||
uri: opts.proxy,
|
|
||||||
})
|
|
||||||
: new EnvHttpProxyAgent(agentOpts)
|
|
||||||
).compose(
|
|
||||||
interceptors.redirect({
|
interceptors.redirect({
|
||||||
maxRedirections: 3,
|
maxRedirections: 3,
|
||||||
throwOnMaxRedirects: true,
|
throwOnMaxRedirects: true,
|
||||||
@@ -484,6 +523,7 @@ export function HTTP(defaultOptions = { baseURL: '' }) {
|
|||||||
url: options.url,
|
url: options.url,
|
||||||
headers: options.headers,
|
headers: options.headers,
|
||||||
body: options.body,
|
body: options.body,
|
||||||
|
autoTransformBody: false,
|
||||||
options: {
|
options: {
|
||||||
Proxy: options.proxy,
|
Proxy: options.proxy,
|
||||||
Timeout: options.timeout
|
Timeout: options.timeout
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
upstream api {
|
|
||||||
server 0.0.0.0:3000;
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
listen 6080;
|
|
||||||
# allow 127.0.0.1;
|
|
||||||
# allow 0.0.0.0;
|
|
||||||
# deny all;
|
|
||||||
|
|
||||||
gzip on;
|
|
||||||
gzip_static on;
|
|
||||||
gzip_types text/plain application/json application/javascript application/x-javascript text/css application/xml text/javascript;
|
|
||||||
gzip_proxied any;
|
|
||||||
gzip_vary on;
|
|
||||||
gzip_comp_level 6;
|
|
||||||
gzip_buffers 16 8k;
|
|
||||||
gzip_http_version 1.0;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
root /Sub-Store/web/dist;
|
|
||||||
index index.html index.htm;
|
|
||||||
try_files $uri $uri/ /index.html;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /api {
|
|
||||||
proxy_set_header Host $http_host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_pass http://api;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /download {
|
|
||||||
proxy_set_header Host $http_host;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_pass http://api;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -26,7 +26,7 @@ function operator(proxies = [], targetPlatform, context) {
|
|||||||
// 12. 以 Surge 为例, 最新的参数一般我都会跟进, 以 Surge 文档为例, 一些常用的: TUIC/Hysteria 2 的 `ecn`, Snell 的 `reuse` 连接复用, QUIC 策略 block-quic`, Hysteria 2 下载带宽 `down`
|
// 12. 以 Surge 为例, 最新的参数一般我都会跟进, 以 Surge 文档为例, 一些常用的: TUIC/Hysteria 2 的 `ecn`, Snell 的 `reuse` 连接复用, QUIC 策略 block-quic`, Hysteria 2 下载带宽 `down`
|
||||||
// 13. `test-url` 为测延迟链接, `test-timeout` 为测延迟超时
|
// 13. `test-url` 为测延迟链接, `test-timeout` 为测延迟超时
|
||||||
// 14. `ports` 为端口跳跃, `hop-interval` 变换端口号的时间间隔
|
// 14. `ports` 为端口跳跃, `hop-interval` 变换端口号的时间间隔
|
||||||
// 15. `ip-version` 设置节点使用 IP 版本,可选:dual,ipv4,ipv6,ipv4-prefer,ipv6-prefer. 会进行内部转换, 若无法匹配则使用原始值
|
// 15. `ip-version` 设置节点使用 IP 版本,兼容各家的值. 会进行内部转换. sing-box 以外: 若无法匹配则使用原始值. sing-box: 需有匹配且节点上设置 `_dns_server` 字段, 将自动设置 `domain_resolver.server`
|
||||||
// 16. `sing-box` 支持使用 `_network` 来设置 `network`, 例如 `tcp`, `udp`
|
// 16. `sing-box` 支持使用 `_network` 来设置 `network`, 例如 `tcp`, `udp`
|
||||||
// 17. `block-quic` 支持 `auto`, `on`, `off`. 不同的平台不一定都支持, 会自动转换
|
// 17. `block-quic` 支持 `auto`, `on`, `off`. 不同的平台不一定都支持, 会自动转换
|
||||||
|
|
||||||
@@ -59,6 +59,15 @@ function operator(proxies = [], targetPlatform, context) {
|
|||||||
// }
|
// }
|
||||||
// console.log($options)
|
// console.log($options)
|
||||||
|
|
||||||
|
// 若设置 $options._res.headers
|
||||||
|
// 则会在输出文件时设置响应头, 例如:
|
||||||
|
|
||||||
|
// $options._res = {
|
||||||
|
// headers: {
|
||||||
|
// 'X-Custom': '1'
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// targetPlatform 为输出的目标平台
|
// targetPlatform 为输出的目标平台
|
||||||
|
|
||||||
// lodash
|
// lodash
|
||||||
@@ -124,6 +133,7 @@ function operator(proxies = [], targetPlatform, context) {
|
|||||||
// isValidUUID, // 辅助判断是否为有效的 UUID
|
// isValidUUID, // 辅助判断是否为有效的 UUID
|
||||||
// Buffer, // https://github.com/feross/buffer
|
// Buffer, // https://github.com/feross/buffer
|
||||||
// Base64, // https://github.com/dankogai/js-base64
|
// Base64, // https://github.com/dankogai/js-base64
|
||||||
|
// JSON5, // https://github.com/json5/json5
|
||||||
// }
|
// }
|
||||||
// 为兼容 https://github.com/xishang0128/sparkle 的 JavaScript 覆写, 也可以直接使用 `b64d`(Base64 解码), `b64e`(Base64 编码), `Buffer`, `yaml`(简单兼容了下 `yaml.parse` 和 `yaml.stringify`)
|
// 为兼容 https://github.com/xishang0128/sparkle 的 JavaScript 覆写, 也可以直接使用 `b64d`(Base64 解码), `b64e`(Base64 编码), `Buffer`, `yaml`(简单兼容了下 `yaml.parse` 和 `yaml.stringify`)
|
||||||
|
|
||||||
@@ -141,6 +151,18 @@ function operator(proxies = [], targetPlatform, context) {
|
|||||||
// });
|
// });
|
||||||
// $server.sni = sni
|
// $server.sni = sni
|
||||||
|
|
||||||
|
// 示例: 从 config 文件中读取配置项并进行节点操作
|
||||||
|
// config 的本地内容为
|
||||||
|
// {
|
||||||
|
// "reuse": false
|
||||||
|
// }
|
||||||
|
// 脚本操作为
|
||||||
|
// const config = (ProxyUtils.JSON5 || JSON).parse(await produceArtifact({
|
||||||
|
// type: 'file',
|
||||||
|
// name: 'config' // 文件名
|
||||||
|
// }))
|
||||||
|
// $server.reuse = config.reuse
|
||||||
|
|
||||||
// 1. Surge 输出 WireGuard 完整配置
|
// 1. Surge 输出 WireGuard 完整配置
|
||||||
|
|
||||||
// let proxies = await produceArtifact({
|
// let proxies = await produceArtifact({
|
||||||
@@ -225,14 +247,14 @@ function operator(proxies = [], targetPlatform, context) {
|
|||||||
// 这个历史遗留原因, 是有点复杂. 提供一个例子, 用来取当前脚本所在的组合订阅或单条订阅名称
|
// 这个历史遗留原因, 是有点复杂. 提供一个例子, 用来取当前脚本所在的组合订阅或单条订阅名称
|
||||||
|
|
||||||
// let name = ''
|
// let name = ''
|
||||||
// for (const [key, value] of Object.entries(env.source)) {
|
// for (const [key, value] of Object.entries(context.source)) {
|
||||||
// if (!key.startsWith('_')) {
|
// if (!key.startsWith('_')) {
|
||||||
// name = value.displayName || value.name
|
// name = value.displayName || value.name
|
||||||
// break
|
// break
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// if (!name) {
|
// if (!name) {
|
||||||
// const collection = env.source._collection
|
// const collection = context.source._collection
|
||||||
// name = collection.displayName || collection.name
|
// name = collection.displayName || collection.name
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user