mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2024-11-16 11:42:21 +08:00
feat: finish rpoxies editor
This commit is contained in:
parent
149d482c7d
commit
5a1edc5ffb
|
@ -35,12 +35,14 @@
|
|||
"dayjs": "1.11.5",
|
||||
"foxact": "^0.2.35",
|
||||
"i18next": "^23.11.5",
|
||||
"js-base64": "^3.7.7",
|
||||
"js-yaml": "^4.1.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"meta-json-schema": "1.18.6",
|
||||
"monaco-editor": "^0.49.0",
|
||||
"monaco-yaml": "^5.2.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"peggy": "^4.0.3",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-error-boundary": "^3.1.4",
|
||||
|
@ -48,7 +50,7 @@
|
|||
"react-i18next": "^13.5.0",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-monaco-editor": "^0.55.0",
|
||||
"react-router-dom": "^6.24.0",
|
||||
"react-router-dom": "^6.24.1",
|
||||
"react-transition-group": "^4.4.5",
|
||||
"react-virtuoso": "^4.7.11",
|
||||
"sockette": "^2.0.6",
|
||||
|
@ -79,7 +81,7 @@
|
|||
"sass": "^1.77.6",
|
||||
"terser": "^5.31.1",
|
||||
"typescript": "^5.5.3",
|
||||
"vite": "^5.3.2",
|
||||
"vite": "^5.3.3",
|
||||
"vite-plugin-monaco-editor": "^1.1.0",
|
||||
"vite-plugin-svgr": "^4.2.0"
|
||||
},
|
||||
|
|
160
pnpm-lock.yaml
160
pnpm-lock.yaml
|
@ -58,6 +58,9 @@ importers:
|
|||
i18next:
|
||||
specifier: ^23.11.5
|
||||
version: 23.11.5
|
||||
js-base64:
|
||||
specifier: ^3.7.7
|
||||
version: 3.7.7
|
||||
js-yaml:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0
|
||||
|
@ -76,6 +79,9 @@ importers:
|
|||
nanoid:
|
||||
specifier: ^5.0.7
|
||||
version: 5.0.7
|
||||
peggy:
|
||||
specifier: ^4.0.3
|
||||
version: 4.0.3
|
||||
react:
|
||||
specifier: ^18.3.1
|
||||
version: 18.3.1
|
||||
|
@ -98,8 +104,8 @@ importers:
|
|||
specifier: ^0.55.0
|
||||
version: 0.55.0(@types/react@18.3.3)(monaco-editor@0.49.0)(react@18.3.1)
|
||||
react-router-dom:
|
||||
specifier: ^6.24.0
|
||||
version: 6.24.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
specifier: ^6.24.1
|
||||
version: 6.24.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
react-transition-group:
|
||||
specifier: ^4.4.5
|
||||
version: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
|
@ -148,10 +154,10 @@ importers:
|
|||
version: 4.4.10
|
||||
"@vitejs/plugin-legacy":
|
||||
specifier: ^5.4.1
|
||||
version: 5.4.1(terser@5.31.1)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
|
||||
version: 5.4.1(terser@5.31.1)(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
|
||||
"@vitejs/plugin-react":
|
||||
specifier: ^4.3.1
|
||||
version: 4.3.1(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
|
||||
version: 4.3.1(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
|
||||
adm-zip:
|
||||
specifier: ^0.5.14
|
||||
version: 0.5.14
|
||||
|
@ -186,14 +192,14 @@ importers:
|
|||
specifier: ^5.5.3
|
||||
version: 5.5.3
|
||||
vite:
|
||||
specifier: ^5.3.2
|
||||
version: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
|
||||
specifier: ^5.3.3
|
||||
version: 5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
|
||||
vite-plugin-monaco-editor:
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0(monaco-editor@0.49.0)
|
||||
vite-plugin-svgr:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
|
||||
version: 4.2.0(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))
|
||||
|
||||
packages:
|
||||
"@actions/github@5.1.1":
|
||||
|
@ -1797,16 +1803,23 @@ packages:
|
|||
integrity: sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==,
|
||||
}
|
||||
|
||||
"@peggyjs/from-mem@1.3.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-kzGoIRJjkg3KuGI4bopz9UvF3KguzfxalHRDEIdqEZUe45xezsQ6cx30e0RKuxPUexojQRBfu89Okn7f4/QXsw==,
|
||||
}
|
||||
engines: { node: ">=18" }
|
||||
|
||||
"@popperjs/core@2.11.8":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==,
|
||||
}
|
||||
|
||||
"@remix-run/router@1.17.0":
|
||||
"@remix-run/router@1.17.1":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-2D6XaHEVvkCn682XBnipbJjgZUU7xjLtA4dGJRBVUKpEaDYOZMENZoZjAOSb7qirxt5RupjzZxz4fK2FO+EFPw==,
|
||||
integrity: sha512-mCOMec4BKd6BRGBZeSnGiIgwsbLGp3yhVqAD8H+PxiRNEHgDpZb8J1TnrSDlg97t0ySKMQJTHCWBCmBpSmkF6Q==,
|
||||
}
|
||||
engines: { node: ">=14.0.0" }
|
||||
|
||||
|
@ -2328,10 +2341,10 @@ packages:
|
|||
peerDependencies:
|
||||
vite: ^4.2.0 || ^5.0.0
|
||||
|
||||
acorn@8.12.0:
|
||||
acorn@8.12.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==,
|
||||
integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==,
|
||||
}
|
||||
engines: { node: ">=0.4.0" }
|
||||
hasBin: true
|
||||
|
@ -2486,10 +2499,10 @@ packages:
|
|||
}
|
||||
engines: { node: ">=10" }
|
||||
|
||||
caniuse-lite@1.0.30001639:
|
||||
caniuse-lite@1.0.30001640:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-eFHflNTBIlFwP2AIKaYuBQN/apnUoKNhBdza8ZnW/h2di4LCZ4xFqYlxUxo+LQ76KFI1PGcC1QDxMbxTZpSCAg==,
|
||||
integrity: sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA==,
|
||||
}
|
||||
|
||||
ccount@2.0.1:
|
||||
|
@ -2581,6 +2594,13 @@ packages:
|
|||
integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==,
|
||||
}
|
||||
|
||||
commander@12.1.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==,
|
||||
}
|
||||
engines: { node: ">=18" }
|
||||
|
||||
commander@2.20.3:
|
||||
resolution:
|
||||
{
|
||||
|
@ -3128,6 +3148,12 @@ packages:
|
|||
integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==,
|
||||
}
|
||||
|
||||
js-base64@3.7.7:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==,
|
||||
}
|
||||
|
||||
js-cookie@2.2.1:
|
||||
resolution:
|
||||
{
|
||||
|
@ -3244,6 +3270,13 @@ packages:
|
|||
integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==,
|
||||
}
|
||||
|
||||
lru-cache@6.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==,
|
||||
}
|
||||
engines: { node: ">=10" }
|
||||
|
||||
magic-string@0.30.10:
|
||||
resolution:
|
||||
{
|
||||
|
@ -3708,6 +3741,14 @@ packages:
|
|||
}
|
||||
engines: { node: ">=8" }
|
||||
|
||||
peggy@4.0.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-v7/Pt6kGYsfXsCrfb52q7/yg5jaAwiVaUMAPLPvy4DJJU6Wwr72t6nDIqIDkGfzd1B4zeVuTnQT0RGeOhe/uSA==,
|
||||
}
|
||||
engines: { node: ">=18" }
|
||||
hasBin: true
|
||||
|
||||
picocolors@1.0.1:
|
||||
resolution:
|
||||
{
|
||||
|
@ -3863,20 +3904,20 @@ packages:
|
|||
}
|
||||
engines: { node: ">=0.10.0" }
|
||||
|
||||
react-router-dom@6.24.0:
|
||||
react-router-dom@6.24.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-960sKuau6/yEwS8e+NVEidYQb1hNjAYM327gjEyXlc6r3Skf2vtwuJ2l7lssdegD2YjoKG5l8MsVyeTDlVeY8g==,
|
||||
integrity: sha512-U19KtXqooqw967Vw0Qcn5cOvrX5Ejo9ORmOtJMzYWtCT4/WOfFLIZGGsVLxcd9UkBO0mSTZtXqhZBsWlHr7+Sg==,
|
||||
}
|
||||
engines: { node: ">=14.0.0" }
|
||||
peerDependencies:
|
||||
react: ">=16.8"
|
||||
react-dom: ">=16.8"
|
||||
|
||||
react-router@6.24.0:
|
||||
react-router@6.24.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-sQrgJ5bXk7vbcC4BxQxeNa5UmboFm35we1AFK0VvQaz9g0LzxEIuLOhHIoZ8rnu9BO21ishGeL9no1WB76W/eg==,
|
||||
integrity: sha512-PTXFXGK2pyXpHzVo3rR9H7ip4lSPZZc0bHG5CARmj65fTT6qG7sTngmb6lcYu1gf3y/8KxORoy9yn59pGpCnpg==,
|
||||
}
|
||||
engines: { node: ">=14.0.0" }
|
||||
peerDependencies:
|
||||
|
@ -4028,6 +4069,14 @@ packages:
|
|||
}
|
||||
hasBin: true
|
||||
|
||||
semver@7.6.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==,
|
||||
}
|
||||
engines: { node: ">=10" }
|
||||
hasBin: true
|
||||
|
||||
server-only@0.0.1:
|
||||
resolution:
|
||||
{
|
||||
|
@ -4066,6 +4115,13 @@ packages:
|
|||
integrity: sha512-W6iG8RGV6Zife3Cj+FhuyHV447E6fqFM2hKmnaQrTvg3OydINV3Msj3WPFbX76blUlUxvQSMMMdrJxce8NqI5Q==,
|
||||
}
|
||||
|
||||
source-map-generator@0.8.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-psgxdGMwl5MZM9S3FWee4EgsEaIjahYV5AzGnwUvPhWeITz/j6rKpysQHlQ4USdxvINlb8lKfWGIXwfkrgtqkA==,
|
||||
}
|
||||
engines: { node: ">= 10" }
|
||||
|
||||
source-map-js@1.2.0:
|
||||
resolution:
|
||||
{
|
||||
|
@ -4328,10 +4384,10 @@ packages:
|
|||
}
|
||||
engines: { node: ">= 10.0.0" }
|
||||
|
||||
update-browserslist-db@1.0.16:
|
||||
update-browserslist-db@1.1.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==,
|
||||
integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==,
|
||||
}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
|
@ -4373,10 +4429,10 @@ packages:
|
|||
peerDependencies:
|
||||
vite: ^2.6.0 || 3 || 4 || 5
|
||||
|
||||
vite@5.3.2:
|
||||
vite@5.3.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-6lA7OBHBlXUxiJxbO5aAY2fsHHzDr1q7DvXYnyZycRs2Dz+dXBWuhpWHvmljTRTpQC2uvGmUFFkSHF2vGo90MA==,
|
||||
integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==,
|
||||
}
|
||||
engines: { node: ^18.0.0 || >=20.0.0 }
|
||||
hasBin: true
|
||||
|
@ -5751,9 +5807,13 @@ snapshots:
|
|||
dependencies:
|
||||
"@octokit/openapi-types": 12.11.0
|
||||
|
||||
"@peggyjs/from-mem@1.3.0":
|
||||
dependencies:
|
||||
semver: 7.6.0
|
||||
|
||||
"@popperjs/core@2.11.8": {}
|
||||
|
||||
"@remix-run/router@1.17.0": {}
|
||||
"@remix-run/router@1.17.1": {}
|
||||
|
||||
"@rollup/pluginutils@5.1.0(rollup@4.18.0)":
|
||||
dependencies:
|
||||
|
@ -6010,7 +6070,7 @@ snapshots:
|
|||
|
||||
"@ungap/structured-clone@1.2.0": {}
|
||||
|
||||
"@vitejs/plugin-legacy@5.4.1(terser@5.31.1)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))":
|
||||
"@vitejs/plugin-legacy@5.4.1(terser@5.31.1)(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))":
|
||||
dependencies:
|
||||
"@babel/core": 7.24.7
|
||||
"@babel/preset-env": 7.24.7(@babel/core@7.24.7)
|
||||
|
@ -6021,22 +6081,22 @@ snapshots:
|
|||
regenerator-runtime: 0.14.1
|
||||
systemjs: 6.15.1
|
||||
terser: 5.31.1
|
||||
vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
|
||||
vite: 5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
"@vitejs/plugin-react@4.3.1(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))":
|
||||
"@vitejs/plugin-react@4.3.1(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1))":
|
||||
dependencies:
|
||||
"@babel/core": 7.24.7
|
||||
"@babel/plugin-transform-react-jsx-self": 7.24.7(@babel/core@7.24.7)
|
||||
"@babel/plugin-transform-react-jsx-source": 7.24.7(@babel/core@7.24.7)
|
||||
"@types/babel__core": 7.20.5
|
||||
react-refresh: 0.14.2
|
||||
vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
|
||||
vite: 5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
acorn@8.12.0: {}
|
||||
acorn@8.12.1: {}
|
||||
|
||||
adm-zip@0.5.14: {}
|
||||
|
||||
|
@ -6127,10 +6187,10 @@ snapshots:
|
|||
|
||||
browserslist@4.23.1:
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001639
|
||||
caniuse-lite: 1.0.30001640
|
||||
electron-to-chromium: 1.4.816
|
||||
node-releases: 2.0.14
|
||||
update-browserslist-db: 1.0.16(browserslist@4.23.1)
|
||||
update-browserslist-db: 1.1.0(browserslist@4.23.1)
|
||||
|
||||
buffer-from@1.1.2: {}
|
||||
|
||||
|
@ -6138,7 +6198,7 @@ snapshots:
|
|||
|
||||
camelcase@6.3.0: {}
|
||||
|
||||
caniuse-lite@1.0.30001639: {}
|
||||
caniuse-lite@1.0.30001640: {}
|
||||
|
||||
ccount@2.0.1: {}
|
||||
|
||||
|
@ -6186,6 +6246,8 @@ snapshots:
|
|||
|
||||
comma-separated-tokens@2.0.3: {}
|
||||
|
||||
commander@12.1.0: {}
|
||||
|
||||
commander@2.20.3: {}
|
||||
|
||||
convert-source-map@1.9.0: {}
|
||||
|
@ -6492,6 +6554,8 @@ snapshots:
|
|||
|
||||
isexe@2.0.0: {}
|
||||
|
||||
js-base64@3.7.7: {}
|
||||
|
||||
js-cookie@2.2.1: {}
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
@ -6542,6 +6606,10 @@ snapshots:
|
|||
dependencies:
|
||||
yallist: 3.1.1
|
||||
|
||||
lru-cache@6.0.0:
|
||||
dependencies:
|
||||
yallist: 4.0.0
|
||||
|
||||
magic-string@0.30.10:
|
||||
dependencies:
|
||||
"@jridgewell/sourcemap-codec": 1.4.15
|
||||
|
@ -6914,6 +6982,12 @@ snapshots:
|
|||
|
||||
path-type@4.0.0: {}
|
||||
|
||||
peggy@4.0.3:
|
||||
dependencies:
|
||||
"@peggyjs/from-mem": 1.3.0
|
||||
commander: 12.1.0
|
||||
source-map-generator: 0.8.0
|
||||
|
||||
picocolors@1.0.1: {}
|
||||
|
||||
picomatch@2.3.1: {}
|
||||
|
@ -7010,16 +7084,16 @@ snapshots:
|
|||
|
||||
react-refresh@0.14.2: {}
|
||||
|
||||
react-router-dom@6.24.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
react-router-dom@6.24.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
"@remix-run/router": 1.17.0
|
||||
"@remix-run/router": 1.17.1
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
react-router: 6.24.0(react@18.3.1)
|
||||
react-router: 6.24.1(react@18.3.1)
|
||||
|
||||
react-router@6.24.0(react@18.3.1):
|
||||
react-router@6.24.1(react@18.3.1):
|
||||
dependencies:
|
||||
"@remix-run/router": 1.17.0
|
||||
"@remix-run/router": 1.17.1
|
||||
react: 18.3.1
|
||||
|
||||
react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
|
@ -7134,6 +7208,10 @@ snapshots:
|
|||
|
||||
semver@6.3.1: {}
|
||||
|
||||
semver@7.6.0:
|
||||
dependencies:
|
||||
lru-cache: 6.0.0
|
||||
|
||||
server-only@0.0.1: {}
|
||||
|
||||
shebang-command@2.0.0:
|
||||
|
@ -7151,6 +7229,8 @@ snapshots:
|
|||
|
||||
sockette@2.0.6: {}
|
||||
|
||||
source-map-generator@0.8.0: {}
|
||||
|
||||
source-map-js@1.2.0: {}
|
||||
|
||||
source-map-support@0.5.21:
|
||||
|
@ -7205,7 +7285,7 @@ snapshots:
|
|||
terser@5.31.1:
|
||||
dependencies:
|
||||
"@jridgewell/source-map": 0.3.6
|
||||
acorn: 8.12.0
|
||||
acorn: 8.12.1
|
||||
commander: 2.20.3
|
||||
source-map-support: 0.5.21
|
||||
|
||||
|
@ -7288,7 +7368,7 @@ snapshots:
|
|||
|
||||
universalify@2.0.1: {}
|
||||
|
||||
update-browserslist-db@1.0.16(browserslist@4.23.1):
|
||||
update-browserslist-db@1.1.0(browserslist@4.23.1):
|
||||
dependencies:
|
||||
browserslist: 4.23.1
|
||||
escalade: 3.1.2
|
||||
|
@ -7313,18 +7393,18 @@ snapshots:
|
|||
dependencies:
|
||||
monaco-editor: 0.49.0
|
||||
|
||||
vite-plugin-svgr@4.2.0(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)):
|
||||
vite-plugin-svgr@4.2.0(rollup@4.18.0)(typescript@5.5.3)(vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)):
|
||||
dependencies:
|
||||
"@rollup/pluginutils": 5.1.0(rollup@4.18.0)
|
||||
"@svgr/core": 8.1.0(typescript@5.5.3)
|
||||
"@svgr/plugin-jsx": 8.1.0(@svgr/core@8.1.0(typescript@5.5.3))
|
||||
vite: 5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
|
||||
vite: 5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1)
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
vite@5.3.2(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1):
|
||||
vite@5.3.3(@types/node@20.14.9)(sass@1.77.6)(terser@5.31.1):
|
||||
dependencies:
|
||||
esbuild: 0.21.5
|
||||
postcss: 8.4.39
|
||||
|
|
|
@ -16,7 +16,6 @@ import {
|
|||
sortableKeyboardCoordinates,
|
||||
} from "@dnd-kit/sortable";
|
||||
import {
|
||||
Autocomplete,
|
||||
Box,
|
||||
Button,
|
||||
Dialog,
|
||||
|
@ -31,13 +30,13 @@ import {
|
|||
} from "@mui/material";
|
||||
import { ProxyItem } from "@/components/profile/proxy-item";
|
||||
import { readProfileFile, saveProfileFile } from "@/services/cmds";
|
||||
import { Notice, Switch } from "@/components/base";
|
||||
import { Notice } from "@/components/base";
|
||||
import getSystem from "@/utils/get-system";
|
||||
import { BaseSearchBox } from "../base/base-search-box";
|
||||
import { Virtuoso } from "react-virtuoso";
|
||||
import MonacoEditor from "react-monaco-editor";
|
||||
import { useThemeMode } from "@/services/states";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import parseUri from "@/utils/uri-parser";
|
||||
|
||||
interface Props {
|
||||
profileUid: string;
|
||||
|
@ -47,8 +46,6 @@ interface Props {
|
|||
onSave?: (prev?: string, curr?: string) => void;
|
||||
}
|
||||
|
||||
const builtinProxyPolicies = ["DIRECT", "REJECT", "REJECT-DROP", "PASS"];
|
||||
|
||||
export const ProxiesEditorViewer = (props: Props) => {
|
||||
const { profileUid, property, open, onClose, onSave } = props;
|
||||
const { t } = useTranslation();
|
||||
|
@ -57,13 +54,7 @@ export const ProxiesEditorViewer = (props: Props) => {
|
|||
const [currData, setCurrData] = useState("");
|
||||
const [visualization, setVisualization] = useState(true);
|
||||
const [match, setMatch] = useState(() => (_: string) => true);
|
||||
|
||||
const { control, watch, register, ...formIns } = useForm<IProxyConfig>({
|
||||
defaultValues: {
|
||||
type: "ss",
|
||||
name: "",
|
||||
},
|
||||
});
|
||||
const [proxyUri, setProxyUri] = useState<string>("");
|
||||
|
||||
const [proxyList, setProxyList] = useState<IProxyConfig[]>([]);
|
||||
const [prependSeq, setPrependSeq] = useState<IProxyConfig[]>([]);
|
||||
|
@ -231,104 +222,36 @@ export const ProxiesEditorViewer = (props: Props) => {
|
|||
overflowY: "auto",
|
||||
}}
|
||||
>
|
||||
<Controller
|
||||
name="type"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Item>
|
||||
<ListItemText primary={t("Proxy Type")} />
|
||||
<Autocomplete
|
||||
size="small"
|
||||
sx={{ minWidth: "240px" }}
|
||||
options={[
|
||||
"ss",
|
||||
"ssr",
|
||||
"direct",
|
||||
"dns",
|
||||
"snell",
|
||||
"http",
|
||||
"trojan",
|
||||
"hysteria",
|
||||
"hysteria2",
|
||||
"tuic",
|
||||
"wireguard",
|
||||
"ssh",
|
||||
"socks5",
|
||||
"vmess",
|
||||
"vless",
|
||||
]}
|
||||
value={field.value}
|
||||
onChange={(_, value) => value && field.onChange(value)}
|
||||
renderInput={(params) => <TextField {...params} />}
|
||||
/>
|
||||
</Item>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="name"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Item>
|
||||
<ListItemText primary={t("Proxy Name")} />
|
||||
<TextField
|
||||
autoComplete="off"
|
||||
size="small"
|
||||
sx={{ minWidth: "240px" }}
|
||||
{...field}
|
||||
required={true}
|
||||
/>
|
||||
</Item>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="server"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Item>
|
||||
<ListItemText primary={t("Proxy Server")} />
|
||||
<TextField
|
||||
autoComplete="off"
|
||||
size="small"
|
||||
sx={{ minWidth: "240px" }}
|
||||
{...field}
|
||||
/>
|
||||
</Item>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="port"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Item>
|
||||
<ListItemText primary={t("Proxy Port")} />
|
||||
<TextField
|
||||
autoComplete="off"
|
||||
type="number"
|
||||
size="small"
|
||||
sx={{ minWidth: "240px" }}
|
||||
onChange={(e) => {
|
||||
field.onChange(parseInt(e.target.value));
|
||||
}}
|
||||
/>
|
||||
</Item>
|
||||
)}
|
||||
/>
|
||||
<Item>
|
||||
<TextField
|
||||
autoComplete="off"
|
||||
placeholder={t("Use newlines for multiple uri")}
|
||||
fullWidth
|
||||
minRows={8}
|
||||
multiline
|
||||
size="small"
|
||||
onChange={(e) => setProxyUri(e.target.value)}
|
||||
/>
|
||||
</Item>
|
||||
</Box>
|
||||
<Item>
|
||||
<Button
|
||||
fullWidth
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
try {
|
||||
for (const item of prependSeq) {
|
||||
if (item.name === formIns.getValues().name) {
|
||||
throw new Error(t("Proxy Name Already Exists"));
|
||||
let proxies = [] as IProxyConfig[];
|
||||
proxyUri
|
||||
.trim()
|
||||
.split("\n")
|
||||
.forEach((uri) => {
|
||||
try {
|
||||
let proxy = parseUri(uri.trim());
|
||||
proxies.push(proxy);
|
||||
} catch (err: any) {
|
||||
Notice.error(err.message || err.toString());
|
||||
}
|
||||
}
|
||||
setPrependSeq([...prependSeq, formIns.getValues()]);
|
||||
} catch (err: any) {
|
||||
Notice.error(err.message || err.toString());
|
||||
}
|
||||
});
|
||||
setPrependSeq([...prependSeq, ...proxies]);
|
||||
}}
|
||||
>
|
||||
{t("Prepend Proxy")}
|
||||
|
@ -339,16 +262,19 @@ export const ProxiesEditorViewer = (props: Props) => {
|
|||
fullWidth
|
||||
variant="contained"
|
||||
onClick={() => {
|
||||
try {
|
||||
for (const item of appendSeq) {
|
||||
if (item.name === formIns.getValues().name) {
|
||||
throw new Error(t("Proxy Name Already Exists"));
|
||||
let proxies = [] as IProxyConfig[];
|
||||
proxyUri
|
||||
.trim()
|
||||
.split("\n")
|
||||
.forEach((uri) => {
|
||||
try {
|
||||
let proxy = parseUri(uri.trim());
|
||||
proxies.push(proxy);
|
||||
} catch (err: any) {
|
||||
Notice.error(err.message || err.toString());
|
||||
}
|
||||
}
|
||||
setAppendSeq([...appendSeq, formIns.getValues()]);
|
||||
} catch (err: any) {
|
||||
Notice.error(err.message || err.toString());
|
||||
}
|
||||
});
|
||||
setAppendSeq([...appendSeq, ...proxies]);
|
||||
}}
|
||||
>
|
||||
{t("Append Proxy")}
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
"Create Profile": "Create Profile",
|
||||
"Edit Profile": "Edit Profile",
|
||||
"Edit Proxies": "Edit Proxies",
|
||||
"Use newlines for multiple uri": "Use newlines for multiple uri",
|
||||
"Edit Rules": "Edit Rules",
|
||||
"Rule Type": "Rule Type",
|
||||
"Rule Content": "Rule Content",
|
||||
|
@ -63,6 +64,8 @@
|
|||
"Append Rule": "Append Rule",
|
||||
"Prepend Group": "Prepend Group",
|
||||
"Append Group": "Append Group",
|
||||
"Prepend Proxy": "Prepend Proxy",
|
||||
"Append Proxy": "Append Proxy",
|
||||
"Rule Condition Required": "Rule Condition Required",
|
||||
"Invalid Rule": "Invalid Rule",
|
||||
"Advanced": "Advanced",
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
"Create Profile": "ایجاد پروفایل",
|
||||
"Edit Profile": "ویرایش پروفایل",
|
||||
"Edit Proxies": "ویرایش پروکسیها",
|
||||
"Use newlines for multiple uri": "استفاده از خطوط جدید برای چندین آدرس",
|
||||
"Edit Rules": "ویرایش قوانین",
|
||||
"Rule Type": "نوع قانون",
|
||||
"Rule Content": "محتوای قانون",
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
"Create Profile": "Создать профиль",
|
||||
"Edit Profile": "Изменить профиль",
|
||||
"Edit Proxies": "Редактировать прокси",
|
||||
"Use newlines for multiple uri": "Используйте новые строки для нескольких URI",
|
||||
"Edit Rules": "Редактировать правила",
|
||||
"Rule Type": "Тип правила",
|
||||
"Rule Content": "Содержимое правила",
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
"Create Profile": "新建配置",
|
||||
"Edit Profile": "编辑配置",
|
||||
"Edit Proxies": "编辑节点",
|
||||
"Use newlines for multiple uri": "多条URI请使用换行分隔",
|
||||
"Edit Rules": "编辑规则",
|
||||
"Rule Type": "规则类型",
|
||||
"Rule Content": "规则内容",
|
||||
|
@ -63,6 +64,8 @@
|
|||
"Append Rule": "添加后置规则",
|
||||
"Prepend Group": "添加前置代理组",
|
||||
"Append Group": "添加后置代理组",
|
||||
"Prepend Proxy": "添加前置代理节点",
|
||||
"Append Proxy": "添加后置代理节点",
|
||||
"Rule Condition Required": "规则条件缺失",
|
||||
"Invalid Rule": "无效规则",
|
||||
"Advanced": "高级",
|
||||
|
|
92
src/services/types.d.ts
vendored
92
src/services/types.d.ts
vendored
|
@ -255,6 +255,98 @@ interface IProxyConfig {
|
|||
tfo?: boolean;
|
||||
mptcp?: boolean;
|
||||
"dialer-proxy"?: string;
|
||||
plugin?: "obfs" | "v2ray-plugin" | "shadow-tls" | "restls";
|
||||
"plugin-opts"?: {
|
||||
mode?: string;
|
||||
host?: string;
|
||||
path?: string;
|
||||
tls?: string;
|
||||
};
|
||||
cipher?: string;
|
||||
password?: string;
|
||||
"udp-over-tcp"?: boolean;
|
||||
protocol?: string;
|
||||
obfs?: string;
|
||||
"protocol-param"?: string;
|
||||
"obfs-param"?: string;
|
||||
uuid?: string;
|
||||
tls?: boolean;
|
||||
"skip-cert-verify"?: boolean;
|
||||
network?: "ws" | "http" | "h2" | "grpc";
|
||||
"ws-opts"?: {
|
||||
path?: string;
|
||||
headers?: {};
|
||||
};
|
||||
alterId?: number;
|
||||
sni?: string;
|
||||
"http-opts"?: {};
|
||||
"grpc-opts"?: {};
|
||||
"ws-opts"?: {};
|
||||
"h2-opts"?: {};
|
||||
"reality-opts"?: {
|
||||
"public-key"?: string;
|
||||
"short-id"?: string;
|
||||
};
|
||||
flow?: "xtls-rprx-vision";
|
||||
"client-fingerprint"?:
|
||||
| "chrome"
|
||||
| "firefox"
|
||||
| "safari"
|
||||
| "iOS"
|
||||
| "android"
|
||||
| "edge"
|
||||
| "360"
|
||||
| "qq"
|
||||
| "random";
|
||||
alpn?: string[];
|
||||
ws?: {
|
||||
headers?: {
|
||||
Host?: string;
|
||||
};
|
||||
"ws-service-name"?: string;
|
||||
path?: string;
|
||||
};
|
||||
http?: {
|
||||
headers?: {
|
||||
Host?: string;
|
||||
};
|
||||
"http-service-name"?: string;
|
||||
path?: string;
|
||||
};
|
||||
grpc?: {};
|
||||
ports?: string;
|
||||
"obfs-password"?: string;
|
||||
"tls-fingerprint"?: string;
|
||||
"auth-str"?: string;
|
||||
up?: string;
|
||||
down?: string;
|
||||
"fast-open"?: boolean;
|
||||
fingerprint?: string;
|
||||
"disable-mtu-discovery"?: boolean;
|
||||
ca?: string;
|
||||
"ca-str"?: string;
|
||||
"recv-window-conn"?: number;
|
||||
"recv-window"?: number;
|
||||
token?: string;
|
||||
ip?: string;
|
||||
"heartbeat-interval"?: number;
|
||||
"disable-sni"?: boolean;
|
||||
"reduce-rtt"?: boolean;
|
||||
"request-timeout"?: number;
|
||||
"udp-relay-mode"?: string;
|
||||
"congestion-controller"?: string;
|
||||
"max-udp-relay-packet-size"?: number;
|
||||
"max-open-streams"?: number;
|
||||
"private-key"?: string;
|
||||
"public-key"?: string;
|
||||
ipv6?: string;
|
||||
reserved?: number[];
|
||||
mtu?: number;
|
||||
"remote-dns-resolve"?: boolean;
|
||||
"allowed-ips"?: string[];
|
||||
dns?: string[];
|
||||
"pre-shared-key"?: string;
|
||||
username?: string;
|
||||
}
|
||||
|
||||
interface IVergeConfig {
|
||||
|
|
131
src/utils/trojan-uri.peg
Normal file
131
src/utils/trojan-uri.peg
Normal file
|
@ -0,0 +1,131 @@
|
|||
// global initializer
|
||||
{{
|
||||
function $set(obj, path, value) {
|
||||
if (Object(obj) !== obj) return obj;
|
||||
if (!Array.isArray(path)) path = path.toString().match(/[^.[\]]+/g) || [];
|
||||
path
|
||||
.slice(0, -1)
|
||||
.reduce((a, c, i) => (Object(a[c]) === a[c] ? a[c] : (a[c] = Math.abs(path[i + 1]) >> 0 === +path[i + 1] ? [] : {})), obj)[
|
||||
path[path.length - 1]
|
||||
] = value;
|
||||
return obj;
|
||||
}
|
||||
|
||||
function toBool(str) {
|
||||
if (typeof str === 'undefined' || str === null) return undefined;
|
||||
return /(TRUE)|1/i.test(str);
|
||||
}
|
||||
}}
|
||||
|
||||
{
|
||||
const proxy = {};
|
||||
const obfs = {};
|
||||
const $ = {};
|
||||
const params = {};
|
||||
}
|
||||
|
||||
start = (trojan) {
|
||||
return proxy
|
||||
}
|
||||
|
||||
trojan = "trojan://" password:password "@" server:server ":" port:port "/"? params? name:name?{
|
||||
proxy.type = "trojan";
|
||||
proxy.password = password;
|
||||
proxy.server = server;
|
||||
proxy.port = port;
|
||||
proxy.name = name;
|
||||
|
||||
// name may be empty
|
||||
if (!proxy.name) {
|
||||
proxy.name = server + ":" + port;
|
||||
}
|
||||
};
|
||||
|
||||
password = match:$[^@]+ {
|
||||
return decodeURIComponent(match);
|
||||
};
|
||||
|
||||
server = ip/domain;
|
||||
|
||||
domain = match:[0-9a-zA-z-_.]+ {
|
||||
const domain = match.join("");
|
||||
if (/(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]/.test(domain)) {
|
||||
return domain;
|
||||
}
|
||||
}
|
||||
|
||||
ip = & {
|
||||
const start = peg$currPos;
|
||||
let end;
|
||||
let j = start;
|
||||
while (j < input.length) {
|
||||
if (input[j] === ",") break;
|
||||
if (input[j] === ":") end = j;
|
||||
j++;
|
||||
}
|
||||
peg$currPos = end || j;
|
||||
$.ip = input.substring(start, end).trim();
|
||||
return true;
|
||||
} { return $.ip; }
|
||||
|
||||
port = digits:[0-9]+ {
|
||||
const port = parseInt(digits.join(""), 10);
|
||||
if (port >= 0 && port <= 65535) {
|
||||
return port;
|
||||
} else {
|
||||
throw new Error("Invalid port: " + port);
|
||||
}
|
||||
}
|
||||
|
||||
params = "?" head:param tail:("&"@param)* {
|
||||
proxy["skip-cert-verify"] = toBool(params["allowInsecure"]);
|
||||
proxy.sni = params["sni"] || params["peer"];
|
||||
|
||||
if (toBool(params["ws"])) {
|
||||
proxy.network = "ws";
|
||||
$set(proxy, "ws-opts.path", params["wspath"]);
|
||||
}
|
||||
|
||||
if (params["type"]) {
|
||||
let httpupgrade
|
||||
proxy.network = params["type"]
|
||||
if(proxy.network === 'httpupgrade') {
|
||||
proxy.network = 'ws'
|
||||
httpupgrade = true
|
||||
}
|
||||
if (['grpc'].includes(proxy.network)) {
|
||||
proxy[proxy.network + '-opts'] = {
|
||||
'grpc-service-name': params["serviceName"],
|
||||
'_grpc-type': params["mode"],
|
||||
};
|
||||
} else {
|
||||
if (params["path"]) {
|
||||
$set(proxy, proxy.network+"-opts.path", decodeURIComponent(params["path"]));
|
||||
}
|
||||
if (params["host"]) {
|
||||
$set(proxy, proxy.network+"-opts.headers.Host", decodeURIComponent(params["host"]));
|
||||
}
|
||||
if (httpupgrade) {
|
||||
$set(proxy, proxy.network+"-opts.v2ray-http-upgrade", true);
|
||||
$set(proxy, proxy.network+"-opts.v2ray-http-upgrade-fast-open", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proxy.udp = toBool(params["udp"]);
|
||||
proxy.tfo = toBool(params["tfo"]);
|
||||
}
|
||||
|
||||
param = kv/single;
|
||||
|
||||
kv = key:$[a-z]i+ "=" value:$[^&#]i* {
|
||||
params[key] = value;
|
||||
}
|
||||
|
||||
single = key:$[a-z]i+ {
|
||||
params[key] = true;
|
||||
};
|
||||
|
||||
name = "#" + match:$.* {
|
||||
return decodeURIComponent(match);
|
||||
}
|
141
src/utils/trojan-uri.ts
Normal file
141
src/utils/trojan-uri.ts
Normal file
|
@ -0,0 +1,141 @@
|
|||
import * as peggy from "peggy";
|
||||
const grammars = String.raw`
|
||||
// global initializer
|
||||
{{
|
||||
function $set(obj, path, value) {
|
||||
if (Object(obj) !== obj) return obj;
|
||||
if (!Array.isArray(path)) path = path.toString().match(/[^.[\]]+/g) || [];
|
||||
path
|
||||
.slice(0, -1)
|
||||
.reduce((a, c, i) => (Object(a[c]) === a[c] ? a[c] : (a[c] = Math.abs(path[i + 1]) >> 0 === +path[i + 1] ? [] : {})), obj)[
|
||||
path[path.length - 1]
|
||||
] = value;
|
||||
return obj;
|
||||
}
|
||||
|
||||
function toBool(str) {
|
||||
if (typeof str === 'undefined' || str === null) return undefined;
|
||||
return /(TRUE)|1/i.test(str);
|
||||
}
|
||||
}}
|
||||
|
||||
{
|
||||
const proxy = {};
|
||||
const obfs = {};
|
||||
const $ = {};
|
||||
const params = {};
|
||||
}
|
||||
|
||||
start = (trojan) {
|
||||
return proxy
|
||||
}
|
||||
|
||||
trojan = "trojan://" password:password "@" server:server ":" port:port "/"? params? name:name?{
|
||||
proxy.type = "trojan";
|
||||
proxy.password = password;
|
||||
proxy.server = server;
|
||||
proxy.port = port;
|
||||
proxy.name = name;
|
||||
|
||||
// name may be empty
|
||||
if (!proxy.name) {
|
||||
proxy.name = server + ":" + port;
|
||||
}
|
||||
};
|
||||
|
||||
password = match:$[^@]+ {
|
||||
return decodeURIComponent(match);
|
||||
};
|
||||
|
||||
server = ip/domain;
|
||||
|
||||
domain = match:[0-9a-zA-z-_.]+ {
|
||||
const domain = match.join("");
|
||||
if (/(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]/.test(domain)) {
|
||||
return domain;
|
||||
}
|
||||
}
|
||||
|
||||
ip = & {
|
||||
const start = peg$currPos;
|
||||
let end;
|
||||
let j = start;
|
||||
while (j < input.length) {
|
||||
if (input[j] === ",") break;
|
||||
if (input[j] === ":") end = j;
|
||||
j++;
|
||||
}
|
||||
peg$currPos = end || j;
|
||||
$.ip = input.substring(start, end).trim();
|
||||
return true;
|
||||
} { return $.ip; }
|
||||
|
||||
port = digits:[0-9]+ {
|
||||
const port = parseInt(digits.join(""), 10);
|
||||
if (port >= 0 && port <= 65535) {
|
||||
return port;
|
||||
} else {
|
||||
throw new Error("Invalid port: " + port);
|
||||
}
|
||||
}
|
||||
|
||||
params = "?" head:param tail:("&"@param)* {
|
||||
proxy["skip-cert-verify"] = toBool(params["allowInsecure"]);
|
||||
proxy.sni = params["sni"] || params["peer"];
|
||||
|
||||
if (toBool(params["ws"])) {
|
||||
proxy.network = "ws";
|
||||
$set(proxy, "ws-opts.path", params["wspath"]);
|
||||
}
|
||||
|
||||
if (params["type"]) {
|
||||
let httpupgrade
|
||||
proxy.network = params["type"]
|
||||
if(proxy.network === 'httpupgrade') {
|
||||
proxy.network = 'ws'
|
||||
httpupgrade = true
|
||||
}
|
||||
if (['grpc'].includes(proxy.network)) {
|
||||
proxy[proxy.network + '-opts'] = {
|
||||
'grpc-service-name': params["serviceName"],
|
||||
'_grpc-type': params["mode"],
|
||||
};
|
||||
} else {
|
||||
if (params["path"]) {
|
||||
$set(proxy, proxy.network+"-opts.path", decodeURIComponent(params["path"]));
|
||||
}
|
||||
if (params["host"]) {
|
||||
$set(proxy, proxy.network+"-opts.headers.Host", decodeURIComponent(params["host"]));
|
||||
}
|
||||
if (httpupgrade) {
|
||||
$set(proxy, proxy.network+"-opts.v2ray-http-upgrade", true);
|
||||
$set(proxy, proxy.network+"-opts.v2ray-http-upgrade-fast-open", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proxy.udp = toBool(params["udp"]);
|
||||
proxy.tfo = toBool(params["tfo"]);
|
||||
}
|
||||
|
||||
param = kv/single;
|
||||
|
||||
kv = key:$[a-z]i+ "=" value:$[^&#]i* {
|
||||
params[key] = value;
|
||||
}
|
||||
|
||||
single = key:$[a-z]i+ {
|
||||
params[key] = true;
|
||||
};
|
||||
|
||||
name = "#" + match:$.* {
|
||||
return decodeURIComponent(match);
|
||||
}
|
||||
`;
|
||||
let parser: any;
|
||||
export default function getParser() {
|
||||
if (!parser) {
|
||||
parser = peggy.generate(grammars);
|
||||
}
|
||||
return parser;
|
||||
}
|
990
src/utils/uri-parser.ts
Normal file
990
src/utils/uri-parser.ts
Normal file
|
@ -0,0 +1,990 @@
|
|||
import getTrojanURIParser from "@/utils/trojan-uri";
|
||||
|
||||
export default function parseUri(uri: string): IProxyConfig {
|
||||
const head = uri.split("://")[0];
|
||||
switch (head) {
|
||||
case "ss":
|
||||
return URI_SS(uri);
|
||||
case "ssr":
|
||||
return URI_SSR(uri);
|
||||
case "vmess":
|
||||
return URI_VMESS(uri);
|
||||
case "vless":
|
||||
return URI_VLESS(uri);
|
||||
case "trojan":
|
||||
return URI_Trojan(uri);
|
||||
case "hysteria2":
|
||||
return URI_Hysteria2(uri);
|
||||
case "hy2":
|
||||
return URI_Hysteria2(uri);
|
||||
case "hysteria":
|
||||
return URI_Hysteria(uri);
|
||||
case "hy":
|
||||
return URI_Hysteria(uri);
|
||||
case "tuic":
|
||||
return URI_TUIC(uri);
|
||||
case "wireguard":
|
||||
return URI_Wireguard(uri);
|
||||
case "wg":
|
||||
return URI_Wireguard(uri);
|
||||
case "http":
|
||||
return URI_HTTP(uri);
|
||||
case "socks5":
|
||||
return URI_SOCKS(uri);
|
||||
default:
|
||||
throw Error(`Unknown uri type: ${head}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getIfNotBlank(
|
||||
value: string | undefined,
|
||||
dft?: string
|
||||
): string | undefined {
|
||||
return value && value.trim() !== "" ? value : dft;
|
||||
}
|
||||
|
||||
function getIfPresent(value: any, dft?: any): any {
|
||||
return value ? value : dft;
|
||||
}
|
||||
|
||||
function isPresent(value: any): boolean {
|
||||
return value !== null && value !== undefined;
|
||||
}
|
||||
|
||||
function isNotBlank(name: string) {
|
||||
return name.trim().length !== 0;
|
||||
}
|
||||
|
||||
function isIPv4(address: string): boolean {
|
||||
// Check if the address is IPv4
|
||||
const ipv4Regex = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/;
|
||||
return ipv4Regex.test(address);
|
||||
}
|
||||
|
||||
function isIPv6(address: string): boolean {
|
||||
// Check if the address is IPv6
|
||||
const ipv6Regex =
|
||||
/^((?=.*(::))(?!.*\3.+)(::)?)([0-9A-Fa-f]{1,4}(\3|:\b)|\3){7}[0-9A-Fa-f]{1,4}$/;
|
||||
return ipv6Regex.test(address);
|
||||
}
|
||||
|
||||
function decodeBase64OrOriginal(str: string): string {
|
||||
try {
|
||||
return atob(str);
|
||||
} catch {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
function URI_SS(line: string): IProxyConfig {
|
||||
// parse url
|
||||
let content = line.split("ss://")[1];
|
||||
|
||||
const proxy: IProxyConfig = {
|
||||
name: decodeURIComponent(line.split("#")[1]),
|
||||
type: "ss",
|
||||
server: "",
|
||||
port: 0,
|
||||
};
|
||||
content = content.split("#")[0]; // strip proxy name
|
||||
// handle IPV4 and IPV6
|
||||
let serverAndPortArray = content.match(/@([^/]*)(\/|$)/);
|
||||
let userInfoStr = decodeBase64OrOriginal(content.split("@")[0]);
|
||||
let query = "";
|
||||
if (!serverAndPortArray) {
|
||||
if (content.includes("?")) {
|
||||
const parsed = content.match(/^(.*)(\?.*)$/);
|
||||
content = parsed?.[1] ?? "";
|
||||
query = parsed?.[2] ?? "";
|
||||
}
|
||||
content = decodeBase64OrOriginal(content);
|
||||
if (query) {
|
||||
if (/(&|\?)v2ray-plugin=/.test(query)) {
|
||||
const parsed = query.match(/(&|\?)v2ray-plugin=(.*?)(&|$)/);
|
||||
let v2rayPlugin = parsed![2];
|
||||
if (v2rayPlugin) {
|
||||
proxy.plugin = "v2ray-plugin";
|
||||
proxy["plugin-opts"] = JSON.parse(
|
||||
decodeBase64OrOriginal(v2rayPlugin)
|
||||
);
|
||||
}
|
||||
}
|
||||
content = `${content}${query}`;
|
||||
}
|
||||
userInfoStr = content.split("@")[0];
|
||||
serverAndPortArray = content.match(/@([^/]*)(\/|$)/);
|
||||
}
|
||||
const serverAndPort = serverAndPortArray?.[1];
|
||||
const portIdx = serverAndPort?.lastIndexOf(":") ?? 0;
|
||||
proxy.server = serverAndPort?.substring(0, portIdx) ?? "";
|
||||
proxy.port = parseInt(
|
||||
`${serverAndPort?.substring(portIdx + 1)}`.match(/\d+/)?.[0] ?? ""
|
||||
);
|
||||
console.log(userInfoStr);
|
||||
const userInfo = userInfoStr.match(/(^.*?):(.*$)/);
|
||||
console.log(userInfo);
|
||||
proxy.cipher = userInfo?.[1];
|
||||
proxy.password = userInfo?.[2];
|
||||
|
||||
// handle obfs
|
||||
const idx = content.indexOf("?plugin=");
|
||||
if (idx !== -1) {
|
||||
const pluginInfo = (
|
||||
"plugin=" + decodeURIComponent(content.split("?plugin=")[1].split("&")[0])
|
||||
).split(";");
|
||||
const params: Record<string, any> = {};
|
||||
for (const item of pluginInfo) {
|
||||
const [key, val] = item.split("=");
|
||||
if (key) params[key] = val || true; // some options like "tls" will not have value
|
||||
}
|
||||
switch (params.plugin) {
|
||||
case "obfs-local":
|
||||
case "simple-obfs":
|
||||
proxy.plugin = "obfs";
|
||||
proxy["plugin-opts"] = {
|
||||
mode: params.obfs,
|
||||
host: getIfNotBlank(params["obfs-host"]),
|
||||
};
|
||||
break;
|
||||
case "v2ray-plugin":
|
||||
proxy.plugin = "v2ray-plugin";
|
||||
proxy["plugin-opts"] = {
|
||||
mode: "websocket",
|
||||
host: getIfNotBlank(params["obfs-host"]),
|
||||
path: getIfNotBlank(params.path),
|
||||
tls: getIfPresent(params.tls),
|
||||
};
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unsupported plugin option: ${params.plugin}`);
|
||||
}
|
||||
}
|
||||
if (/(&|\?)uot=(1|true)/i.test(query)) {
|
||||
proxy["udp-over-tcp"] = true;
|
||||
}
|
||||
if (/(&|\?)tfo=(1|true)/i.test(query)) {
|
||||
proxy.tfo = true;
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
|
||||
function URI_SSR(line: string): IProxyConfig {
|
||||
line = decodeBase64OrOriginal(line.split("ssr://")[1]);
|
||||
|
||||
// handle IPV6 & IPV4 format
|
||||
let splitIdx = line.indexOf(":origin");
|
||||
if (splitIdx === -1) {
|
||||
splitIdx = line.indexOf(":auth_");
|
||||
}
|
||||
const serverAndPort = line.substring(0, splitIdx);
|
||||
const server = serverAndPort.substring(0, serverAndPort.lastIndexOf(":"));
|
||||
const port = parseInt(
|
||||
serverAndPort.substring(serverAndPort.lastIndexOf(":") + 1)
|
||||
);
|
||||
|
||||
let params = line
|
||||
.substring(splitIdx + 1)
|
||||
.split("/?")[0]
|
||||
.split(":");
|
||||
let proxy: IProxyConfig = {
|
||||
name: "",
|
||||
type: "ssr",
|
||||
server,
|
||||
port,
|
||||
protocol: params[0],
|
||||
cipher: params[1],
|
||||
obfs: params[2],
|
||||
password: decodeBase64OrOriginal(params[3]),
|
||||
};
|
||||
|
||||
// get other params
|
||||
const other_params: Record<string, string> = {};
|
||||
const paramsArray = line.split("/?")[1]?.split("&") || [];
|
||||
for (const item of paramsArray) {
|
||||
const [key, val] = item.split("=");
|
||||
if (val?.trim().length > 0) {
|
||||
other_params[key] = val.trim();
|
||||
}
|
||||
}
|
||||
|
||||
proxy = {
|
||||
...proxy,
|
||||
name: other_params.remarks
|
||||
? decodeBase64OrOriginal(other_params.remarks)
|
||||
: proxy.server,
|
||||
"protocol-param": getIfNotBlank(
|
||||
decodeBase64OrOriginal(other_params.protoparam || "").replace(/\s/g, "")
|
||||
),
|
||||
"obfs-param": getIfNotBlank(
|
||||
decodeBase64OrOriginal(other_params.obfsparam || "").replace(/\s/g, "")
|
||||
),
|
||||
};
|
||||
return proxy;
|
||||
}
|
||||
|
||||
function URI_VMESS(line: string): IProxyConfig {
|
||||
line = line.split("vmess://")[1];
|
||||
let content = decodeBase64OrOriginal(line);
|
||||
console.log(content);
|
||||
if (/=\s*vmess/.test(content)) {
|
||||
// Quantumult VMess URI format
|
||||
const partitions = content.split(",").map((p) => p.trim());
|
||||
console.log(partitions);
|
||||
const params: Record<string, string> = {};
|
||||
for (const part of partitions) {
|
||||
if (part.indexOf("=") !== -1) {
|
||||
const [key, val] = part.split("=");
|
||||
params[key.trim()] = val.trim();
|
||||
}
|
||||
}
|
||||
|
||||
const proxy: IProxyConfig = {
|
||||
name: partitions[0].split("=")[0].trim(),
|
||||
type: "vmess",
|
||||
server: partitions[1],
|
||||
port: parseInt(partitions[2], 10),
|
||||
cipher: getIfNotBlank(partitions[3], "auto"),
|
||||
uuid: partitions[4].match(/^"(.*)"$/)?.[1] || "",
|
||||
tls: params.obfs === "wss",
|
||||
udp: getIfPresent(params["udp-relay"]),
|
||||
tfo: getIfPresent(params["fast-open"]),
|
||||
"skip-cert-verify": isPresent(params["tls-verification"])
|
||||
? !params["tls-verification"]
|
||||
: undefined,
|
||||
};
|
||||
|
||||
if (isPresent(params.obfs)) {
|
||||
if (params.obfs === "ws" || params.obfs === "wss") {
|
||||
proxy.network = "ws";
|
||||
proxy["ws-opts"] = {
|
||||
path:
|
||||
(getIfNotBlank(params["obfs-path"]) || '"/"').match(
|
||||
/^"(.*)"$/
|
||||
)?.[1] || "/",
|
||||
headers: {
|
||||
Host:
|
||||
params["obfs-header"]?.match(/Host:\s*([a-zA-Z0-9-.]*)/)?.[1] ||
|
||||
"",
|
||||
},
|
||||
};
|
||||
} else {
|
||||
throw new Error(`Unsupported obfs: ${params.obfs}`);
|
||||
}
|
||||
}
|
||||
|
||||
return proxy;
|
||||
} else {
|
||||
let params: Record<string, any> = {};
|
||||
|
||||
try {
|
||||
// V2rayN URI format
|
||||
params = JSON.parse(content);
|
||||
} catch (e) {
|
||||
// Shadowrocket URI format
|
||||
const match = /(^[^?]+?)\/?\?(.*)$/.exec(line);
|
||||
if (match) {
|
||||
let [_, base64Line, qs] = match;
|
||||
content = decodeBase64OrOriginal(base64Line);
|
||||
|
||||
for (const addon of qs.split("&")) {
|
||||
const [key, valueRaw] = addon.split("=");
|
||||
const value = decodeURIComponent(valueRaw);
|
||||
if (value.indexOf(",") === -1) {
|
||||
params[key] = value;
|
||||
} else {
|
||||
params[key] = value.split(",");
|
||||
}
|
||||
}
|
||||
|
||||
const contentMatch = /(^[^:]+?):([^:]+?)@(.*):(\d+)$/.exec(content);
|
||||
|
||||
if (contentMatch) {
|
||||
let [__, cipher, uuid, server, port] = contentMatch;
|
||||
|
||||
params.scy = cipher;
|
||||
params.id = uuid;
|
||||
params.port = port;
|
||||
params.add = server;
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(params);
|
||||
const server = params.add;
|
||||
const port = parseInt(getIfPresent(params.port), 10);
|
||||
const proxy: IProxyConfig = {
|
||||
name:
|
||||
params.ps ??
|
||||
params.remarks ??
|
||||
params.remark ??
|
||||
`VMess ${server}:${port}`,
|
||||
type: "vmess",
|
||||
server,
|
||||
port,
|
||||
cipher: getIfPresent(params.scy, "auto"),
|
||||
uuid: params.id,
|
||||
alterId: parseInt(getIfPresent(params.aid ?? params.alterId, 0), 10),
|
||||
tls: ["tls", true, 1, "1"].includes(params.tls),
|
||||
"skip-cert-verify": isPresent(params.verify_cert)
|
||||
? !params.verify_cert
|
||||
: undefined,
|
||||
};
|
||||
|
||||
if (proxy.tls && params.sni) {
|
||||
proxy.sni = params.sni;
|
||||
}
|
||||
|
||||
let httpupgrade = false;
|
||||
if (params.net === "ws" || params.obfs === "websocket") {
|
||||
proxy.network = "ws";
|
||||
} else if (
|
||||
["http"].includes(params.net) ||
|
||||
["http"].includes(params.obfs) ||
|
||||
["http"].includes(params.type)
|
||||
) {
|
||||
proxy.network = "http";
|
||||
} else if (["grpc"].includes(params.net)) {
|
||||
proxy.network = "grpc";
|
||||
} else if (params.net === "httpupgrade") {
|
||||
proxy.network = "ws";
|
||||
httpupgrade = true;
|
||||
} else if (params.net === "h2" || proxy.network === "h2") {
|
||||
proxy.network = "h2";
|
||||
}
|
||||
|
||||
if (proxy.network) {
|
||||
let transportHost = params.host ?? params.obfsParam;
|
||||
try {
|
||||
const parsedObfs = JSON.parse(transportHost);
|
||||
const parsedHost = parsedObfs?.Host;
|
||||
if (parsedHost) {
|
||||
transportHost = parsedHost;
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore JSON parse errors
|
||||
}
|
||||
|
||||
let transportPath = params.path;
|
||||
if (proxy.network === "http") {
|
||||
if (transportHost) {
|
||||
transportHost = Array.isArray(transportHost)
|
||||
? transportHost[0]
|
||||
: transportHost;
|
||||
}
|
||||
if (transportPath) {
|
||||
transportPath = Array.isArray(transportPath)
|
||||
? transportPath[0]
|
||||
: transportPath;
|
||||
} else {
|
||||
transportPath = "/";
|
||||
}
|
||||
}
|
||||
|
||||
if (transportPath || transportHost) {
|
||||
if (["grpc"].includes(proxy.network)) {
|
||||
proxy[`grpc-opts`] = {
|
||||
"grpc-service-name": getIfNotBlank(transportPath),
|
||||
"_grpc-type": getIfNotBlank(params.type),
|
||||
};
|
||||
} else {
|
||||
const opts: Record<string, any> = {
|
||||
path: getIfNotBlank(transportPath),
|
||||
headers: { Host: getIfNotBlank(transportHost) },
|
||||
};
|
||||
if (httpupgrade) {
|
||||
opts["v2ray-http-upgrade"] = true;
|
||||
opts["v2ray-http-upgrade-fast-open"] = true;
|
||||
}
|
||||
proxy[`${proxy.network}-opts`] = opts;
|
||||
}
|
||||
} else {
|
||||
delete proxy.network;
|
||||
}
|
||||
|
||||
if (proxy.tls && !proxy.sni && transportHost) {
|
||||
proxy.sni = transportHost;
|
||||
}
|
||||
}
|
||||
|
||||
return proxy;
|
||||
}
|
||||
}
|
||||
|
||||
function URI_VLESS(line: string): IProxyConfig {
|
||||
line = line.split("vless://")[1];
|
||||
let isShadowrocket;
|
||||
let parsed = /^(.*?)@(.*?):(\d+)\/?(\?(.*?))?(?:#(.*?))?$/.exec(line)!;
|
||||
if (!parsed) {
|
||||
let [_, base64, other] = /^(.*?)(\?.*?$)/.exec(line)!;
|
||||
line = `${atob(base64)}${other}`;
|
||||
parsed = /^(.*?)@(.*?):(\d+)\/?(\?(.*?))?(?:#(.*?))?$/.exec(line)!;
|
||||
isShadowrocket = true;
|
||||
}
|
||||
let [__, uuid, server, portStr, ___, addons = "", name] = parsed;
|
||||
if (isShadowrocket) {
|
||||
uuid = uuid.replace(/^.*?:/g, "");
|
||||
}
|
||||
|
||||
const port = parseInt(portStr, 10);
|
||||
uuid = decodeURIComponent(uuid);
|
||||
name = decodeURIComponent(name);
|
||||
|
||||
const proxy: IProxyConfig = {
|
||||
type: "vless",
|
||||
name,
|
||||
server,
|
||||
port,
|
||||
uuid,
|
||||
};
|
||||
|
||||
const params: Record<string, string> = {};
|
||||
for (const addon of addons.split("&")) {
|
||||
const [key, valueRaw] = addon.split("=");
|
||||
const value = decodeURIComponent(valueRaw);
|
||||
params[key] = value;
|
||||
}
|
||||
|
||||
proxy.name =
|
||||
name ?? params.remarks ?? params.remark ?? `VLESS ${server}:${port}`;
|
||||
|
||||
proxy.tls = (params.security && params.security !== "none") || undefined;
|
||||
if (isShadowrocket && /TRUE|1/i.test(params.tls)) {
|
||||
proxy.tls = true;
|
||||
params.security = params.security ?? "reality";
|
||||
}
|
||||
proxy.sni = params.sni || params.peer;
|
||||
proxy.flow = params.flow ? "xtls-rprx-vision" : undefined;
|
||||
|
||||
proxy["client-fingerprint"] = params.fp as
|
||||
| "chrome"
|
||||
| "firefox"
|
||||
| "safari"
|
||||
| "iOS"
|
||||
| "android"
|
||||
| "edge"
|
||||
| "360"
|
||||
| "qq"
|
||||
| "random";
|
||||
proxy.alpn = params.alpn ? params.alpn.split(",") : undefined;
|
||||
proxy["skip-cert-verify"] = /(TRUE)|1/i.test(params.allowInsecure);
|
||||
|
||||
if (["reality"].includes(params.security)) {
|
||||
const opts: IProxyConfig["reality-opts"] = {};
|
||||
if (params.pbk) {
|
||||
opts["public-key"] = params.pbk;
|
||||
}
|
||||
if (params.sid) {
|
||||
opts["short-id"] = params.sid;
|
||||
}
|
||||
if (Object.keys(opts).length > 0) {
|
||||
proxy["reality-opts"] = opts;
|
||||
}
|
||||
}
|
||||
|
||||
let httpupgrade = false;
|
||||
proxy.ws = {
|
||||
headers: undefined,
|
||||
"ws-service-name": undefined,
|
||||
path: undefined,
|
||||
};
|
||||
proxy.http = {
|
||||
headers: undefined,
|
||||
"http-service-name": undefined,
|
||||
path: undefined,
|
||||
};
|
||||
proxy.grpc = { "_grpc-type": undefined };
|
||||
proxy.network = params.type as "ws" | "http" | "h2" | "grpc";
|
||||
if (params.headerType === "http") {
|
||||
proxy.network = "http";
|
||||
} else {
|
||||
proxy.network = "ws";
|
||||
httpupgrade = true;
|
||||
}
|
||||
if (!proxy.network && isShadowrocket && params.obfs) {
|
||||
proxy.network = params.obfs as "ws" | "http" | "h2" | "grpc";
|
||||
}
|
||||
if (["websocket"].includes(proxy.network)) {
|
||||
proxy.network = "ws";
|
||||
}
|
||||
if (proxy.network && !["tcp", "none"].includes(proxy.network)) {
|
||||
const opts: Record<string, any> = {};
|
||||
const host = params.host ?? params.obfsParam;
|
||||
if (host) {
|
||||
if (params.obfsParam) {
|
||||
try {
|
||||
const parsed = JSON.parse(host);
|
||||
opts.headers = parsed;
|
||||
} catch (e) {
|
||||
opts.headers = { Host: host };
|
||||
}
|
||||
} else {
|
||||
opts.headers = { Host: host };
|
||||
}
|
||||
}
|
||||
if (params.serviceName) {
|
||||
opts[`${proxy.network}-service-name`] = params.serviceName;
|
||||
} else if (isShadowrocket && params.path) {
|
||||
if (!["ws", "http", "h2"].includes(proxy.network)) {
|
||||
opts[`${proxy.network}-service-name`] = params.path;
|
||||
delete params.path;
|
||||
}
|
||||
}
|
||||
if (params.path) {
|
||||
opts.path = params.path;
|
||||
}
|
||||
if (["grpc"].includes(proxy.network)) {
|
||||
opts["_grpc-type"] = params.mode || "gun";
|
||||
}
|
||||
if (httpupgrade) {
|
||||
opts["v2ray-http-upgrade"] = true;
|
||||
opts["v2ray-http-upgrade-fast-open"] = true;
|
||||
}
|
||||
if (Object.keys(opts).length > 0) {
|
||||
proxy[`${proxy.network}-opts`] = opts;
|
||||
}
|
||||
}
|
||||
|
||||
if (proxy.tls && !proxy.sni) {
|
||||
if (proxy.network === "ws") {
|
||||
proxy.sni = proxy.ws?.headers?.Host;
|
||||
} else if (proxy.network === "http") {
|
||||
let httpHost = proxy.http?.headers?.Host;
|
||||
proxy.sni = Array.isArray(httpHost) ? httpHost[0] : httpHost;
|
||||
}
|
||||
}
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
function URI_Trojan(line: string): IProxyConfig {
|
||||
let [newLine, name] = line.split(/#(.+)/, 2);
|
||||
const parser = getTrojanURIParser();
|
||||
const proxy = parser.parse(newLine);
|
||||
if (isNotBlank(name)) {
|
||||
try {
|
||||
proxy.name = decodeURIComponent(name);
|
||||
} catch (e) {
|
||||
throw Error("Can not get proxy name");
|
||||
}
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
|
||||
function URI_Hysteria2(line: string): IProxyConfig {
|
||||
line = line.split(/(hysteria2|hy2):\/\//)[2];
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
let [__, password, server, ___, port, ____, addons = "", name] =
|
||||
/^(.*?)@(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line) || [];
|
||||
let portNum = parseInt(`${port}`, 10);
|
||||
if (isNaN(portNum)) {
|
||||
portNum = 443;
|
||||
}
|
||||
password = decodeURIComponent(password);
|
||||
if (name != null) {
|
||||
name = decodeURIComponent(name);
|
||||
}
|
||||
name = name ?? `Hysteria2 ${server}:${port}`;
|
||||
|
||||
const proxy: IProxyConfig = {
|
||||
type: "hysteria2",
|
||||
name,
|
||||
server,
|
||||
port: portNum,
|
||||
password,
|
||||
};
|
||||
|
||||
const params: Record<string, string> = {};
|
||||
for (const addon of addons.split("&")) {
|
||||
const [key, valueRaw] = addon.split("=");
|
||||
let value = valueRaw;
|
||||
value = decodeURIComponent(valueRaw);
|
||||
params[key] = value;
|
||||
}
|
||||
|
||||
proxy.sni = params.sni;
|
||||
if (!proxy.sni && params.peer) {
|
||||
proxy.sni = params.peer;
|
||||
}
|
||||
if (params.obfs && params.obfs !== "none") {
|
||||
proxy.obfs = params.obfs;
|
||||
}
|
||||
|
||||
proxy.ports = params.mport;
|
||||
proxy["obfs-password"] = params["obfs-password"];
|
||||
proxy["skip-cert-verify"] = /(TRUE)|1/i.test(params.insecure);
|
||||
proxy.tfo = /(TRUE)|1/i.test(params.fastopen);
|
||||
proxy["tls-fingerprint"] = params.pinSHA256;
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
function URI_Hysteria(line: string): IProxyConfig {
|
||||
line = line.split(/(hysteria|hy):\/\//)[2];
|
||||
let [__, server, ___, port, ____, addons = "", name] =
|
||||
/^(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line)!;
|
||||
let portNum = parseInt(`${port}`, 10);
|
||||
if (isNaN(portNum)) {
|
||||
portNum = 443;
|
||||
}
|
||||
if (name != null) {
|
||||
name = decodeURIComponent(name);
|
||||
}
|
||||
name = name ?? `Hysteria ${server}:${port}`;
|
||||
|
||||
const proxy: IProxyConfig = {
|
||||
type: "hysteria",
|
||||
name,
|
||||
server,
|
||||
port: portNum,
|
||||
};
|
||||
const params: Record<string, string> = {};
|
||||
|
||||
for (const addon of addons.split("&")) {
|
||||
let [key, value] = addon.split("=");
|
||||
key = key.replace(/_/, "-");
|
||||
value = decodeURIComponent(value);
|
||||
switch (key) {
|
||||
case "alpn":
|
||||
proxy["alpn"] = value ? value.split(",") : undefined;
|
||||
break;
|
||||
case "insecure":
|
||||
proxy["skip-cert-verify"] = /(TRUE)|1/i.test(value);
|
||||
break;
|
||||
case "auth":
|
||||
proxy["auth-str"] = value;
|
||||
break;
|
||||
case "mport":
|
||||
proxy["ports"] = value;
|
||||
break;
|
||||
case "obfsParam":
|
||||
proxy["obfs"] = value;
|
||||
break;
|
||||
case "upmbps":
|
||||
proxy["up"] = value;
|
||||
break;
|
||||
case "downmbps":
|
||||
proxy["down"] = value;
|
||||
break;
|
||||
case "obfs":
|
||||
proxy["obfs"] = value || "";
|
||||
break;
|
||||
case "fast-open":
|
||||
proxy["fast-open"] = /(TRUE)|1/i.test(value);
|
||||
break;
|
||||
case "peer":
|
||||
proxy["fast-open"] = /(TRUE)|1/i.test(value);
|
||||
break;
|
||||
case "recv-window-conn":
|
||||
proxy["recv-window-conn"] = parseInt(value);
|
||||
break;
|
||||
case "recv-window":
|
||||
proxy["recv-window"] = parseInt(value);
|
||||
break;
|
||||
case "ca":
|
||||
proxy["ca"] = value;
|
||||
break;
|
||||
case "ca-str":
|
||||
proxy["ca-str"] = value;
|
||||
break;
|
||||
case "disable-mtu-discovery":
|
||||
proxy["disable-mtu-discovery"] = /(TRUE)|1/i.test(value);
|
||||
break;
|
||||
case "fingerprint":
|
||||
proxy["fingerprint"] = value;
|
||||
break;
|
||||
case "protocol":
|
||||
proxy["protocol"] = value;
|
||||
case "sni":
|
||||
proxy["sni"] = value;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!proxy.sni && params.peer) {
|
||||
proxy.sni = params.peer;
|
||||
}
|
||||
if (!proxy["fast-open"] && params["fast-open"]) {
|
||||
proxy["fast-open"] = true;
|
||||
}
|
||||
if (!proxy.protocol) {
|
||||
proxy.protocol = "udp";
|
||||
}
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
function URI_TUIC(line: string): IProxyConfig {
|
||||
line = line.split(/tuic:\/\//)[1];
|
||||
|
||||
let [__, uuid, password, server, ___, port, ____, addons = "", name] =
|
||||
/^(.*?):(.*?)@(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line) || [];
|
||||
|
||||
let portNum = parseInt(`${port}`, 10);
|
||||
if (isNaN(portNum)) {
|
||||
portNum = 443;
|
||||
}
|
||||
password = decodeURIComponent(password);
|
||||
if (name != null) {
|
||||
name = decodeURIComponent(name);
|
||||
}
|
||||
name = name ?? `TUIC ${server}:${port}`;
|
||||
|
||||
const proxy: IProxyConfig = {
|
||||
type: "tuic",
|
||||
name,
|
||||
server,
|
||||
port: portNum,
|
||||
password,
|
||||
uuid,
|
||||
};
|
||||
|
||||
for (const addon of addons.split("&")) {
|
||||
let [key, value] = addon.split("=");
|
||||
key = key.replace(/_/, "-");
|
||||
value = decodeURIComponent(value);
|
||||
switch (key) {
|
||||
case "token":
|
||||
proxy["token"] = value;
|
||||
break;
|
||||
case "ip":
|
||||
proxy["ip"] = value;
|
||||
break;
|
||||
case "heartbeat-interval":
|
||||
proxy["heartbeat-interval"] = parseInt(value);
|
||||
break;
|
||||
case "alpn":
|
||||
proxy["alpn"] = value ? value.split(",") : undefined;
|
||||
break;
|
||||
case "disable-sni":
|
||||
proxy["disable-sni"] = /(TRUE)|1/i.test(value);
|
||||
break;
|
||||
case "reduce-rtt":
|
||||
proxy["reduce-rtt"] = /(TRUE)|1/i.test(value);
|
||||
break;
|
||||
case "request-timeout":
|
||||
proxy["request-timeout"] = parseInt(value);
|
||||
break;
|
||||
case "udp-relay-mode":
|
||||
proxy["udp-relay-mode"] = value;
|
||||
break;
|
||||
case "congestion-controller":
|
||||
proxy["congestion-controller"] = value;
|
||||
break;
|
||||
case "max-udp-relay-packet-size":
|
||||
proxy["max-udp-relay-packet-size"] = parseInt(value);
|
||||
break;
|
||||
case "fast-open":
|
||||
proxy["fast-open"] = /(TRUE)|1/i.test(value);
|
||||
break;
|
||||
case "skip-cert-verify":
|
||||
proxy["skip-cert-verify"] = /(TRUE)|1/i.test(value);
|
||||
break;
|
||||
case "max-open-streams":
|
||||
proxy["max-open-streams"] = parseInt(value);
|
||||
break;
|
||||
case "sni":
|
||||
proxy["sni"] = value;
|
||||
break;
|
||||
case "allow-insecure":
|
||||
proxy["skip-cert-verify"] = /(TRUE)|1/i.test(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
function URI_Wireguard(line: string): IProxyConfig {
|
||||
line = line.split(/(wireguard|wg):\/\//)[2];
|
||||
let [__, ___, privateKey, server, ____, port, _____, addons = "", name] =
|
||||
/^((.*?)@)?(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line)!;
|
||||
|
||||
let portNum = parseInt(`${port}`, 10);
|
||||
if (isNaN(portNum)) {
|
||||
portNum = 443;
|
||||
}
|
||||
privateKey = decodeURIComponent(privateKey);
|
||||
if (name != null) {
|
||||
name = decodeURIComponent(name);
|
||||
}
|
||||
name = name ?? `WireGuard ${server}:${port}`;
|
||||
const proxy: IProxyConfig = {
|
||||
type: "wireguard",
|
||||
name,
|
||||
server,
|
||||
port: portNum,
|
||||
"private-key": privateKey,
|
||||
udp: true,
|
||||
};
|
||||
for (const addon of addons.split("&")) {
|
||||
let [key, value] = addon.split("=");
|
||||
key = key.replace(/_/, "-");
|
||||
value = decodeURIComponent(value);
|
||||
switch (key) {
|
||||
case "address":
|
||||
case "ip":
|
||||
value.split(",").map((i) => {
|
||||
const ip = i
|
||||
.trim()
|
||||
.replace(/\/\d+$/, "")
|
||||
.replace(/^\[/, "")
|
||||
.replace(/\]$/, "");
|
||||
if (isIPv4(ip)) {
|
||||
proxy.ip = ip;
|
||||
} else if (isIPv6(ip)) {
|
||||
proxy.ipv6 = ip;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "publickey":
|
||||
proxy["public-key"] = value;
|
||||
break;
|
||||
case "allowed-ips":
|
||||
proxy["allowed-ips"] = value.split(",");
|
||||
break;
|
||||
case "pre-shared-key":
|
||||
proxy["pre-shared-key"] = value;
|
||||
break;
|
||||
case "reserved":
|
||||
const parsed = value
|
||||
.split(",")
|
||||
.map((i) => parseInt(i.trim(), 10))
|
||||
.filter((i) => Number.isInteger(i));
|
||||
if (parsed.length === 3) {
|
||||
proxy["reserved"] = parsed;
|
||||
}
|
||||
break;
|
||||
case "udp":
|
||||
proxy["udp"] = /(TRUE)|1/i.test(value);
|
||||
break;
|
||||
case "mtu":
|
||||
proxy.mtu = parseInt(value.trim(), 10);
|
||||
break;
|
||||
case "dialer-proxy":
|
||||
proxy["dialer-proxy"] = value;
|
||||
break;
|
||||
case "remote-dns-resolve":
|
||||
proxy["remote-dns-resolve"] = /(TRUE)|1/i.test(value);
|
||||
break;
|
||||
case "dns":
|
||||
proxy.dns = value.split(",");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
function URI_HTTP(line: string): IProxyConfig {
|
||||
line = line.split(/(http|https):\/\//)[2];
|
||||
let [__, ___, auth, server, ____, port, _____, addons = "", name] =
|
||||
/^((.*?)@)?(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line)!;
|
||||
|
||||
let portNum = parseInt(`${port}`, 10);
|
||||
if (isNaN(portNum)) {
|
||||
portNum = 443;
|
||||
}
|
||||
if (auth) {
|
||||
auth = decodeURIComponent(auth);
|
||||
}
|
||||
if (name != null) {
|
||||
name = decodeURIComponent(name);
|
||||
}
|
||||
name = name ?? `HTTP ${server}:${portNum}`;
|
||||
const proxy: IProxyConfig = {
|
||||
type: "http",
|
||||
name,
|
||||
server,
|
||||
port: portNum,
|
||||
};
|
||||
if (auth) {
|
||||
const [username, password] = auth.split(":");
|
||||
proxy.username = username;
|
||||
proxy.password = password;
|
||||
}
|
||||
|
||||
for (const addon of addons.split("&")) {
|
||||
let [key, value] = addon.split("=");
|
||||
key = key.replace(/_/, "-");
|
||||
value = decodeURIComponent(value);
|
||||
switch (key) {
|
||||
case "tls":
|
||||
proxy.tls = /(TRUE)|1/i.test(value);
|
||||
break;
|
||||
case "fingerprint":
|
||||
proxy["fingerprint"] = value;
|
||||
break;
|
||||
case "skip-cert-verify":
|
||||
proxy["skip-cert-verify"] = /(TRUE)|1/i.test(value);
|
||||
break;
|
||||
case "udp":
|
||||
proxy["udp"] = /(TRUE)|1/i.test(value);
|
||||
break;
|
||||
case "ip-version":
|
||||
proxy["ip-version"] = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
function URI_SOCKS(line: string): IProxyConfig {
|
||||
line = line.split(/socks5:\/\//)[1];
|
||||
let [__, ___, auth, server, ____, port, _____, addons = "", name] =
|
||||
/^((.*?)@)?(.*?)(:(\d+))?\/?(\?(.*?))?(?:#(.*?))?$/.exec(line)!;
|
||||
|
||||
let portNum = parseInt(`${port}`, 10);
|
||||
if (isNaN(portNum)) {
|
||||
portNum = 443;
|
||||
}
|
||||
if (auth) {
|
||||
auth = decodeURIComponent(auth);
|
||||
}
|
||||
if (name != null) {
|
||||
name = decodeURIComponent(name);
|
||||
}
|
||||
name = name ?? `SOCKS5 ${server}:${portNum}`;
|
||||
const proxy: IProxyConfig = {
|
||||
type: "socks5",
|
||||
name,
|
||||
server,
|
||||
port: portNum,
|
||||
};
|
||||
if (auth) {
|
||||
const [username, password] = auth.split(":");
|
||||
proxy.username = username;
|
||||
proxy.password = password;
|
||||
}
|
||||
|
||||
for (const addon of addons.split("&")) {
|
||||
let [key, value] = addon.split("=");
|
||||
key = key.replace(/_/, "-");
|
||||
value = decodeURIComponent(value);
|
||||
switch (key) {
|
||||
case "tls":
|
||||
proxy.tls = /(TRUE)|1/i.test(value);
|
||||
break;
|
||||
case "fingerprint":
|
||||
proxy["fingerprint"] = value;
|
||||
break;
|
||||
case "skip-cert-verify":
|
||||
proxy["skip-cert-verify"] = /(TRUE)|1/i.test(value);
|
||||
break;
|
||||
case "udp":
|
||||
proxy["udp"] = /(TRUE)|1/i.test(value);
|
||||
break;
|
||||
case "ip-version":
|
||||
proxy["ip-version"] = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return proxy;
|
||||
}
|
Loading…
Reference in New Issue
Block a user