mirror of
https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 00:52:40 +00:00
Compare commits
1202 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe804fcf2c | ||
|
|
c7c9b21f79 | ||
|
|
d84f761b5d | ||
|
|
b7d7346ef7 | ||
|
|
cbfe528c5e | ||
|
|
59ea2bd174 | ||
|
|
1c9ae2a079 | ||
|
|
22e39dc18f | ||
|
|
4276869033 | ||
|
|
c902ad8c87 | ||
|
|
10fe493162 | ||
|
|
3f9867512b | ||
|
|
8ab694efcc | ||
|
|
4977c4ac43 | ||
|
|
84144ad057 | ||
|
|
4c871964b0 | ||
|
|
b3d66d42ff | ||
|
|
65cdaa0946 | ||
|
|
a8135c7d6c | ||
|
|
d752690129 | ||
|
|
43f7ae4a9a | ||
|
|
9b3f76be19 | ||
|
|
7310d9bd66 | ||
|
|
d1551c5644 | ||
|
|
785a715ce2 | ||
|
|
86b828fd40 | ||
|
|
2a8db4a21c | ||
|
|
d0da52fff9 | ||
|
|
146c2d966f | ||
|
|
8059a23cf9 | ||
|
|
437c95925e | ||
|
|
6bbb3a1ccf | ||
|
|
dfaa02aa1a | ||
|
|
5b554b27e4 | ||
|
|
1d1ac03e44 | ||
|
|
9ce1ebb816 | ||
|
|
73fd36ceca | ||
|
|
4e1b776785 | ||
|
|
7e6476aecd | ||
|
|
e043a37b59 | ||
|
|
8b605e10a0 | ||
|
|
1945455ba6 | ||
|
|
427faed8d8 | ||
|
|
843cff42c4 | ||
|
|
80f44884fb | ||
|
|
6cfcd1c6e2 | ||
|
|
956fff1b98 | ||
|
|
bd2b3f3fdf | ||
|
|
2093baaedb | ||
|
|
1cd740aca0 | ||
|
|
0f1b61b5a7 | ||
|
|
88283211cb | ||
|
|
1874c20c6d | ||
|
|
4558ee8c91 | ||
|
|
cd2fd624db | ||
|
|
0c17e14d9f | ||
|
|
f0c0361174 | ||
|
|
fe8e64dfbf | ||
|
|
f962b41ccb | ||
|
|
75e4225d9c | ||
|
|
0b22492fbc | ||
|
|
7d35911f7f | ||
|
|
960706b1cc | ||
|
|
ebb5ef68cb | ||
|
|
7f67f57031 | ||
|
|
9dabe7826a | ||
|
|
3daa85683b | ||
|
|
1adb27bbcf | ||
|
|
93bbb3b345 | ||
|
|
0f10978b65 | ||
|
|
7029802b88 | ||
|
|
ecdbd01bc6 | ||
|
|
17a50cf0ec | ||
|
|
b70c689b61 | ||
|
|
dde77b5333 | ||
|
|
6285475873 | ||
|
|
f80a34d830 | ||
|
|
5473a9b627 | ||
|
|
4a19d5a0df | ||
|
|
8a4f510ca5 | ||
|
|
32d968bb8e | ||
|
|
edb1b9f6dc | ||
|
|
fa813a0040 | ||
|
|
db2be960ac | ||
|
|
e2ed426d96 | ||
|
|
6331cdd8cc | ||
|
|
36fc22d9da | ||
|
|
a9386242cd | ||
|
|
8a128ee31b | ||
|
|
c7f9326ef9 | ||
|
|
082b0a56ad | ||
|
|
e4e0c7e69c | ||
|
|
06acaa905a | ||
|
|
bd4ab1440a | ||
|
|
b4e24a9bc7 | ||
|
|
7c68c272ee | ||
|
|
a080d94f8d | ||
|
|
dd03f0a25f | ||
|
|
b677c01c3f | ||
|
|
96e6f63c88 | ||
|
|
2c479aceef | ||
|
|
6d488007f2 | ||
|
|
27fa8aca15 | ||
|
|
e87eaddb60 | ||
|
|
2805a0b477 | ||
|
|
d4378025f9 | ||
|
|
81a0cfdb4f | ||
|
|
32cdddd934 | ||
|
|
883e091930 | ||
|
|
9e1803e795 | ||
|
|
a75a845c24 | ||
|
|
ef7bfb11f7 | ||
|
|
63d5b3e6f7 | ||
|
|
5c98e6ac9f | ||
|
|
e320c258fa | ||
|
|
753523cdfe | ||
|
|
94c313e9b9 | ||
|
|
28b13dd7ac | ||
|
|
d7930bfc6b | ||
|
|
b95eb39694 | ||
|
|
5470bceee1 | ||
|
|
5c761a4137 | ||
|
|
1fe54a699a | ||
|
|
78037af70c | ||
|
|
54c9e66bab | ||
|
|
e7bd21cd34 | ||
|
|
422e161c5b | ||
|
|
2f7f9a93e2 | ||
|
|
bd0a2b3eeb | ||
|
|
515ba0183d | ||
|
|
2828122098 | ||
|
|
3ab0bfdca5 | ||
|
|
177ec57a81 | ||
|
|
32407e6071 | ||
|
|
fb4b606fb3 | ||
|
|
b27d348d94 | ||
|
|
49cd9762db | ||
|
|
109752b5c3 | ||
|
|
0092efd95c | ||
|
|
a53748ebda | ||
|
|
592dc36f81 | ||
|
|
cd51eaae0a | ||
|
|
bdca115229 | ||
|
|
ab632087aa | ||
|
|
a84c2cc6cd | ||
|
|
d28c8eb9c1 | ||
|
|
4cc562f48c | ||
|
|
a36de02496 | ||
|
|
47f09cb6b8 | ||
|
|
01ff77fa99 | ||
|
|
24c448b8ac | ||
|
|
e56bc1eed4 | ||
|
|
48550cb71c | ||
|
|
3d835c63d6 | ||
|
|
daaa4a70d4 | ||
|
|
6c2e5fe9ca | ||
|
|
879b728600 | ||
|
|
01c183c41a | ||
|
|
4234dc8b20 | ||
|
|
31053ca0d1 | ||
|
|
7233c7816f | ||
|
|
248fe8cdcc | ||
|
|
27ceb9f8ca | ||
|
|
7ec1a19ff8 | ||
|
|
2b4cefcc12 | ||
|
|
f92c4799d1 | ||
|
|
7459821d84 | ||
|
|
71a26c0750 | ||
|
|
012b580f5e | ||
|
|
08e6835c2d | ||
|
|
8ecfeed953 | ||
|
|
01f781a270 | ||
|
|
d3b282f864 | ||
|
|
28d930ad7e | ||
|
|
d4813ea124 | ||
|
|
a18ba58860 | ||
|
|
69d1f87249 | ||
|
|
d786601e30 | ||
|
|
857e736274 | ||
|
|
ea17ca6089 | ||
|
|
fe94b2e76d | ||
|
|
8228189476 | ||
|
|
121a2d3c52 | ||
|
|
5b46a25448 | ||
|
|
8bb19debdc | ||
|
|
0292124f4b | ||
|
|
f35ddb8160 | ||
|
|
597062b1ac | ||
|
|
7dcfb22911 | ||
|
|
c0e5ff2d48 | ||
|
|
9e47b1baf6 | ||
|
|
84875fdba5 | ||
|
|
eae9f0869c | ||
|
|
fcef8e755e | ||
|
|
fc66309a0f | ||
|
|
3a4de52c20 | ||
|
|
354c2c0b1b | ||
|
|
7403946d80 | ||
|
|
39f7355e02 | ||
|
|
f60731b36a | ||
|
|
6099c2bd97 | ||
|
|
bf2e80cf30 | ||
|
|
c4a682baba | ||
|
|
607420bb0d | ||
|
|
438a3a3db3 | ||
|
|
3eee8a5102 | ||
|
|
4ad7803511 | ||
|
|
b40d312141 | ||
|
|
b053482435 | ||
|
|
41e5964fa4 | ||
|
|
ab4d9b4c7e | ||
|
|
bf42d6ec02 | ||
|
|
856b6c1be0 | ||
|
|
46f8296667 | ||
|
|
75a53b23b8 | ||
|
|
1b08cb8ae1 | ||
|
|
9e6a147703 | ||
|
|
44ede4d7e7 | ||
|
|
bd44e81ed9 | ||
|
|
4d51172919 | ||
|
|
d51577aedb | ||
|
|
c1a7a313c7 | ||
|
|
e16f21b102 | ||
|
|
00bda4b257 | ||
|
|
4cf920438b | ||
|
|
e238a033dc | ||
|
|
221acdaf23 | ||
|
|
4331fd2138 | ||
|
|
c4f82642b9 | ||
|
|
9cf33887c6 | ||
|
|
40e651d301 | ||
|
|
1ff6de9979 | ||
|
|
5335fc17d8 | ||
|
|
451c6fa2ad | ||
|
|
11c9d20f65 | ||
|
|
816de94599 | ||
|
|
190f358b8f | ||
|
|
145fa83224 | ||
|
|
d659bdcfb5 | ||
|
|
7605ca39de | ||
|
|
f3862eb962 | ||
|
|
629f584e2f | ||
|
|
074026a997 | ||
|
|
6ba6365969 | ||
|
|
5dcd708f9c | ||
|
|
d15514f2df | ||
|
|
746c5975d7 | ||
|
|
a8391ad8e6 | ||
|
|
853c032872 | ||
|
|
5d60afb957 | ||
|
|
95a0f45c34 | ||
|
|
6e713f75f3 | ||
|
|
66bc15cea5 | ||
|
|
5026a74556 | ||
|
|
6d7255dc05 | ||
|
|
a10317844f | ||
|
|
a053f907fc | ||
|
|
58eaa91c66 | ||
|
|
d5d001c109 | ||
|
|
d92feb8c25 | ||
|
|
4dd42e8077 | ||
|
|
0d761c79ef | ||
|
|
3ff2ad5873 | ||
|
|
64d13d954d | ||
|
|
773cb16e91 | ||
|
|
3bf81767c0 | ||
|
|
57b9d97e1e | ||
|
|
facfdac06f | ||
|
|
e865459467 | ||
|
|
340e20b318 | ||
|
|
004e0a575d | ||
|
|
241be53cb5 | ||
|
|
314989aa55 | ||
|
|
a93f13d295 | ||
|
|
2bb19ae8be | ||
|
|
efd6e71c9b | ||
|
|
d52d0f50f3 | ||
|
|
d71d1296f8 | ||
|
|
deaa1630a1 | ||
|
|
63814b3c3c | ||
|
|
d66f4d5e54 | ||
|
|
21810be696 | ||
|
|
a1a522c472 | ||
|
|
62295895a9 | ||
|
|
ed5b395afc | ||
|
|
8219a09f53 | ||
|
|
b0117ca82d | ||
|
|
168c7f597a | ||
|
|
69a6480477 | ||
|
|
ac75b9e78b | ||
|
|
6d86235040 | ||
|
|
4b307067d0 | ||
|
|
d7e5a133c9 | ||
|
|
7c4b8fd8a5 | ||
|
|
b4ec6cfd0d | ||
|
|
94a609f08e | ||
|
|
ebcf1265e6 | ||
|
|
abf3c84cd4 | ||
|
|
c63d9a304e | ||
|
|
4a96716cd9 | ||
|
|
0b67abf4f6 | ||
|
|
2aa4308c83 | ||
|
|
82a762b0f6 | ||
|
|
21f6f7721c | ||
|
|
93f3ab7f44 | ||
|
|
57d7d98507 | ||
|
|
80880f066e | ||
|
|
8781f476cc | ||
|
|
7217055c15 | ||
|
|
18834895d8 | ||
|
|
7f6d132eb9 | ||
|
|
48e8c2a8af | ||
|
|
d5a0c8839a | ||
|
|
05f0dbedcf | ||
|
|
11db3cfdac | ||
|
|
5fce41347a | ||
|
|
169bd88bef | ||
|
|
d6e86a3176 | ||
|
|
5bcf5d557a | ||
|
|
4a2211becd | ||
|
|
45f00fc002 | ||
|
|
1da129a76c | ||
|
|
520e3f9c15 | ||
|
|
6f18dae272 | ||
|
|
b08f6ea13b | ||
|
|
38cc9a5a47 | ||
|
|
0f94d4e964 | ||
|
|
29c02277bf | ||
|
|
3717488095 | ||
|
|
9a2216fdd1 | ||
|
|
4a2815125b | ||
|
|
50e4dbc53e | ||
|
|
aa68018ad0 | ||
|
|
cc4d862335 | ||
|
|
adeff19c4d | ||
|
|
08aaad3bf5 | ||
|
|
0e8328dc10 | ||
|
|
bc2afca4ff | ||
|
|
9250e90848 | ||
|
|
c45310e731 | ||
|
|
b90c566380 | ||
|
|
26e9b1b6ef | ||
|
|
1d0ff6473a | ||
|
|
8c1d478941 | ||
|
|
f24bb15394 | ||
|
|
56d7c01b8b | ||
|
|
b2b187f8e8 | ||
|
|
76da8d1c5c | ||
|
|
d7f7069ee0 | ||
|
|
0aef932843 | ||
|
|
78d9ffa290 | ||
|
|
ba13620701 | ||
|
|
6538205956 | ||
|
|
4424886899 | ||
|
|
aefc05b0d8 | ||
|
|
fedf0e5587 | ||
|
|
96e2152cec | ||
|
|
2825f13186 | ||
|
|
7a8be56cd8 | ||
|
|
707463e09e | ||
|
|
befe20c773 | ||
|
|
4c65f261ba | ||
|
|
78ac733ed9 | ||
|
|
8d1150e48d | ||
|
|
17fd3c1dcc | ||
|
|
21f728f324 | ||
|
|
09cd3c9ff7 | ||
|
|
e718e4871e | ||
|
|
891e9253ea | ||
|
|
591e3881d5 | ||
|
|
648717c0e3 | ||
|
|
69fa1978be | ||
|
|
45a710490e | ||
|
|
ddd7753bf6 | ||
|
|
16f3ab7272 | ||
|
|
fff63928c9 | ||
|
|
79748c7042 | ||
|
|
b30e5b4f71 | ||
|
|
87959922be | ||
|
|
34122f3971 | ||
|
|
d64e547c68 | ||
|
|
aaa073745b | ||
|
|
e6e86423aa | ||
|
|
66d0a6fd4c | ||
|
|
9c8cdb2ab0 | ||
|
|
2a1c88c496 | ||
|
|
1092d49d90 | ||
|
|
cbda600c32 | ||
|
|
0f1a65cc42 | ||
|
|
749dd59b43 | ||
|
|
640c194220 | ||
|
|
df502e6206 | ||
|
|
5ac4aa8cb4 | ||
|
|
39ac327444 | ||
|
|
d43ffe29f8 | ||
|
|
2a69fc3acd | ||
|
|
22ad63f3f1 | ||
|
|
73ac2e85d6 | ||
|
|
af263bbd0a | ||
|
|
684cf2f52d | ||
|
|
83eb46f455 | ||
|
|
394bc2428a | ||
|
|
5cca33d49b | ||
|
|
5ec36f082c | ||
|
|
878354c44c | ||
|
|
e4ccb549c5 | ||
|
|
496aa42e86 | ||
|
|
83275bbc59 | ||
|
|
2e5f719762 | ||
|
|
ac1a46e853 | ||
|
|
225a3373f2 | ||
|
|
8accee4084 | ||
|
|
f732add651 | ||
|
|
cd48bce840 | ||
|
|
d5f3f7f10e | ||
|
|
053327bf93 | ||
|
|
9846c262c7 | ||
|
|
dd3cf82660 | ||
|
|
c977ebaa25 | ||
|
|
cb1a1216b5 | ||
|
|
1d5c21839c | ||
|
|
07f0ea81ff | ||
|
|
70f3d4c341 | ||
|
|
8ff99c314c | ||
|
|
563cae90dd | ||
|
|
6081c185b4 | ||
|
|
26325d89c9 | ||
|
|
cbb0424bc3 | ||
|
|
ec96b1c910 | ||
|
|
6a5b483159 | ||
|
|
7fcd65bc6b | ||
|
|
25ad2847c3 | ||
|
|
e12234e33c | ||
|
|
021425c3b9 | ||
|
|
ae1dcd4372 | ||
|
|
6e94693ed2 | ||
|
|
43933d5c03 | ||
|
|
ffe0de5add | ||
|
|
054ea43c28 | ||
|
|
50b19026c9 | ||
|
|
64548f6e83 | ||
|
|
51e1596ab0 | ||
|
|
1fa6af63c5 | ||
|
|
5b87a7bbe0 | ||
|
|
79308ac256 | ||
|
|
9003ab1f10 | ||
|
|
99b39a9e2b | ||
|
|
1c1f009214 | ||
|
|
65c0b83982 | ||
|
|
319ae7e837 | ||
|
|
92a6775cdf | ||
|
|
69c3182311 | ||
|
|
28dddc1d28 | ||
|
|
8e1583742d | ||
|
|
66d2f65261 | ||
|
|
bcb8bd2882 | ||
|
|
d357ca4990 | ||
|
|
94970b6922 | ||
|
|
33de07268c | ||
|
|
3b0dd362ce | ||
|
|
b57c1999e7 | ||
|
|
ddf8cf5539 | ||
|
|
1c91fa05ac | ||
|
|
0ccd0e5451 | ||
|
|
17028f615b | ||
|
|
3cd103eb4b | ||
|
|
2a8483f22f | ||
|
|
4636012f19 | ||
|
|
47a38805b2 | ||
|
|
26d0efa4a5 | ||
|
|
4abb9d7444 | ||
|
|
14bc4334b4 | ||
|
|
ebec33d149 | ||
|
|
b5256fc950 | ||
|
|
0fb2a78474 | ||
|
|
ef3cedebd7 | ||
|
|
06aa928d03 | ||
|
|
4ed88777f2 | ||
|
|
87595c31b1 | ||
|
|
44fa0f761a | ||
|
|
236a588fb4 | ||
|
|
6c4b2b5484 | ||
|
|
e3c1533d40 | ||
|
|
4a070a72b0 | ||
|
|
220ab78b5b | ||
|
|
d42722bd8e | ||
|
|
692232fd55 | ||
|
|
12c48cb613 | ||
|
|
ddc832842c | ||
|
|
73e95302ce | ||
|
|
6ecf84c6cc | ||
|
|
bdc127c5e7 | ||
|
|
c5721618df | ||
|
|
b3a2c52e7c | ||
|
|
8d2340b63f | ||
|
|
26a094e525 | ||
|
|
b877529657 | ||
|
|
33d39bdef3 | ||
|
|
9772be82e7 | ||
|
|
1ae00b2305 | ||
|
|
e3aade7167 | ||
|
|
5f5219fd31 | ||
|
|
409b54bc70 | ||
|
|
63cad8b378 | ||
|
|
28b53e1240 | ||
|
|
f8df06d4a5 | ||
|
|
ab6f6f612d | ||
|
|
f1739c3127 | ||
|
|
85bfc1a434 | ||
|
|
a9e9a4a933 | ||
|
|
e23bd3e8fe | ||
|
|
7ebbb43a75 | ||
|
|
1bb7da4ec6 | ||
|
|
68086275f7 | ||
|
|
52d10bf4e6 | ||
|
|
54bfe655a4 | ||
|
|
5b587e425c | ||
|
|
0274fa2300 | ||
|
|
d2ef060d4c | ||
|
|
465f3e5cdc | ||
|
|
3cebb6e3f3 | ||
|
|
216313f9c1 | ||
|
|
158a50435f | ||
|
|
4ce70db88a | ||
|
|
a175ab4802 | ||
|
|
155ceb27ab | ||
|
|
fe0d5bb0d6 | ||
|
|
7dbbbe9af2 | ||
|
|
190a8b6fc3 | ||
|
|
c39930707b | ||
|
|
7045cbe5c8 | ||
|
|
319525d4a7 | ||
|
|
8689b08136 | ||
|
|
b463299e76 | ||
|
|
aaa1832145 | ||
|
|
82dfb79c26 | ||
|
|
00b12d5775 | ||
|
|
a4f30cced6 | ||
|
|
486d6a4d3e | ||
|
|
9da9419155 | ||
|
|
b20effb4ec | ||
|
|
23a0857bcf | ||
|
|
f45faa7763 | ||
|
|
1fa518ba9b | ||
|
|
bd66596647 | ||
|
|
dc57e0886a | ||
|
|
544d8d907d | ||
|
|
544f8de9c6 | ||
|
|
0b18118e60 | ||
|
|
eee68fd024 | ||
|
|
13db14b703 | ||
|
|
2c54275ea3 | ||
|
|
4bc372c6fc | ||
|
|
ddc2564188 | ||
|
|
74834feb6a | ||
|
|
f0eca8f031 | ||
|
|
50fe25ac1c | ||
|
|
90923b2650 | ||
|
|
76bc7c9bb1 | ||
|
|
49e975ba5e | ||
|
|
afb2b19c66 | ||
|
|
b879d16442 | ||
|
|
f9eae3ec10 | ||
|
|
a2272de03f | ||
|
|
1436d4bd4e | ||
|
|
038efd6da6 | ||
|
|
c5dd77fab2 | ||
|
|
44556bc051 | ||
|
|
db67d0b809 | ||
|
|
f586ba09fd | ||
|
|
474d5fea57 | ||
|
|
a87eec14bd | ||
|
|
42ca08a970 | ||
|
|
cec5f34eb9 | ||
|
|
bfc00029ab | ||
|
|
f6c18367e9 | ||
|
|
ab6fc348b9 | ||
|
|
61df4d2144 | ||
|
|
628e280383 | ||
|
|
98a028e72a | ||
|
|
30ee7bb2a9 | ||
|
|
ed76f4df8f | ||
|
|
9344dc64b0 | ||
|
|
9c220490fb | ||
|
|
d83fec84b7 | ||
|
|
f192af0a5c | ||
|
|
a14c87095f | ||
|
|
b149a74785 | ||
|
|
6f70cb323d | ||
|
|
4b3fbb1400 | ||
|
|
5f72443f58 | ||
|
|
c02cc4bc62 | ||
|
|
d8ac3fc0dd | ||
|
|
00d0bd54fb | ||
|
|
3717630c49 | ||
|
|
10e1bfd1e4 | ||
|
|
b049f12e5b | ||
|
|
e21a25e8f4 | ||
|
|
5889bfa6cc | ||
|
|
337a0218fb | ||
|
|
2445a683a4 | ||
|
|
e5d8bfa29f | ||
|
|
c5c8ba9bbc | ||
|
|
7b9ff7dfe0 | ||
|
|
243e0ee457 | ||
|
|
6c12216424 | ||
|
|
a0cf875b4f | ||
|
|
da05d68cef | ||
|
|
6c9c9ba056 | ||
|
|
dcdeac0b13 | ||
|
|
a7e96cd696 | ||
|
|
9574ddc090 | ||
|
|
43ea98794e | ||
|
|
76c67c14b8 | ||
|
|
662268c546 | ||
|
|
6d53326919 | ||
|
|
66ff92fd79 | ||
|
|
610f71a33b | ||
|
|
9aee487fd6 | ||
|
|
4b4dbb5377 | ||
|
|
dd52573ada | ||
|
|
79108d18ac | ||
|
|
decce03905 | ||
|
|
92cb6446ad | ||
|
|
beef9ac1bb | ||
|
|
142ddbabb5 | ||
|
|
50af686f22 | ||
|
|
92d78b605a | ||
|
|
2015f60b1f | ||
|
|
22c7ab8d5d | ||
|
|
46498234b7 | ||
|
|
3a349e5f09 | ||
|
|
663bcc6d9e | ||
|
|
e5d8d49003 | ||
|
|
73b4f651d0 | ||
|
|
a664da6541 | ||
|
|
64dcb8dc53 | ||
|
|
a034f5c98c | ||
|
|
aa76e53a70 | ||
|
|
3314ad5f0e | ||
|
|
cca259b63c | ||
|
|
27e8967df8 | ||
|
|
410b02d4c7 | ||
|
|
4695a65d7f | ||
|
|
b1d8d502f8 | ||
|
|
a49aaa4fab | ||
|
|
4af31dd922 | ||
|
|
09d4902f41 | ||
|
|
956fa20af5 | ||
|
|
fd68656a76 | ||
|
|
50ed7d7241 | ||
|
|
b176a313e2 | ||
|
|
18903710ac | ||
|
|
63c6b627cc | ||
|
|
f796f445b1 | ||
|
|
af7724612b | ||
|
|
723d5e6e1b | ||
|
|
33b345ad22 | ||
|
|
feb1b45102 | ||
|
|
f7bfaea4f0 | ||
|
|
105b91a699 | ||
|
|
b6e3d81807 | ||
|
|
7a76874a6d | ||
|
|
228c8862e1 | ||
|
|
0509530caa | ||
|
|
8515b8ad15 | ||
|
|
ad3b12fa3a | ||
|
|
9dec5d9ccf | ||
|
|
cc8ba7782e | ||
|
|
cdbd31f265 | ||
|
|
3c9da46f13 | ||
|
|
cfd12e0da7 | ||
|
|
a778d2e222 | ||
|
|
ed1fa5d675 | ||
|
|
32a664676f | ||
|
|
cbba784d84 | ||
|
|
b0c4b03175 | ||
|
|
fda828ceae | ||
|
|
9efc2087a1 | ||
|
|
71f4695c3b | ||
|
|
d5a4b24209 | ||
|
|
3cbeba07b6 | ||
|
|
6b1fa38b4b | ||
|
|
a6e54c7560 | ||
|
|
9937b07557 | ||
|
|
b2878d8a2a | ||
|
|
362cbe9686 | ||
|
|
e8e903f630 | ||
|
|
b57b520aa0 | ||
|
|
a77d5dd5c9 | ||
|
|
954f08fca0 | ||
|
|
6ac729c7e6 | ||
|
|
d97766f1ba | ||
|
|
f9b1f82e31 | ||
|
|
41816cb0d8 | ||
|
|
4ca9ab33b1 | ||
|
|
272974ff39 | ||
|
|
bdb8a963a1 | ||
|
|
a0ab168a42 | ||
|
|
9c59a8fe1b | ||
|
|
ba49a50580 | ||
|
|
d1fcd2c048 | ||
|
|
92880fece6 | ||
|
|
de2d78e371 | ||
|
|
3bae9dc896 | ||
|
|
e573aa0da7 | ||
|
|
b8b47c74dc | ||
|
|
cc593bddda | ||
|
|
3958333aee | ||
|
|
d5993db6cb | ||
|
|
b46c83453f | ||
|
|
994863739e | ||
|
|
0323551382 | ||
|
|
ac239e8ba2 | ||
|
|
3a3988ee52 | ||
|
|
0d7ae57daa | ||
|
|
02ea0f360c | ||
|
|
fdb89a3a67 | ||
|
|
54515fcc58 | ||
|
|
ad559cafac | ||
|
|
6b56a34d1a | ||
|
|
de621d90a1 | ||
|
|
95b3b046f3 | ||
|
|
b2384a8736 | ||
|
|
245afe43f2 | ||
|
|
56c2cfb903 | ||
|
|
a77f738676 | ||
|
|
def5c3d533 | ||
|
|
dc16b980d6 | ||
|
|
eb386a180f | ||
|
|
a09d91e8cd | ||
|
|
4a9b72b058 | ||
|
|
b0114f2b29 | ||
|
|
4bb8ad9626 | ||
|
|
1502c40b3d | ||
|
|
0d84a7bd6b | ||
|
|
2e80c13fa5 | ||
|
|
6ec276472d | ||
|
|
79e748d98b | ||
|
|
2fe79008bc | ||
|
|
cad433b84c | ||
|
|
66f7ef047e | ||
|
|
6445374b2e | ||
|
|
17e47d40f0 | ||
|
|
823fe52933 | ||
|
|
f76a1aa267 | ||
|
|
a2b57f7885 | ||
|
|
5ca0dfdcca | ||
|
|
140450d3c7 | ||
|
|
ad94c25914 | ||
|
|
cfc6a944fa | ||
|
|
1449313265 | ||
|
|
0d58b4c845 | ||
|
|
c82c8db56b | ||
|
|
501e153dbf | ||
|
|
e2d0428d99 | ||
|
|
4c446e8335 | ||
|
|
ed359c28e3 | ||
|
|
c3a1d9cec6 | ||
|
|
a1b715db0c | ||
|
|
a07c055152 | ||
|
|
1843d3d083 | ||
|
|
c0d8f51523 | ||
|
|
dd88e6c693 | ||
|
|
68da7052fa | ||
|
|
cafce794e9 | ||
|
|
cb745297e5 | ||
|
|
3a27c9ac9c | ||
|
|
6af8c916b7 | ||
|
|
078e813a29 | ||
|
|
bef9652732 | ||
|
|
b207fb284f | ||
|
|
315cf22c54 | ||
|
|
8d5b2c7348 | ||
|
|
ec4e415df4 | ||
|
|
4d69dc2e78 | ||
|
|
2251565e24 | ||
|
|
e56c9c84dd | ||
|
|
e2c35d4e46 | ||
|
|
60c1c3d2a5 | ||
|
|
6e2d9024a6 | ||
|
|
baa130b0f7 | ||
|
|
7b2b246b92 | ||
|
|
b3c80147dc | ||
|
|
ac60d34866 | ||
|
|
e9b9c78101 | ||
|
|
164a9cb635 | ||
|
|
70fbb79d7b | ||
|
|
78479529ae | ||
|
|
694cf93f85 | ||
|
|
5059269227 | ||
|
|
8b8bfe123e | ||
|
|
1f05f42649 | ||
|
|
903d7d8bbe | ||
|
|
3541f24da7 | ||
|
|
93a98e4cec | ||
|
|
f27ce7ee48 | ||
|
|
8a95e17637 | ||
|
|
97b7b0c774 | ||
|
|
bd63842c50 | ||
|
|
392def5ac1 | ||
|
|
d7b5d42148 | ||
|
|
f62a79fedb | ||
|
|
1a85895c01 | ||
|
|
d234a8c314 | ||
|
|
5d57ce2859 | ||
|
|
bdb8096339 | ||
|
|
b767c8146b | ||
|
|
9b2adfacc6 | ||
|
|
e41e4968cd | ||
|
|
44622b43dc | ||
|
|
6ca5a525a0 | ||
|
|
5698208a47 | ||
|
|
51e3593ac2 | ||
|
|
0f7e995fc0 | ||
|
|
15511b0552 | ||
|
|
03a64823e4 | ||
|
|
51353fbd5d | ||
|
|
134375c31b | ||
|
|
22fc800e45 | ||
|
|
41478d28e8 | ||
|
|
72f7606062 | ||
|
|
ee13ebba0b | ||
|
|
449bfe6fca | ||
|
|
ca9ca76ad9 | ||
|
|
1e07a3c733 | ||
|
|
8679e6b240 | ||
|
|
8b3bbb5e04 | ||
|
|
2954b1af40 | ||
|
|
993658ff0b | ||
|
|
1f20c00c33 | ||
|
|
62e100ba55 | ||
|
|
8e5a85a402 | ||
|
|
6d5e8cd674 | ||
|
|
e4ad4df1e0 | ||
|
|
2bb27fbcb7 | ||
|
|
83d7d789a9 | ||
|
|
b50e0b3523 | ||
|
|
5a5b39a3ca | ||
|
|
1304c3f35b | ||
|
|
55982972b2 | ||
|
|
4ff1317074 | ||
|
|
b4d5003d0e | ||
|
|
818e94f41d | ||
|
|
8647f9fd59 | ||
|
|
c929bb3e48 | ||
|
|
002428d8ff | ||
|
|
f8671dc8a9 | ||
|
|
0e52e3c67c | ||
|
|
12acadb5c4 | ||
|
|
2419ae5374 | ||
|
|
0e196dbed7 | ||
|
|
c84aa4eb8b | ||
|
|
33c974ca93 | ||
|
|
107f04067f | ||
|
|
28fa2b8fb7 | ||
|
|
f885d171a5 | ||
|
|
c74834a9b1 | ||
|
|
161f455da1 | ||
|
|
6657fe1b05 | ||
|
|
0225e5f081 | ||
|
|
3cfabe703d | ||
|
|
b3630ae9e2 | ||
|
|
2359a221d7 | ||
|
|
e38f056a41 | ||
|
|
3999c61987 | ||
|
|
2c84b3d6cd | ||
|
|
def42d683c | ||
|
|
5cb2e3c105 | ||
|
|
092e873eb8 | ||
|
|
98af5051cb | ||
|
|
62501fd2dd | ||
|
|
7f6d55c635 | ||
|
|
8082c0d28d | ||
|
|
287082027e | ||
|
|
da44dc9cab | ||
|
|
fc86f3e15d | ||
|
|
56abcf29f7 | ||
|
|
e916c934e6 | ||
|
|
3db239115a | ||
|
|
f2538a2636 | ||
|
|
8451cfe0d6 | ||
|
|
30e5e079d5 | ||
|
|
b46fc08d76 | ||
|
|
4cb6b295f0 | ||
|
|
a95cb94ee7 | ||
|
|
40f6052231 | ||
|
|
a72a879a23 | ||
|
|
e4034ad480 | ||
|
|
e7f0259eaf | ||
|
|
469baf8281 | ||
|
|
4ab1e3cdaa | ||
|
|
dcc017182f | ||
|
|
020ba67bdb | ||
|
|
4a35f1293c | ||
|
|
b9f4c2e596 | ||
|
|
1ba6211c7c | ||
|
|
f0a286e516 | ||
|
|
f77aef65d8 | ||
|
|
8aa849312e | ||
|
|
7e1de6e49f | ||
|
|
f6237c8f58 | ||
|
|
63728b8423 | ||
|
|
9dea4800f7 | ||
|
|
9980101d2e | ||
|
|
6b9372ed37 | ||
|
|
6f4ca7f1b3 | ||
|
|
c78800b85d | ||
|
|
ecd33ef604 | ||
|
|
5f4622c039 | ||
|
|
74e9087460 | ||
|
|
24a27e51e3 | ||
|
|
d400be8b2e | ||
|
|
0185441ed2 | ||
|
|
f3b13a711e | ||
|
|
f56c4358f9 | ||
|
|
a9dc80cffa | ||
|
|
bbfd139ec8 | ||
|
|
7b4f75fddc | ||
|
|
8c7e8f01ee | ||
|
|
a3ddb13289 | ||
|
|
3f6b1356cb | ||
|
|
fb660ce957 | ||
|
|
b2987fa732 | ||
|
|
13dbe900fe | ||
|
|
3a1bc439d0 | ||
|
|
605d211dbd | ||
|
|
def4e496e4 | ||
|
|
a24525c30c | ||
|
|
9c30654d31 | ||
|
|
2da08c1817 | ||
|
|
693f23578e | ||
|
|
741121127b | ||
|
|
8a031ec767 | ||
|
|
f926e0fe92 | ||
|
|
b3b1cdea38 | ||
|
|
3ccd4cef52 | ||
|
|
5279f86ebf | ||
|
|
24e4f8d37a | ||
|
|
f84d3707bd | ||
|
|
8b4a440cdd | ||
|
|
2d4f589eb0 | ||
|
|
25cb0e7a69 | ||
|
|
242ec3673d | ||
|
|
caee36d818 | ||
|
|
c8efa40d81 | ||
|
|
64e57bd784 | ||
|
|
b6c5f5ae05 | ||
|
|
18851f182c | ||
|
|
8f7d58c5fb | ||
|
|
cbdf8f40e5 | ||
|
|
ff2efd2520 | ||
|
|
fd4ab77bf8 | ||
|
|
6ef0e591d7 | ||
|
|
cbd6311c2c | ||
|
|
6ad3ce4a18 | ||
|
|
2cff94e85d | ||
|
|
7e20815bd9 | ||
|
|
95ce460c7d | ||
|
|
0f99eb39cf | ||
|
|
77e66ffe2a | ||
|
|
869ca44da2 | ||
|
|
c4605eb127 | ||
|
|
9619fda7f1 | ||
|
|
4b0c1d9c70 | ||
|
|
c604cb6227 | ||
|
|
2f13938d1f | ||
|
|
0e953f0c47 | ||
|
|
16a1ec5641 | ||
|
|
0443a947d4 | ||
|
|
dd0a9ff0c2 | ||
|
|
2e75f172e4 | ||
|
|
05aa163cd5 | ||
|
|
9448cd2101 | ||
|
|
2a8f26c371 | ||
|
|
8324d59fa4 | ||
|
|
e89506817d | ||
|
|
f5cdb79446 | ||
|
|
5ca585659a | ||
|
|
0cd1d02b0a | ||
|
|
97291e4bbc | ||
|
|
c47770d58a | ||
|
|
ca6a16ccf4 | ||
|
|
31d8342f11 | ||
|
|
8986cefb4b | ||
|
|
ff21c4f554 | ||
|
|
24173fd7fe | ||
|
|
af7e719cce | ||
|
|
5edbffd1a6 | ||
|
|
bcfcd6d91d | ||
|
|
cba24939ea | ||
|
|
d39d3f6af0 | ||
|
|
d4c880e487 | ||
|
|
046b70a561 | ||
|
|
496af15335 | ||
|
|
e5cfa2c821 | ||
|
|
810f60f829 | ||
|
|
baf462c222 | ||
|
|
5d8a337cda | ||
|
|
b9376e8ad6 | ||
|
|
4c6a2a6118 | ||
|
|
8ea91879af | ||
|
|
6689f29281 | ||
|
|
b85a90562a | ||
|
|
f1431aa1e4 | ||
|
|
d4110df6f2 | ||
|
|
49ca638979 | ||
|
|
c31b2e703e | ||
|
|
74daeb3035 | ||
|
|
71d7f35b06 | ||
|
|
c534a64ebc | ||
|
|
8c811ed474 | ||
|
|
22767adb5a | ||
|
|
c8ef7533a8 | ||
|
|
26f27d7296 | ||
|
|
e9f29fec4a | ||
|
|
c5eb4878f2 | ||
|
|
9060347fdd | ||
|
|
4f3b4bfcee | ||
|
|
299b287c22 | ||
|
|
038141e5f2 | ||
|
|
10102540ac | ||
|
|
8da264608a | ||
|
|
37370cccda | ||
|
|
4752438966 | ||
|
|
40da24a354 | ||
|
|
3f3f9af66a | ||
|
|
b5d78225d2 | ||
|
|
199a81bfd8 | ||
|
|
24cc4cbe8b | ||
|
|
63f871ef0c | ||
|
|
943ce1b28f | ||
|
|
e8234a75e3 | ||
|
|
6f5837ce73 | ||
|
|
40744a5bf6 | ||
|
|
1ab4f9c058 | ||
|
|
7cede18e85 | ||
|
|
159047d589 | ||
|
|
2eb3541462 | ||
|
|
b6dec7d509 | ||
|
|
943e75839d | ||
|
|
a04bfcc550 | ||
|
|
d04a6edd7e | ||
|
|
101036df84 | ||
|
|
a1e62f29be | ||
|
|
37062a38dd | ||
|
|
5176151938 | ||
|
|
8f6abcbd91 | ||
|
|
aacd3d3892 | ||
|
|
4ce1a372af | ||
|
|
5aa9b8ceef | ||
|
|
9b4ae402bb | ||
|
|
6055c222ca | ||
|
|
ceae75379a | ||
|
|
d856a422e5 | ||
|
|
a094de32b5 | ||
|
|
a064e0327d | ||
|
|
3d130a7cbf | ||
|
|
d9a1830a30 | ||
|
|
23c3deb495 | ||
|
|
d4966a793b | ||
|
|
abb7d61c72 | ||
|
|
e3e5635514 | ||
|
|
84cd596a1c | ||
|
|
a066d95267 | ||
|
|
5baaf91ee7 | ||
|
|
43d41270d1 | ||
|
|
f334f430d1 | ||
|
|
2f7023e9e6 | ||
|
|
f2f72c696f | ||
|
|
ff619f6040 | ||
|
|
4f2b5540cf | ||
|
|
06353a08ff | ||
|
|
54359b38dd | ||
|
|
c1a8b4ad5b | ||
|
|
3b6a3e955c | ||
|
|
30a452528a | ||
|
|
4e6e863017 | ||
|
|
393eb77fa9 | ||
|
|
6caaba35e4 | ||
|
|
fdf2d929f8 | ||
|
|
e6cf086ef8 | ||
|
|
0ddc112453 | ||
|
|
fbc3c4434a | ||
|
|
e44cf7c7f2 | ||
|
|
2f02c545be | ||
|
|
6b9999da70 | ||
|
|
44b6505cfe | ||
|
|
d058762b45 | ||
|
|
e6288b1355 | ||
|
|
f257c47fc1 | ||
|
|
c184c0ef96 | ||
|
|
8cf67a1aec | ||
|
|
1e6f0d9694 | ||
|
|
755ac5565c | ||
|
|
1717fee73e | ||
|
|
6d76cda39b | ||
|
|
8bae8e0ac5 | ||
|
|
ca31a50c48 | ||
|
|
d81a0e2d3f | ||
|
|
32595ee27e | ||
|
|
17d514c6da | ||
|
|
3c1e813f55 | ||
|
|
bb13f44fbd | ||
|
|
cf8d4678ff | ||
|
|
ae6c720b59 | ||
|
|
bdf19c95f6 | ||
|
|
28187d73f6 | ||
|
|
512c3fffd5 | ||
|
|
3f0292e6ac | ||
|
|
ef3d2fc351 | ||
|
|
de6570cf91 | ||
|
|
e05bf0decd | ||
|
|
c39fe4aac9 | ||
|
|
b8960a99dd | ||
|
|
173e9c05fd | ||
|
|
8bc0c78cb8 | ||
|
|
fc6964359f | ||
|
|
433ddf5c81 | ||
|
|
5d101b0e8b | ||
|
|
6faa20eb3b | ||
|
|
82b3d71678 | ||
|
|
b2f2697413 | ||
|
|
f1c64efb26 | ||
|
|
0a3c1ec0a5 | ||
|
|
82b9efe1df | ||
|
|
ecaeabd4f4 | ||
|
|
69c9d31012 | ||
|
|
669cd60dc0 | ||
|
|
4e01d5030c | ||
|
|
04e7591d62 | ||
|
|
fc734ac268 | ||
|
|
9acb381b24 | ||
|
|
4af5c0c4ac | ||
|
|
67fb47411f | ||
|
|
6f8986fb48 | ||
|
|
62cdcc8fc4 | ||
|
|
b751f2e719 | ||
|
|
85b02d8eb0 | ||
|
|
9b39a9dfa4 | ||
|
|
d60b7ff87c | ||
|
|
3d101cf22a | ||
|
|
f6f4467c07 | ||
|
|
8e3afe82d3 | ||
|
|
37512eaa00 | ||
|
|
7b008ef684 | ||
|
|
e493f7874a | ||
|
|
001ce3f96f | ||
|
|
a91d7bbfbb | ||
|
|
20fac381af | ||
|
|
5a30fb9549 | ||
|
|
7565b02647 | ||
|
|
f793e21f17 | ||
|
|
1b8c8599fa | ||
|
|
73c163098e | ||
|
|
bcbb18a07f | ||
|
|
01dd6c852e | ||
|
|
87e5671a6f | ||
|
|
62bf30db08 | ||
|
|
c066091b2d | ||
|
|
a5a5415fc0 | ||
|
|
43e9ef4a6f | ||
|
|
acc637a3bf | ||
|
|
42f61069dd | ||
|
|
f8bd4f6713 | ||
|
|
d6c438514b | ||
|
|
86c3abd11d | ||
|
|
d0b3a26d2d | ||
|
|
ee55ae887f | ||
|
|
30bfd1d522 | ||
|
|
e1717459d4 | ||
|
|
d3a4692a6a | ||
|
|
0dfadd86c2 | ||
|
|
2a7466bba8 | ||
|
|
2869e2479f | ||
|
|
9bfb0e6430 | ||
|
|
3a5e043f80 | ||
|
|
e66ce0769b | ||
|
|
21e7523457 | ||
|
|
f62959c5d1 | ||
|
|
e6cb50ee47 | ||
|
|
942acfcf19 | ||
|
|
9edbcb35d7 | ||
|
|
50d92ad971 | ||
|
|
c2999826c7 | ||
|
|
6d40d01c5e | ||
|
|
9f6fe2336b | ||
|
|
c05373b1c9 | ||
|
|
c908e6b190 | ||
|
|
82fc7e4f0f | ||
|
|
43998f9274 | ||
|
|
faab7338d5 | ||
|
|
b0aca387bb | ||
|
|
3657eddf35 | ||
|
|
a378d3e73a | ||
|
|
512e49915c | ||
|
|
d71fb0a1cf | ||
|
|
7c4a261104 | ||
|
|
02e3246388 | ||
|
|
fe2f37984f | ||
|
|
d7ac5d4a1d | ||
|
|
ff12e4ef16 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
.DS_Store
|
||||
# json config
|
||||
sub-store.json
|
||||
sub-store_*.json
|
||||
root.json
|
||||
|
||||
# Logs
|
||||
|
||||
0
.gitmodules
vendored
0
.gitmodules
vendored
35
README.md
35
README.md
@@ -26,13 +26,14 @@ Core functionalities:
|
||||
|
||||
### 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))
|
||||
|
||||
example: `socks5+tls://user:pass@ip:port#name`
|
||||
|
||||
- [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 Proxy JSON(single line)
|
||||
- [x] QX (SS, SSR, VMess, Trojan, HTTP, SOCKS5, VLESS)
|
||||
@@ -41,6 +42,9 @@ Core functionalities:
|
||||
- [x] Surfboard (SS, VMess, Trojan, HTTP, SOCKS5, SOCKS5-TLS, WireGuard(Surfboard to Surfboard))
|
||||
- [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)
|
||||
|
||||
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)
|
||||
|
||||
### Supported Target Platforms
|
||||
@@ -48,7 +52,6 @@ Core functionalities:
|
||||
- [x] Plain JSON
|
||||
- [x] Stash
|
||||
- [x] Clash.Meta(mihomo)
|
||||
- [x] Clash
|
||||
- [x] Surfboard
|
||||
- [x] Surge
|
||||
- [x] SurgeMac(Use mihomo to support protocols that are not supported by Surge itself)
|
||||
@@ -60,6 +63,10 @@ Core functionalities:
|
||||
- [x] V2Ray
|
||||
- [x] V2Ray URI
|
||||
|
||||
Deprecated:
|
||||
|
||||
- [x] Clash
|
||||
|
||||
## 2. Subscription Formatting
|
||||
|
||||
### Filtering
|
||||
@@ -92,22 +99,16 @@ Go to `backend` directories, install node dependencies:
|
||||
pnpm i
|
||||
```
|
||||
|
||||
1. In `backend`, run the backend server on http://localhost:3000
|
||||
|
||||
babel(old school)
|
||||
|
||||
```
|
||||
pnpm start
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
esbuild(experimental)
|
||||
|
||||
```
|
||||
SUB_STORE_BACKEND_API_PORT=3000 pnpm run --parallel "/^dev:.*/"
|
||||
```
|
||||
|
||||
### Build
|
||||
|
||||
```
|
||||
pnpm bundle:esbuild
|
||||
```
|
||||
|
||||
## LICENSE
|
||||
|
||||
This project is under the GPL V3 LICENSE.
|
||||
@@ -122,3 +123,9 @@ This project is under the GPL V3 LICENSE.
|
||||
|
||||
- Special thanks to @KOP-XIAO for his awesome resource-parser. Please give a [star](https://github.com/KOP-XIAO/QuantumultX) for his great work!
|
||||
- Special thanks to @Orz-3 and @58xinian for their awesome icons.
|
||||
|
||||
## Sponsors
|
||||
|
||||
[](https://yxvm.com)
|
||||
|
||||
[NodeSupport](https://github.com/NodeSeekDev/NodeSupport) sponsored this project.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sub-store",
|
||||
"version": "2.17.8",
|
||||
"version": "2.20.2",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
@@ -17,6 +17,11 @@
|
||||
},
|
||||
"author": "Peng-YM",
|
||||
"license": "GPL-3.0",
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"http-proxy@1.18.1": "patches/http-proxy@1.18.1.patch"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@maxmind/geoip2-node": "^5.0.0",
|
||||
"automerge": "1.0.1-preview.7",
|
||||
@@ -25,17 +30,21 @@
|
||||
"connect-history-api-fallback": "^2.0.0",
|
||||
"cron": "^3.1.6",
|
||||
"dns-packet": "^5.6.1",
|
||||
"dotenv": "^16.4.7",
|
||||
"express": "^4.17.1",
|
||||
"http-proxy-middleware": "^2.0.6",
|
||||
"fetch-socks": "^1.3.2",
|
||||
"http-proxy-middleware": "^3.0.3",
|
||||
"ip-address": "^9.0.5",
|
||||
"js-base64": "^3.7.2",
|
||||
"json5": "^2.2.3",
|
||||
"jsrsasign": "^11.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mime-types": "^2.1.35",
|
||||
"ms": "^2.1.3",
|
||||
"nanoid": "^3.3.3",
|
||||
"request": "^2.88.2",
|
||||
"semver": "^7.6.3",
|
||||
"static-js-yaml": "^1.0.0"
|
||||
"static-js-yaml": "^1.0.0",
|
||||
"undici": "^7.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.18.0",
|
||||
|
||||
46
backend/patches/http-proxy@1.18.1.patch
Normal file
46
backend/patches/http-proxy@1.18.1.patch
Normal file
@@ -0,0 +1,46 @@
|
||||
diff --git a/lib/http-proxy/common.js b/lib/http-proxy/common.js
|
||||
index 6513e81d80d5250ea249ea833f819ece67897c7e..486d4c896d65a3bb7cf63307af68facb3ddb886b 100644
|
||||
--- a/lib/http-proxy/common.js
|
||||
+++ b/lib/http-proxy/common.js
|
||||
@@ -1,6 +1,5 @@
|
||||
var common = exports,
|
||||
url = require('url'),
|
||||
- extend = require('util')._extend,
|
||||
required = require('requires-port');
|
||||
|
||||
var upgradeHeader = /(^|,)\s*upgrade\s*($|,)/i,
|
||||
@@ -40,10 +39,10 @@ common.setupOutgoing = function(outgoing, options, req, forward) {
|
||||
);
|
||||
|
||||
outgoing.method = options.method || req.method;
|
||||
- outgoing.headers = extend({}, req.headers);
|
||||
+ outgoing.headers = Object.assign({}, req.headers);
|
||||
|
||||
if (options.headers){
|
||||
- extend(outgoing.headers, options.headers);
|
||||
+ Object.assign(outgoing.headers, options.headers);
|
||||
}
|
||||
|
||||
if (options.auth) {
|
||||
diff --git a/lib/http-proxy/index.js b/lib/http-proxy/index.js
|
||||
index 977a4b3622b9eaac27689f06347ea4c5173a96cd..88b2d0fcfa03c3aafa47c7e6d38e64412c45a7cc 100644
|
||||
--- a/lib/http-proxy/index.js
|
||||
+++ b/lib/http-proxy/index.js
|
||||
@@ -1,5 +1,4 @@
|
||||
var httpProxy = module.exports,
|
||||
- extend = require('util')._extend,
|
||||
parse_url = require('url').parse,
|
||||
EE3 = require('eventemitter3'),
|
||||
http = require('http'),
|
||||
@@ -47,9 +46,9 @@ function createRightProxy(type) {
|
||||
args[cntr] !== res
|
||||
) {
|
||||
//Copy global options
|
||||
- requestOptions = extend({}, options);
|
||||
+ requestOptions = Object.assign({}, options);
|
||||
//Overwrite with request options
|
||||
- extend(requestOptions, args[cntr]);
|
||||
+ Object.assign(requestOptions, args[cntr]);
|
||||
|
||||
cntr--;
|
||||
}
|
||||
330
backend/pnpm-lock.yaml
generated
330
backend/pnpm-lock.yaml
generated
@@ -4,6 +4,11 @@ settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
patchedDependencies:
|
||||
http-proxy@1.18.1:
|
||||
hash: 8071c23044f455271f4d4074ae4c7b81beec17a03aefd158d5f4edd4ef751c11
|
||||
path: patches/http-proxy@1.18.1.patch
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
@@ -29,39 +34,51 @@ importers:
|
||||
dns-packet:
|
||||
specifier: ^5.6.1
|
||||
version: 5.6.1
|
||||
dotenv:
|
||||
specifier: ^16.4.7
|
||||
version: 16.5.0
|
||||
express:
|
||||
specifier: ^4.17.1
|
||||
version: 4.21.2
|
||||
fetch-socks:
|
||||
specifier: ^1.3.2
|
||||
version: 1.3.2
|
||||
http-proxy-middleware:
|
||||
specifier: ^2.0.6
|
||||
version: 2.0.7
|
||||
specifier: ^3.0.3
|
||||
version: 3.0.3
|
||||
ip-address:
|
||||
specifier: ^9.0.5
|
||||
version: 9.0.5
|
||||
js-base64:
|
||||
specifier: ^3.7.2
|
||||
version: 3.7.7
|
||||
json5:
|
||||
specifier: ^2.2.3
|
||||
version: 2.2.3
|
||||
jsrsasign:
|
||||
specifier: ^11.1.0
|
||||
version: 11.1.0
|
||||
lodash:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
mime-types:
|
||||
specifier: ^2.1.35
|
||||
version: 2.1.35
|
||||
ms:
|
||||
specifier: ^2.1.3
|
||||
version: 2.1.3
|
||||
nanoid:
|
||||
specifier: ^3.3.3
|
||||
version: 3.3.8
|
||||
request:
|
||||
specifier: ^2.88.2
|
||||
version: 2.88.2
|
||||
semver:
|
||||
specifier: ^7.6.3
|
||||
version: 7.6.3
|
||||
static-js-yaml:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
undici:
|
||||
specifier: ^7.4.0
|
||||
version: 7.4.0
|
||||
devDependencies:
|
||||
'@babel/core':
|
||||
specifier: ^7.18.0
|
||||
@@ -1072,9 +1089,6 @@ packages:
|
||||
asn1.js@4.10.1:
|
||||
resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==}
|
||||
|
||||
asn1@0.2.6:
|
||||
resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==}
|
||||
|
||||
assert-plus@1.0.0:
|
||||
resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==}
|
||||
engines: {node: '>=0.8'}
|
||||
@@ -1100,9 +1114,6 @@ packages:
|
||||
resolution: {integrity: sha512-VPXfB4Vk49z1LHHodrEQ6Xf7W4gg1w0dAPROHngx7qgDjqmIQ+fXmwgGXTW/ITLai0YLSvWepJOP9EVpMnEAcw==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
asynckit@0.4.0:
|
||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||
|
||||
atob@2.1.2:
|
||||
resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==}
|
||||
engines: {node: '>= 4.5.0'}
|
||||
@@ -1115,12 +1126,6 @@ packages:
|
||||
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
aws-sign2@0.7.0:
|
||||
resolution: {integrity: sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==}
|
||||
|
||||
aws4@1.13.2:
|
||||
resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==}
|
||||
|
||||
axios@0.21.4:
|
||||
resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
|
||||
|
||||
@@ -1168,9 +1173,6 @@ packages:
|
||||
resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
bcrypt-pbkdf@1.0.2:
|
||||
resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
|
||||
|
||||
binary-extensions@1.13.1:
|
||||
resolution: {integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -1327,9 +1329,6 @@ packages:
|
||||
caniuse-lite@1.0.30001692:
|
||||
resolution: {integrity: sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==}
|
||||
|
||||
caseless@0.12.0:
|
||||
resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==}
|
||||
|
||||
chai@4.5.0:
|
||||
resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -1410,10 +1409,6 @@ packages:
|
||||
combine-source-map@0.8.0:
|
||||
resolution: {integrity: sha512-UlxQ9Vw0b/Bt/KYwCFqdEwsQ1eL8d1gibiFb7lxQJFdvTgc2hIZi6ugsg+kyhzhPV+QEpUiEIwInIAIrgoEkrg==}
|
||||
|
||||
combined-stream@1.0.8:
|
||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
commander@2.20.3:
|
||||
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
|
||||
|
||||
@@ -1535,10 +1530,6 @@ packages:
|
||||
dash-ast@2.0.1:
|
||||
resolution: {integrity: sha512-5TXltWJGc+RdnabUGzhRae1TRq6m4gr+3K2wQX0is5/F2yS6MJXJvLyI3ErAnsAXuJoGqvfVD5icRgim07DrxQ==}
|
||||
|
||||
dashdash@1.14.1:
|
||||
resolution: {integrity: sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==}
|
||||
engines: {node: '>=0.10'}
|
||||
|
||||
data-view-buffer@1.0.2:
|
||||
resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1633,10 +1624,6 @@ packages:
|
||||
defined@1.0.1:
|
||||
resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==}
|
||||
|
||||
delayed-stream@1.0.0:
|
||||
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
|
||||
depd@2.0.0:
|
||||
resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@@ -1680,6 +1667,10 @@ packages:
|
||||
resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==}
|
||||
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:
|
||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1702,9 +1693,6 @@ packages:
|
||||
each-props@1.3.2:
|
||||
resolution: {integrity: sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==}
|
||||
|
||||
ecc-jsbn@0.1.2:
|
||||
resolution: {integrity: sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==}
|
||||
|
||||
ee-first@1.1.1:
|
||||
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
|
||||
|
||||
@@ -1989,6 +1977,9 @@ packages:
|
||||
fastq@1.18.0:
|
||||
resolution: {integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==}
|
||||
|
||||
fetch-socks@1.3.2:
|
||||
resolution: {integrity: sha512-vkH5+Zgj2yEbU57Cei0iyLgTZ4OkEKJj56Xu3ViB5dpsl599JgEooQ3x6NVagIFRHWnWJ+7K0MO0aIV1TMgvnw==}
|
||||
|
||||
file-entry-cache@6.0.1:
|
||||
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
@@ -2074,16 +2065,9 @@ packages:
|
||||
resolution: {integrity: sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
forever-agent@0.6.1:
|
||||
resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==}
|
||||
|
||||
fork-stream@0.0.4:
|
||||
resolution: {integrity: sha512-Pqq5NnT78ehvUnAk/We/Jr22vSvanRlFTpAmQ88xBY/M1TlHe+P0ILuEyXS595ysdGfaj22634LBkGMA2GTcpA==}
|
||||
|
||||
form-data@2.3.3:
|
||||
resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==}
|
||||
engines: {node: '>= 0.12'}
|
||||
|
||||
forwarded@0.2.0:
|
||||
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -2167,9 +2151,6 @@ packages:
|
||||
resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
getpass@0.1.7:
|
||||
resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==}
|
||||
|
||||
glob-parent@3.1.0:
|
||||
resolution: {integrity: sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==}
|
||||
|
||||
@@ -2273,15 +2254,6 @@ packages:
|
||||
resolution: {integrity: sha512-hm6N8nrm3Y08jXie48jsC55eCZz9mnb4OirAStEk2deqeyhXU3C1otDVh+ccttMuc1sBi6RX6ZJ720hs9RCvgw==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
har-schema@2.0.0:
|
||||
resolution: {integrity: sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
har-validator@5.1.5:
|
||||
resolution: {integrity: sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==}
|
||||
engines: {node: '>=6'}
|
||||
deprecated: this library is no longer supported
|
||||
|
||||
has-ansi@2.0.0:
|
||||
resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -2366,23 +2338,14 @@ packages:
|
||||
resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
http-proxy-middleware@2.0.7:
|
||||
resolution: {integrity: sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
'@types/express': ^4.17.13
|
||||
peerDependenciesMeta:
|
||||
'@types/express':
|
||||
optional: true
|
||||
http-proxy-middleware@3.0.3:
|
||||
resolution: {integrity: sha512-usY0HG5nyDUwtqpiZdETNbmKtw3QQ1jwYFZ9wi5iHzX2BcILwQKtYDJPo7XHTsu5Z0B2Hj3W9NNnbd+AjFWjqg==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
|
||||
http-proxy@1.18.1:
|
||||
resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
||||
http-signature@1.2.0:
|
||||
resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==}
|
||||
engines: {node: '>=0.8', npm: '>=1.3.7'}
|
||||
|
||||
https-browserify@1.0.0:
|
||||
resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==}
|
||||
|
||||
@@ -2597,10 +2560,6 @@ packages:
|
||||
resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
is-plain-obj@3.0.0:
|
||||
resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
is-plain-object@2.0.4:
|
||||
resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -2637,9 +2596,6 @@ packages:
|
||||
resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
is-typedarray@1.0.0:
|
||||
resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
|
||||
|
||||
is-unc-path@1.0.0:
|
||||
resolution: {integrity: sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -2691,9 +2647,6 @@ packages:
|
||||
resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
isstream@0.1.2:
|
||||
resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==}
|
||||
|
||||
js-base64@3.7.7:
|
||||
resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==}
|
||||
|
||||
@@ -2708,9 +2661,6 @@ packages:
|
||||
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
|
||||
hasBin: true
|
||||
|
||||
jsbn@0.1.1:
|
||||
resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==}
|
||||
|
||||
jsbn@1.1.0:
|
||||
resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==}
|
||||
|
||||
@@ -2736,9 +2686,6 @@ packages:
|
||||
json-stable-stringify-without-jsonify@1.0.1:
|
||||
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
|
||||
|
||||
json-stringify-safe@5.0.1:
|
||||
resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
|
||||
|
||||
json5@2.2.3:
|
||||
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -2748,10 +2695,6 @@ packages:
|
||||
resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==}
|
||||
engines: {'0': node >= 0.2.0}
|
||||
|
||||
jsprim@1.4.2:
|
||||
resolution: {integrity: sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==}
|
||||
engines: {node: '>=0.6.0'}
|
||||
|
||||
jsprim@2.0.2:
|
||||
resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==}
|
||||
engines: {'0': node >=0.6.0}
|
||||
@@ -3065,9 +3008,6 @@ packages:
|
||||
resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
oauth-sign@0.9.0:
|
||||
resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==}
|
||||
|
||||
object-assign@4.1.1:
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -3272,9 +3212,6 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
performance-now@2.1.0:
|
||||
resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==}
|
||||
|
||||
picocolors@1.1.1:
|
||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||
|
||||
@@ -3360,9 +3297,6 @@ packages:
|
||||
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
psl@1.15.0:
|
||||
resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==}
|
||||
|
||||
pstree.remy@1.1.8:
|
||||
resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==}
|
||||
|
||||
@@ -3390,10 +3324,6 @@ packages:
|
||||
resolution: {integrity: sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
||||
qs@6.5.3:
|
||||
resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==}
|
||||
engines: {node: '>=0.6'}
|
||||
|
||||
querystring-es3@0.2.1:
|
||||
resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==}
|
||||
engines: {node: '>=0.4.x'}
|
||||
@@ -3524,11 +3454,6 @@ packages:
|
||||
resolution: {integrity: sha512-CHPV/GAglbIB1tnQgaiysb8H2yCy8WQ7lcEwQ/eT+kLj0QHV8LnJW0zpqpE7RSkrMSRoa+EBoag86clf7WAgSg==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
request@2.88.2:
|
||||
resolution: {integrity: sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==}
|
||||
engines: {node: '>= 6'}
|
||||
deprecated: request has been deprecated, see https://github.com/request/request/issues/3142
|
||||
|
||||
require-directory@2.1.1:
|
||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -3717,6 +3642,10 @@ packages:
|
||||
resolution: {integrity: sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==}
|
||||
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:
|
||||
resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -3729,6 +3658,10 @@ packages:
|
||||
resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==}
|
||||
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:
|
||||
resolution: {integrity: sha512-psgxdGMwl5MZM9S3FWee4EgsEaIjahYV5AzGnwUvPhWeITz/j6rKpysQHlQ4USdxvINlb8lKfWGIXwfkrgtqkA==}
|
||||
engines: {node: '>= 10'}
|
||||
@@ -3790,11 +3723,6 @@ packages:
|
||||
sprintf-js@1.1.3:
|
||||
resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==}
|
||||
|
||||
sshpk@1.18.0:
|
||||
resolution: {integrity: sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
hasBin: true
|
||||
|
||||
stack-trace@0.0.10:
|
||||
resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
|
||||
|
||||
@@ -4010,22 +3938,12 @@ packages:
|
||||
resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==}
|
||||
hasBin: true
|
||||
|
||||
tough-cookie@2.5.0:
|
||||
resolution: {integrity: sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
transform-ast@2.4.4:
|
||||
resolution: {integrity: sha512-AxjeZAcIOUO2lev2GDe3/xZ1Q0cVGjIMk5IsriTy8zbWlsEnjeB025AhkhBJHoy997mXpLd4R+kRbvnnQVuQHQ==}
|
||||
|
||||
tty-browserify@0.0.1:
|
||||
resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==}
|
||||
|
||||
tunnel-agent@0.6.0:
|
||||
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
|
||||
|
||||
tweetnacl@0.14.5:
|
||||
resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==}
|
||||
|
||||
type-check@0.3.2:
|
||||
resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
@@ -4112,6 +4030,10 @@ packages:
|
||||
undici-types@6.20.0:
|
||||
resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
|
||||
|
||||
undici@7.4.0:
|
||||
resolution: {integrity: sha512-PUQM3/es3noM24oUn10u3kNNap0AbxESOmnssmW+dOi9yGwlUSi5nTNYl3bNbTkWOF8YZDkx2tCmj9OtQ3iGGw==}
|
||||
engines: {node: '>=20.18.1'}
|
||||
|
||||
unicode-canonical-property-names-ecmascript@2.0.1:
|
||||
resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -5376,10 +5298,6 @@ snapshots:
|
||||
inherits: 2.0.4
|
||||
minimalistic-assert: 1.0.1
|
||||
|
||||
asn1@0.2.6:
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
|
||||
assert-plus@1.0.0: {}
|
||||
|
||||
assert@1.5.1:
|
||||
@@ -5404,8 +5322,6 @@ snapshots:
|
||||
dependencies:
|
||||
async-done: 1.3.2
|
||||
|
||||
asynckit@0.4.0: {}
|
||||
|
||||
atob@2.1.2: {}
|
||||
|
||||
automerge@1.0.1-preview.7:
|
||||
@@ -5418,13 +5334,9 @@ snapshots:
|
||||
dependencies:
|
||||
possible-typed-array-names: 1.0.0
|
||||
|
||||
aws-sign2@0.7.0: {}
|
||||
|
||||
aws4@1.13.2: {}
|
||||
|
||||
axios@0.21.4:
|
||||
dependencies:
|
||||
follow-redirects: 1.15.9
|
||||
follow-redirects: 1.15.9(debug@4.4.0)
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
|
||||
@@ -5491,10 +5403,6 @@ snapshots:
|
||||
mixin-deep: 1.3.2
|
||||
pascalcase: 0.1.1
|
||||
|
||||
bcrypt-pbkdf@1.0.2:
|
||||
dependencies:
|
||||
tweetnacl: 0.14.5
|
||||
|
||||
binary-extensions@1.13.1: {}
|
||||
|
||||
binary-extensions@2.3.0: {}
|
||||
@@ -5779,8 +5687,6 @@ snapshots:
|
||||
|
||||
caniuse-lite@1.0.30001692: {}
|
||||
|
||||
caseless@0.12.0: {}
|
||||
|
||||
chai@4.5.0:
|
||||
dependencies:
|
||||
assertion-error: 1.1.0
|
||||
@@ -5908,10 +5814,6 @@ snapshots:
|
||||
lodash.memoize: 3.0.4
|
||||
source-map: 0.5.7
|
||||
|
||||
combined-stream@1.0.8:
|
||||
dependencies:
|
||||
delayed-stream: 1.0.0
|
||||
|
||||
commander@2.20.3: {}
|
||||
|
||||
commander@6.2.1: {}
|
||||
@@ -6050,10 +5952,6 @@ snapshots:
|
||||
|
||||
dash-ast@2.0.1: {}
|
||||
|
||||
dashdash@1.14.1:
|
||||
dependencies:
|
||||
assert-plus: 1.0.0
|
||||
|
||||
data-view-buffer@1.0.2:
|
||||
dependencies:
|
||||
call-bound: 1.0.3
|
||||
@@ -6144,8 +6042,6 @@ snapshots:
|
||||
|
||||
defined@1.0.1: {}
|
||||
|
||||
delayed-stream@1.0.0: {}
|
||||
|
||||
depd@2.0.0: {}
|
||||
|
||||
deps-sort@2.0.1:
|
||||
@@ -6188,6 +6084,8 @@ snapshots:
|
||||
|
||||
domain-browser@1.2.0: {}
|
||||
|
||||
dotenv@16.5.0: {}
|
||||
|
||||
dunder-proto@1.0.1:
|
||||
dependencies:
|
||||
call-bind-apply-helpers: 1.0.1
|
||||
@@ -6223,11 +6121,6 @@ snapshots:
|
||||
is-plain-object: 2.0.4
|
||||
object.defaults: 1.1.0
|
||||
|
||||
ecc-jsbn@0.1.2:
|
||||
dependencies:
|
||||
jsbn: 0.1.1
|
||||
safer-buffer: 2.1.2
|
||||
|
||||
ee-first@1.1.1: {}
|
||||
|
||||
electron-to-chromium@1.5.80: {}
|
||||
@@ -6674,6 +6567,11 @@ snapshots:
|
||||
dependencies:
|
||||
reusify: 1.0.4
|
||||
|
||||
fetch-socks@1.3.2:
|
||||
dependencies:
|
||||
socks: 2.8.6
|
||||
undici: 7.4.0
|
||||
|
||||
file-entry-cache@6.0.1:
|
||||
dependencies:
|
||||
flat-cache: 3.2.0
|
||||
@@ -6767,7 +6665,9 @@ snapshots:
|
||||
inherits: 2.0.4
|
||||
readable-stream: 2.3.8
|
||||
|
||||
follow-redirects@1.15.9: {}
|
||||
follow-redirects@1.15.9(debug@4.4.0):
|
||||
optionalDependencies:
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
|
||||
for-each@0.3.3:
|
||||
dependencies:
|
||||
@@ -6779,16 +6679,8 @@ snapshots:
|
||||
dependencies:
|
||||
for-in: 1.0.2
|
||||
|
||||
forever-agent@0.6.1: {}
|
||||
|
||||
fork-stream@0.0.4: {}
|
||||
|
||||
form-data@2.3.3:
|
||||
dependencies:
|
||||
asynckit: 0.4.0
|
||||
combined-stream: 1.0.8
|
||||
mime-types: 2.1.35
|
||||
|
||||
forwarded@0.2.0: {}
|
||||
|
||||
fragment-cache@0.2.1:
|
||||
@@ -6876,10 +6768,6 @@ snapshots:
|
||||
|
||||
get-value@2.0.6: {}
|
||||
|
||||
getpass@0.1.7:
|
||||
dependencies:
|
||||
assert-plus: 1.0.0
|
||||
|
||||
glob-parent@3.1.0:
|
||||
dependencies:
|
||||
is-glob: 3.1.0
|
||||
@@ -7060,13 +6948,6 @@ snapshots:
|
||||
dependencies:
|
||||
glogg: 1.0.2
|
||||
|
||||
har-schema@2.0.0: {}
|
||||
|
||||
har-validator@5.1.5:
|
||||
dependencies:
|
||||
ajv: 6.12.6
|
||||
har-schema: 2.0.0
|
||||
|
||||
has-ansi@2.0.0:
|
||||
dependencies:
|
||||
ansi-regex: 2.1.1
|
||||
@@ -7150,30 +7031,25 @@ snapshots:
|
||||
statuses: 2.0.1
|
||||
toidentifier: 1.0.1
|
||||
|
||||
http-proxy-middleware@2.0.7:
|
||||
http-proxy-middleware@3.0.3:
|
||||
dependencies:
|
||||
'@types/http-proxy': 1.17.15
|
||||
http-proxy: 1.18.1
|
||||
debug: 4.4.0(supports-color@8.1.1)
|
||||
http-proxy: 1.18.1(patch_hash=8071c23044f455271f4d4074ae4c7b81beec17a03aefd158d5f4edd4ef751c11)(debug@4.4.0)
|
||||
is-glob: 4.0.3
|
||||
is-plain-obj: 3.0.0
|
||||
is-plain-object: 5.0.0
|
||||
micromatch: 4.0.8
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
- supports-color
|
||||
|
||||
http-proxy@1.18.1:
|
||||
http-proxy@1.18.1(patch_hash=8071c23044f455271f4d4074ae4c7b81beec17a03aefd158d5f4edd4ef751c11)(debug@4.4.0):
|
||||
dependencies:
|
||||
eventemitter3: 4.0.7
|
||||
follow-redirects: 1.15.9
|
||||
follow-redirects: 1.15.9(debug@4.4.0)
|
||||
requires-port: 1.0.0
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
|
||||
http-signature@1.2.0:
|
||||
dependencies:
|
||||
assert-plus: 1.0.0
|
||||
jsprim: 1.4.2
|
||||
sshpk: 1.18.0
|
||||
|
||||
https-browserify@1.0.0: {}
|
||||
|
||||
iconv-lite@0.4.24:
|
||||
@@ -7382,8 +7258,6 @@ snapshots:
|
||||
|
||||
is-plain-obj@2.1.0: {}
|
||||
|
||||
is-plain-obj@3.0.0: {}
|
||||
|
||||
is-plain-object@2.0.4:
|
||||
dependencies:
|
||||
isobject: 3.0.1
|
||||
@@ -7422,8 +7296,6 @@ snapshots:
|
||||
dependencies:
|
||||
which-typed-array: 1.1.18
|
||||
|
||||
is-typedarray@1.0.0: {}
|
||||
|
||||
is-unc-path@1.0.0:
|
||||
dependencies:
|
||||
unc-path-regex: 0.1.2
|
||||
@@ -7461,8 +7333,6 @@ snapshots:
|
||||
|
||||
isobject@3.0.1: {}
|
||||
|
||||
isstream@0.1.2: {}
|
||||
|
||||
js-base64@3.7.7: {}
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
@@ -7476,8 +7346,6 @@ snapshots:
|
||||
dependencies:
|
||||
argparse: 2.0.1
|
||||
|
||||
jsbn@0.1.1: {}
|
||||
|
||||
jsbn@1.1.0: {}
|
||||
|
||||
jsesc@3.0.2: {}
|
||||
@@ -7492,19 +7360,10 @@ snapshots:
|
||||
|
||||
json-stable-stringify-without-jsonify@1.0.1: {}
|
||||
|
||||
json-stringify-safe@5.0.1: {}
|
||||
|
||||
json5@2.2.3: {}
|
||||
|
||||
jsonparse@1.3.1: {}
|
||||
|
||||
jsprim@1.4.2:
|
||||
dependencies:
|
||||
assert-plus: 1.0.0
|
||||
extsprintf: 1.3.0
|
||||
json-schema: 0.4.0
|
||||
verror: 1.10.0
|
||||
|
||||
jsprim@2.0.2:
|
||||
dependencies:
|
||||
assert-plus: 1.0.0
|
||||
@@ -7889,8 +7748,6 @@ snapshots:
|
||||
|
||||
number-is-nan@1.0.1: {}
|
||||
|
||||
oauth-sign@0.9.0: {}
|
||||
|
||||
object-assign@4.1.1: {}
|
||||
|
||||
object-copy@0.1.0:
|
||||
@@ -8104,8 +7961,6 @@ snapshots:
|
||||
commander: 9.5.0
|
||||
source-map-generator: 0.8.0
|
||||
|
||||
performance-now@2.1.0: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
|
||||
picomatch@2.3.1: {}
|
||||
@@ -8165,10 +8020,6 @@ snapshots:
|
||||
forwarded: 0.2.0
|
||||
ipaddr.js: 1.9.1
|
||||
|
||||
psl@1.15.0:
|
||||
dependencies:
|
||||
punycode: 2.3.1
|
||||
|
||||
pstree.remy@1.1.8: {}
|
||||
|
||||
public-encrypt@4.0.3:
|
||||
@@ -8203,8 +8054,6 @@ snapshots:
|
||||
dependencies:
|
||||
side-channel: 1.1.0
|
||||
|
||||
qs@6.5.3: {}
|
||||
|
||||
querystring-es3@0.2.1: {}
|
||||
|
||||
queue-microtask@1.2.3: {}
|
||||
@@ -8374,29 +8223,6 @@ snapshots:
|
||||
is-absolute: 1.0.0
|
||||
remove-trailing-separator: 1.1.0
|
||||
|
||||
request@2.88.2:
|
||||
dependencies:
|
||||
aws-sign2: 0.7.0
|
||||
aws4: 1.13.2
|
||||
caseless: 0.12.0
|
||||
combined-stream: 1.0.8
|
||||
extend: 3.0.2
|
||||
forever-agent: 0.6.1
|
||||
form-data: 2.3.3
|
||||
har-validator: 5.1.5
|
||||
http-signature: 1.2.0
|
||||
is-typedarray: 1.0.0
|
||||
isstream: 0.1.2
|
||||
json-stringify-safe: 5.0.1
|
||||
mime-types: 2.1.35
|
||||
oauth-sign: 0.9.0
|
||||
performance-now: 2.1.0
|
||||
qs: 6.5.3
|
||||
safe-buffer: 5.2.1
|
||||
tough-cookie: 2.5.0
|
||||
tunnel-agent: 0.6.0
|
||||
uuid: 3.4.0
|
||||
|
||||
require-directory@2.1.1: {}
|
||||
|
||||
require-main-filename@1.0.1: {}
|
||||
@@ -8617,6 +8443,8 @@ snapshots:
|
||||
|
||||
slash@1.0.0: {}
|
||||
|
||||
smart-buffer@4.2.0: {}
|
||||
|
||||
snapdragon-node@2.1.1:
|
||||
dependencies:
|
||||
define-property: 1.0.0
|
||||
@@ -8640,6 +8468,11 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- 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-resolve@0.5.3:
|
||||
@@ -8694,18 +8527,6 @@ snapshots:
|
||||
|
||||
sprintf-js@1.1.3: {}
|
||||
|
||||
sshpk@1.18.0:
|
||||
dependencies:
|
||||
asn1: 0.2.6
|
||||
assert-plus: 1.0.0
|
||||
bcrypt-pbkdf: 1.0.2
|
||||
dashdash: 1.14.1
|
||||
ecc-jsbn: 0.1.2
|
||||
getpass: 0.1.7
|
||||
jsbn: 0.1.1
|
||||
safer-buffer: 2.1.2
|
||||
tweetnacl: 0.14.5
|
||||
|
||||
stack-trace@0.0.10: {}
|
||||
|
||||
static-eval@0.2.4:
|
||||
@@ -8993,11 +8814,6 @@ snapshots:
|
||||
|
||||
touch@3.1.1: {}
|
||||
|
||||
tough-cookie@2.5.0:
|
||||
dependencies:
|
||||
psl: 1.15.0
|
||||
punycode: 2.3.1
|
||||
|
||||
transform-ast@2.4.4:
|
||||
dependencies:
|
||||
acorn-node: 1.8.2
|
||||
@@ -9010,12 +8826,6 @@ snapshots:
|
||||
|
||||
tty-browserify@0.0.1: {}
|
||||
|
||||
tunnel-agent@0.6.0:
|
||||
dependencies:
|
||||
safe-buffer: 5.2.1
|
||||
|
||||
tweetnacl@0.14.5: {}
|
||||
|
||||
type-check@0.3.2:
|
||||
dependencies:
|
||||
prelude-ls: 1.1.2
|
||||
@@ -9137,6 +8947,8 @@ snapshots:
|
||||
|
||||
undici-types@6.20.0: {}
|
||||
|
||||
undici@7.4.0: {}
|
||||
|
||||
unicode-canonical-property-names-ecmascript@2.0.1: {}
|
||||
|
||||
unicode-match-property-ecmascript@2.0.0:
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Base64 } from 'js-base64';
|
||||
import { Buffer } from 'buffer';
|
||||
import rs from '@/utils/rs';
|
||||
import YAML from '@/utils/yaml';
|
||||
import download from '@/utils/download';
|
||||
import download, { downloadFile } from '@/utils/download';
|
||||
import {
|
||||
isIPv4,
|
||||
isIPv6,
|
||||
@@ -24,6 +25,7 @@ import { getFlag, removeFlag, getISO, MMDB } from '@/utils/geo';
|
||||
import Gist from '@/utils/gist';
|
||||
import { isPresent } from './producers/utils';
|
||||
import { doh } from '@/utils/dns';
|
||||
import JSON5 from 'json5';
|
||||
|
||||
function preprocess(raw) {
|
||||
for (const processor of PROXY_PREPROCESSORS) {
|
||||
@@ -114,12 +116,7 @@ async function processFn(
|
||||
if (item.type.indexOf('Script') !== -1) {
|
||||
const { mode, content } = item.args;
|
||||
if (mode === 'link') {
|
||||
let noCache;
|
||||
let url = content || '';
|
||||
if (url.endsWith('#noCache')) {
|
||||
url = url.replace(/#noCache$/, '');
|
||||
noCache = true;
|
||||
}
|
||||
// extract link arguments
|
||||
const rawArgs = url.split('#');
|
||||
if (rawArgs.length > 1) {
|
||||
@@ -138,10 +135,17 @@ async function processFn(
|
||||
}
|
||||
}
|
||||
}
|
||||
url = `${url.split('#')[0]}${noCache ? '#noCache' : ''}`;
|
||||
const downloadUrlMatch = url.match(
|
||||
/^\/api\/(file|module)\/(.+)/,
|
||||
);
|
||||
url = `${url.split('#')[0]}${
|
||||
rawArgs[2]
|
||||
? `#${rawArgs[2]}`
|
||||
: $arguments?.noCache != null ||
|
||||
$arguments?.insecure != null
|
||||
? `#${rawArgs[1]}`
|
||||
: ''
|
||||
}`;
|
||||
const downloadUrlMatch = url
|
||||
.split('#')[0]
|
||||
.match(/^\/api\/(file|module)\/(.+)/);
|
||||
if (downloadUrlMatch) {
|
||||
let type = '';
|
||||
try {
|
||||
@@ -171,6 +175,17 @@ async function processFn(
|
||||
);
|
||||
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 {
|
||||
// if this is a remote script, download it
|
||||
try {
|
||||
@@ -328,8 +343,12 @@ export const ProxyUtils = {
|
||||
MMDB,
|
||||
Gist,
|
||||
download,
|
||||
downloadFile,
|
||||
isValidUUID,
|
||||
doh,
|
||||
Buffer,
|
||||
Base64,
|
||||
JSON5,
|
||||
};
|
||||
|
||||
function tryParse(parser, line) {
|
||||
@@ -518,6 +537,14 @@ function lastParse(proxy) {
|
||||
proxy['obfs-password'] = proxy.obfs;
|
||||
proxy.obfs = 'salamander';
|
||||
}
|
||||
if (
|
||||
['hysteria2'].includes(proxy.type) &&
|
||||
!proxy['obfs-password'] &&
|
||||
proxy['obfs_password']
|
||||
) {
|
||||
proxy['obfs-password'] = proxy['obfs_password'];
|
||||
delete proxy['obfs_password'];
|
||||
}
|
||||
if (['vless'].includes(proxy.type)) {
|
||||
// 删除 reality-opts: {}
|
||||
if (
|
||||
@@ -605,7 +632,7 @@ function lastParse(proxy) {
|
||||
proxy['tls-fingerprint'] = rs.generateFingerprint(caStr);
|
||||
}
|
||||
if (
|
||||
['shadowsocks'].includes(proxy.type) &&
|
||||
['ss'].includes(proxy.type) &&
|
||||
isPresent(proxy, 'shadow-tls-password')
|
||||
) {
|
||||
proxy.plugin = 'shadow-tls';
|
||||
|
||||
@@ -12,6 +12,8 @@ import getLoonParser from './peggy/loon';
|
||||
import getQXParser from './peggy/qx';
|
||||
import getTrojanURIParser from './peggy/trojan-uri';
|
||||
import $ from '@/core/app';
|
||||
import JSON5 from 'json5';
|
||||
import YAML from '@/utils/yaml';
|
||||
|
||||
import { Base64 } from 'js-base64';
|
||||
|
||||
@@ -128,13 +130,13 @@ function URI_SS() {
|
||||
// parse url
|
||||
let content = line.split('ss://')[1];
|
||||
|
||||
let name = line.split('#')[1];
|
||||
const proxy = {
|
||||
name: decodeURIComponent(line.split('#')[1]),
|
||||
type: 'ss',
|
||||
};
|
||||
content = content.split('#')[0]; // strip proxy name
|
||||
// handle IPV4 and IPV6
|
||||
let serverAndPortArray = content.match(/@([^/]*)(\/|$)/);
|
||||
let serverAndPortArray = content.match(/@([^/?]*)(\/|\?|$)/);
|
||||
|
||||
let rawUserInfoStr = decodeURIComponent(content.split('@')[0]); // 其实应该分隔之后, 用户名和密码再 decodeURIComponent. 但是问题不大
|
||||
let userInfoStr;
|
||||
@@ -260,6 +262,10 @@ function URI_SS() {
|
||||
if (/(&|\?)tfo=(1|true)/i.test(query)) {
|
||||
proxy.tfo = true;
|
||||
}
|
||||
if (name != null) {
|
||||
name = decodeURIComponent(name);
|
||||
}
|
||||
proxy.name = name ?? `SS ${proxy.server}:${proxy.port}`;
|
||||
return proxy;
|
||||
};
|
||||
return { name, test, parse };
|
||||
@@ -342,7 +348,7 @@ function URI_VMess() {
|
||||
};
|
||||
const parse = (line) => {
|
||||
line = line.split('vmess://')[1];
|
||||
let content = Base64.decode(line);
|
||||
let content = Base64.decode(line.replace(/\?.*?$/, ''));
|
||||
if (/=\s*vmess/.test(content)) {
|
||||
// Quantumult VMess URI format
|
||||
const partitions = content.split(',').map((p) => p.trim());
|
||||
@@ -435,7 +441,16 @@ function URI_VMess() {
|
||||
type: 'vmess',
|
||||
server,
|
||||
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,
|
||||
alterId: parseInt(
|
||||
getIfPresent(params.aid ?? params.alterId, 0),
|
||||
@@ -452,8 +467,12 @@ function URI_VMess() {
|
||||
);
|
||||
}
|
||||
// https://github.com/2dust/v2rayN/wiki/%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E(ver-2)
|
||||
if (proxy.tls && params.sni && params.sni !== '') {
|
||||
proxy.sni = params.sni;
|
||||
if (proxy.tls) {
|
||||
if (params.sni && params.sni !== '') {
|
||||
proxy.sni = params.sni;
|
||||
} else if (params.peer && params.peer !== '') {
|
||||
proxy.sni = params.peer;
|
||||
}
|
||||
}
|
||||
let httpupgrade = false;
|
||||
// handle obfs
|
||||
@@ -465,8 +484,8 @@ function URI_VMess() {
|
||||
['http'].includes(params.type)
|
||||
) {
|
||||
proxy.network = 'http';
|
||||
} else if (['grpc'].includes(params.net)) {
|
||||
proxy.network = 'grpc';
|
||||
} else if (['grpc', 'kcp', 'quic'].includes(params.net)) {
|
||||
proxy.network = params.net;
|
||||
} else if (
|
||||
params.net === 'httpupgrade' ||
|
||||
proxy.network === 'httpupgrade'
|
||||
@@ -492,6 +511,11 @@ function URI_VMess() {
|
||||
} catch (e) {}
|
||||
let transportPath = params.path;
|
||||
|
||||
// 补上默认 path
|
||||
if (['ws'].includes(proxy.network)) {
|
||||
transportPath = transportPath || '/';
|
||||
}
|
||||
|
||||
if (proxy.network === 'http') {
|
||||
if (transportHost) {
|
||||
// 1)http(tcp)->host中间逗号(,)隔开
|
||||
@@ -511,13 +535,28 @@ function URI_VMess() {
|
||||
}
|
||||
}
|
||||
// 传输层应该有配置, 暂时不考虑兼容不给配置的节点
|
||||
if (transportPath || transportHost) {
|
||||
if (
|
||||
transportPath ||
|
||||
transportHost ||
|
||||
['kcp', 'quic'].includes(proxy.network)
|
||||
) {
|
||||
if (['grpc'].includes(proxy.network)) {
|
||||
proxy[`${proxy.network}-opts`] = {
|
||||
'grpc-service-name': getIfNotBlank(transportPath),
|
||||
'_grpc-type': getIfNotBlank(params.type),
|
||||
'_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 {
|
||||
const opts = {
|
||||
path: getIfNotBlank(transportPath),
|
||||
@@ -533,6 +572,12 @@ function URI_VMess() {
|
||||
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;
|
||||
}
|
||||
};
|
||||
@@ -634,6 +679,9 @@ function URI_VLESS() {
|
||||
}
|
||||
if (!proxy.network && isShadowrocket && params.obfs) {
|
||||
proxy.network = params.obfs;
|
||||
if (['none'].includes(proxy.network)) {
|
||||
proxy.network = 'tcp';
|
||||
}
|
||||
}
|
||||
if (['websocket'].includes(proxy.network)) {
|
||||
proxy.network = 'ws';
|
||||
@@ -736,6 +784,8 @@ function URI_AnyTLS() {
|
||||
proxy[key] = value ? value.split(',') : undefined;
|
||||
} else if (['insecure'].includes(key)) {
|
||||
proxy['skip-cert-verify'] = /(TRUE)|1/i.test(value);
|
||||
} else if (['udp'].includes(key)) {
|
||||
proxy[key] = /(TRUE)|1/i.test(value);
|
||||
} else {
|
||||
proxy[key] = value;
|
||||
}
|
||||
@@ -954,6 +1004,9 @@ function URI_TUIC() {
|
||||
proxy.tfo = true;
|
||||
} else if (['disable-sni', 'reduce-rtt'].includes(key)) {
|
||||
proxy[key] = /(TRUE)|1/i.test(value);
|
||||
} else if (key === 'congestion-control') {
|
||||
proxy['congestion-controller'] = value;
|
||||
delete proxy[key];
|
||||
} else {
|
||||
proxy[key] = value;
|
||||
}
|
||||
@@ -1078,15 +1131,21 @@ function URI_Trojan() {
|
||||
function Clash_All() {
|
||||
const name = 'Clash Parser';
|
||||
const test = (line) => {
|
||||
let proxy;
|
||||
try {
|
||||
JSON.parse(line);
|
||||
proxy = JSON5.parse(line);
|
||||
} catch (e) {
|
||||
return false;
|
||||
proxy = YAML.parse(line);
|
||||
}
|
||||
return true;
|
||||
return !!proxy?.type;
|
||||
};
|
||||
const parse = (line) => {
|
||||
const proxy = JSON.parse(line);
|
||||
let proxy;
|
||||
try {
|
||||
proxy = JSON5.parse(line);
|
||||
} catch (e) {
|
||||
proxy = YAML.parse(line);
|
||||
}
|
||||
if (
|
||||
![
|
||||
'anytls',
|
||||
|
||||
@@ -39,12 +39,12 @@ start = (shadowsocksr/shadowsocks/vmess/vless/trojan/https/http/socks5/hysteria2
|
||||
return proxy;
|
||||
}
|
||||
|
||||
shadowsocksr = tag equals "shadowsocksr"i address method password (ssr_protocol/ssr_protocol_param/obfs_ssr/obfs_ssr_param/obfs_host/obfs_uri/fast_open/udp_relay/udp_port/shadow_tls_version/shadow_tls_sni/shadow_tls_password/ip_mode/others)*{
|
||||
shadowsocksr = tag equals "shadowsocksr"i address method password (ssr_protocol/ssr_protocol_param/obfs_ssr/obfs_ssr_param/obfs_host/obfs_uri/fast_open/udp_relay/udp_port/shadow_tls_version/shadow_tls_sni/shadow_tls_password/ip_mode/block_quic/others)*{
|
||||
proxy.type = "ssr";
|
||||
// handle ssr obfs
|
||||
proxy.obfs = obfs.type;
|
||||
}
|
||||
shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs_hostv)? (obfs_ss/obfs_host/obfs_uri/fast_open/udp_relay/udp_port/shadow_tls_version/shadow_tls_sni/shadow_tls_password/ip_mode/others)* {
|
||||
shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs_hostv)? (obfs_ss/obfs_host/obfs_uri/fast_open/udp_relay/udp_port/shadow_tls_version/shadow_tls_sni/shadow_tls_password/ip_mode/block_quic/others)* {
|
||||
proxy.type = "ss";
|
||||
// handle ss obfs
|
||||
if (obfs.type == "http" || obfs.type === "tls") {
|
||||
@@ -54,31 +54,31 @@ shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs
|
||||
$set(proxy, "plugin-opts.path", obfs.path);
|
||||
}
|
||||
}
|
||||
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/ip_mode/others)* {
|
||||
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/ip_mode/public_key/short_id/block_quic/others)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
proxy.alterId = proxy.alterId || 0;
|
||||
handleTransport();
|
||||
}
|
||||
vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* {
|
||||
vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/flow/public_key/short_id/block_quic/others)* {
|
||||
proxy.type = "vless";
|
||||
handleTransport();
|
||||
}
|
||||
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* {
|
||||
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
proxy.type = "trojan";
|
||||
handleTransport();
|
||||
}
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/udp_relay/fast_open/download_bandwidth/salamander_password/ecn/ip_mode/others)* {
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/udp_relay/fast_open/download_bandwidth/salamander_password/ecn/ip_mode/block_quic/others)* {
|
||||
proxy.type = "hysteria2";
|
||||
}
|
||||
https = tag equals "https"i address (username password)? (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* {
|
||||
https = tag equals "https"i address (username password)? (tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
proxy.tls = true;
|
||||
}
|
||||
http = tag equals "http"i address (username password)? (fast_open/udp_relay/ip_mode/others)* {
|
||||
http = tag equals "http"i address (username password)? (fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
}
|
||||
socks5 = tag equals "socks5"i address (username password)? (over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* {
|
||||
socks5 = tag equals "socks5"i address (username password)? (over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
proxy.type = "socks5";
|
||||
}
|
||||
|
||||
@@ -175,11 +175,16 @@ shadow_tls_sni = comma "shadow-tls-sni" equals match:[^,]+ { proxy["shadow-tls-s
|
||||
shadow_tls_password = comma "shadow-tls-password" equals match:[^,]+ { proxy["shadow-tls-password"] = match.join(""); }
|
||||
|
||||
over_tls = comma "over-tls" equals flag:bool { proxy.tls = flag; }
|
||||
tls_host = comma sni:("tls-name"/"sni") equals host:domain { proxy.sni = host; }
|
||||
tls_name = comma sni:("tls-name") equals host:domain { proxy.sni = host; }
|
||||
sni = comma sni:("sni") equals host:domain { proxy.sni = host; }
|
||||
tls_verification = comma "skip-cert-verify" equals flag:bool { proxy["skip-cert-verify"] = flag; }
|
||||
tls_cert_sha256 = comma "tls-cert-sha256" equals match:[^,]+ { proxy["tls-fingerprint"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
tls_pubkey_sha256 = comma "tls-pubkey-sha256" equals match:[^,]+ { proxy["tls-pubkey-sha256"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
|
||||
flow = comma "flow" equals match:[^,]+ { proxy["flow"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
public_key = comma "public-key" equals match:[^,]+ { proxy["reality-opts"] = proxy["reality-opts"] || {}; proxy["reality-opts"]["public-key"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
short_id = comma "short-id" equals match:[^,]+ { proxy["reality-opts"] = proxy["reality-opts"] || {}; proxy["reality-opts"]["short-id"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
|
||||
fast_open = comma "fast-open" equals flag:bool { proxy.tfo = flag; }
|
||||
udp_relay = comma "udp" equals flag:bool { proxy.udp = flag; }
|
||||
ip_mode = comma "ip-mode" equals match:[^,]+ { proxy["ip-version"] = match.join(""); }
|
||||
@@ -188,6 +193,8 @@ ecn = comma "ecn" equals flag:bool { proxy.ecn = flag; }
|
||||
download_bandwidth = comma "download-bandwidth" equals match:[^,]+ { proxy.down = match.join(""); }
|
||||
salamander_password = comma "salamander-password" equals match:[^,]+ { proxy['obfs-password'] = match.join(""); proxy.obfs = 'salamander'; }
|
||||
|
||||
block_quic = comma "block-quic" equals flag:bool { if(flag) proxy["block-quic"] = "on"; else proxy["block-quic"] = "off"; }
|
||||
|
||||
tag = match:[^=,]* { proxy.name = match.join("").trim(); }
|
||||
comma = _ "," _
|
||||
equals = _ "=" _
|
||||
|
||||
@@ -37,12 +37,12 @@ start = (shadowsocksr/shadowsocks/vmess/vless/trojan/https/http/socks5/hysteria2
|
||||
return proxy;
|
||||
}
|
||||
|
||||
shadowsocksr = tag equals "shadowsocksr"i address method password (ssr_protocol/ssr_protocol_param/obfs_ssr/obfs_ssr_param/obfs_host/obfs_uri/fast_open/udp_relay/udp_port/shadow_tls_version/shadow_tls_sni/shadow_tls_password/ip_mode/others)*{
|
||||
shadowsocksr = tag equals "shadowsocksr"i address method password (ssr_protocol/ssr_protocol_param/obfs_ssr/obfs_ssr_param/obfs_host/obfs_uri/fast_open/udp_relay/udp_port/shadow_tls_version/shadow_tls_sni/shadow_tls_password/ip_mode/block_quic/others)*{
|
||||
proxy.type = "ssr";
|
||||
// handle ssr obfs
|
||||
proxy.obfs = obfs.type;
|
||||
}
|
||||
shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs_hostv)? (obfs_ss/obfs_host/obfs_uri/fast_open/udp_relay/udp_port/shadow_tls_version/shadow_tls_sni/shadow_tls_password/ip_mode/others)* {
|
||||
shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs_hostv)? (obfs_ss/obfs_host/obfs_uri/fast_open/udp_relay/udp_port/shadow_tls_version/shadow_tls_sni/shadow_tls_password/ip_mode/block_quic/others)* {
|
||||
proxy.type = "ss";
|
||||
// handle ss obfs
|
||||
if (obfs.type == "http" || obfs.type === "tls") {
|
||||
@@ -52,31 +52,31 @@ shadowsocks = tag equals "shadowsocks"i address method password (obfs_typev obfs
|
||||
$set(proxy, "plugin-opts.path", obfs.path);
|
||||
}
|
||||
}
|
||||
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/ip_mode/others)* {
|
||||
vmess = tag equals "vmess"i address method uuid (transport/transport_host/transport_path/over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/vmess_alterId/fast_open/udp_relay/ip_mode/public_key/short_id/block_quic/others)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
proxy.alterId = proxy.alterId || 0;
|
||||
handleTransport();
|
||||
}
|
||||
vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* {
|
||||
vless = tag equals "vless"i address uuid (transport/transport_host/transport_path/over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/flow/public_key/short_id/block_quic/others)* {
|
||||
proxy.type = "vless";
|
||||
handleTransport();
|
||||
}
|
||||
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* {
|
||||
trojan = tag equals "trojan"i address password (transport/transport_host/transport_path/over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
proxy.type = "trojan";
|
||||
handleTransport();
|
||||
}
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/udp_relay/fast_open/download_bandwidth/salamander_password/ecn/ip_mode/others)* {
|
||||
hysteria2 = tag equals "hysteria2"i address password (tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/udp_relay/fast_open/download_bandwidth/salamander_password/ecn/ip_mode/block_quic/others)* {
|
||||
proxy.type = "hysteria2";
|
||||
}
|
||||
https = tag equals "https"i address (username password)? (tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* {
|
||||
https = tag equals "https"i address (username password)? (tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
proxy.tls = true;
|
||||
}
|
||||
http = tag equals "http"i address (username password)? (fast_open/udp_relay/ip_mode/others)* {
|
||||
http = tag equals "http"i address (username password)? (fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
proxy.type = "http";
|
||||
}
|
||||
socks5 = tag equals "socks5"i address (username password)? (over_tls/tls_host/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/others)* {
|
||||
socks5 = tag equals "socks5"i address (username password)? (over_tls/tls_name/sni/tls_verification/tls_cert_sha256/tls_pubkey_sha256/fast_open/udp_relay/ip_mode/block_quic/others)* {
|
||||
proxy.type = "socks5";
|
||||
}
|
||||
|
||||
@@ -173,11 +173,16 @@ shadow_tls_sni = comma "shadow-tls-sni" equals match:[^,]+ { proxy["shadow-tls-s
|
||||
shadow_tls_password = comma "shadow-tls-password" equals match:[^,]+ { proxy["shadow-tls-password"] = match.join(""); }
|
||||
|
||||
over_tls = comma "over-tls" equals flag:bool { proxy.tls = flag; }
|
||||
tls_host = comma sni:("tls-name"/"sni") equals host:domain { proxy.sni = host; }
|
||||
tls_name = comma sni:("tls-name") equals host:domain { proxy.sni = host; }
|
||||
sni = comma sni:("sni") equals host:domain { proxy.sni = host; }
|
||||
tls_verification = comma "skip-cert-verify" equals flag:bool { proxy["skip-cert-verify"] = flag; }
|
||||
tls_cert_sha256 = comma "tls-cert-sha256" equals match:[^,]+ { proxy["tls-fingerprint"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
tls_pubkey_sha256 = comma "tls-pubkey-sha256" equals match:[^,]+ { proxy["tls-pubkey-sha256"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
|
||||
flow = comma "flow" equals match:[^,]+ { proxy["flow"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
public_key = comma "public-key" equals match:[^,]+ { proxy["reality-opts"] = proxy["reality-opts"] || {}; proxy["reality-opts"]["public-key"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
short_id = comma "short-id" equals match:[^,]+ { proxy["reality-opts"] = proxy["reality-opts"] || {}; proxy["reality-opts"]["short-id"] = match.join("").replace(/^"(.*)"$/, '$1'); }
|
||||
|
||||
fast_open = comma "fast-open" equals flag:bool { proxy.tfo = flag; }
|
||||
udp_relay = comma "udp" equals flag:bool { proxy.udp = flag; }
|
||||
ip_mode = comma "ip-mode" equals match:[^,]+ { proxy["ip-version"] = match.join(""); }
|
||||
@@ -186,6 +191,8 @@ ecn = comma "ecn" equals flag:bool { proxy.ecn = flag; }
|
||||
download_bandwidth = comma "download-bandwidth" equals match:[^,]+ { proxy.down = match.join(""); }
|
||||
salamander_password = comma "salamander-password" equals match:[^,]+ { proxy['obfs-password'] = match.join(""); proxy.obfs = 'salamander'; }
|
||||
|
||||
block_quic = comma "block-quic" equals flag:bool { if(flag) proxy["block-quic"] = "on"; else proxy["block-quic"] = "off"; }
|
||||
|
||||
tag = match:[^=,]* { proxy.name = match.join("").trim(); }
|
||||
comma = _ "," _
|
||||
equals = _ "=" _
|
||||
|
||||
@@ -49,7 +49,7 @@ trojan = "trojan" equals address
|
||||
}
|
||||
|
||||
shadowsocks = "shadowsocks" equals address
|
||||
(password/method/obfs_ssr/obfs_ss/obfs_host/obfs_uri/ssr_protocol/ssr_protocol_param/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/udp_relay/udp_over_tcp/fast_open/tag/server_check_url/others)* {
|
||||
(password/method/obfs_ssr/obfs_ss/obfs_host/obfs_uri/ssr_protocol/ssr_protocol_param/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/udp_relay/udp_over_tcp_new/fast_open/tag/server_check_url/others)* {
|
||||
if (proxy.protocol || proxy.type === "ssr") {
|
||||
proxy.type = "ssr";
|
||||
if (!proxy.protocol) {
|
||||
@@ -86,10 +86,10 @@ vmess = "vmess" equals address
|
||||
(uuid/method/over_tls/tls_host/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/tag/obfs/obfs_host/obfs_uri/udp_relay/udp_over_tcp/fast_open/aead/server_check_url/others)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
if (proxy.aead) {
|
||||
proxy.alterId = 0;
|
||||
if (proxy.aead === false) {
|
||||
proxy.alterId = 1;
|
||||
} else {
|
||||
proxy.alterId = proxy.alterId || 0;
|
||||
proxy.alterId = 0;
|
||||
}
|
||||
handleObfs();
|
||||
}
|
||||
@@ -145,18 +145,20 @@ port = digits:[0-9]+ {
|
||||
}
|
||||
}
|
||||
|
||||
username = comma "username" equals username:[^=,]+ { proxy.username = username.join("").trim(); }
|
||||
password = comma "password" equals password:[^=,]+ { proxy.password = password.join("").trim(); }
|
||||
uuid = comma "password" equals uuid:[^=,]+ { proxy.uuid = uuid.join("").trim(); }
|
||||
username = comma "username" equals username:[^,]+ { proxy.username = username.join("").trim(); }
|
||||
password = comma "password" equals password:[^,]+ { proxy.password = password.join("").trim(); }
|
||||
uuid = comma "password" equals uuid:[^,]+ { proxy.uuid = uuid.join("").trim(); }
|
||||
|
||||
method = comma "method" equals cipher:cipher {
|
||||
proxy.cipher = cipher;
|
||||
};
|
||||
cipher = ("aes-128-cfb"/"aes-128-ctr"/"aes-128-gcm"/"aes-192-cfb"/"aes-192-ctr"/"aes-192-gcm"/"aes-256-cfb"/"aes-256-ctr"/"aes-256-gcm"/"bf-cfb"/"cast5-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"des-cfb"/"none"/"rc2-cfb"/"rc4-md5-6"/"rc4-md5"/"salsa20"/"xchacha20-ietf-poly1305");
|
||||
cipher = ("aes-128-cfb"/"aes-128-ctr"/"aes-128-gcm"/"aes-192-cfb"/"aes-192-ctr"/"aes-192-gcm"/"aes-256-cfb"/"aes-256-ctr"/"aes-256-gcm"/"bf-cfb"/"cast5-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"des-cfb"/"none"/"rc2-cfb"/"rc4-md5-6"/"rc4-md5"/"salsa20"/"xchacha20-ietf-poly1305"/"2022-blake3-aes-128-gcm"/"2022-blake3-aes-256-gcm");
|
||||
aead = comma "aead" equals flag:bool { proxy.aead = flag; }
|
||||
|
||||
udp_relay = comma "udp-relay" equals flag:bool { proxy.udp = flag; }
|
||||
udp_over_tcp = comma "udp-over-tcp" equals flag:bool { throw new Error("UDP over TCP is not supported"); }
|
||||
udp_over_tcp_new = comma "udp-over-tcp" equals param:$[^=,]+ { if (param === "sp.v1") { proxy["udp-over-tcp"] = true; proxy["udp-over-tcp-version"] = 1; } else if (param === "sp.v2") { proxy["udp-over-tcp"] = true; proxy["udp-over-tcp-version"] = 2; } else if (param === "true") { proxy["_ssr_python_uot"] = true; } else { throw new Error("Invalid value for udp-over-tcp"); } }
|
||||
|
||||
fast_open = comma "fast-open" equals flag:bool { proxy.tfo = flag; }
|
||||
|
||||
over_tls = comma "over-tls" equals flag:bool { proxy.tls = flag; }
|
||||
|
||||
@@ -47,7 +47,7 @@ trojan = "trojan" equals address
|
||||
}
|
||||
|
||||
shadowsocks = "shadowsocks" equals address
|
||||
(password/method/obfs_ssr/obfs_ss/obfs_host/obfs_uri/ssr_protocol/ssr_protocol_param/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/udp_relay/udp_over_tcp/fast_open/tag/server_check_url/others)* {
|
||||
(password/method/obfs_ssr/obfs_ss/obfs_host/obfs_uri/ssr_protocol/ssr_protocol_param/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/udp_relay/udp_over_tcp_new/fast_open/tag/server_check_url/others)* {
|
||||
if (proxy.protocol || proxy.type === "ssr") {
|
||||
proxy.type = "ssr";
|
||||
if (!proxy.protocol) {
|
||||
@@ -84,10 +84,10 @@ vmess = "vmess" equals address
|
||||
(uuid/method/over_tls/tls_host/tls_pubkey_sha256/tls_alpn/tls_no_session_ticket/tls_no_session_reuse/tls_fingerprint/tls_verification/tag/obfs/obfs_host/obfs_uri/udp_relay/udp_over_tcp/fast_open/aead/server_check_url/others)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
if (proxy.aead) {
|
||||
proxy.alterId = 0;
|
||||
if (proxy.aead === false) {
|
||||
proxy.alterId = 1;
|
||||
} else {
|
||||
proxy.alterId = proxy.alterId || 0;
|
||||
proxy.alterId = 0;
|
||||
}
|
||||
handleObfs();
|
||||
}
|
||||
@@ -143,18 +143,20 @@ port = digits:[0-9]+ {
|
||||
}
|
||||
}
|
||||
|
||||
username = comma "username" equals username:[^=,]+ { proxy.username = username.join("").trim(); }
|
||||
password = comma "password" equals password:[^=,]+ { proxy.password = password.join("").trim(); }
|
||||
uuid = comma "password" equals uuid:[^=,]+ { proxy.uuid = uuid.join("").trim(); }
|
||||
username = comma "username" equals username:[^,]+ { proxy.username = username.join("").trim(); }
|
||||
password = comma "password" equals password:[^,]+ { proxy.password = password.join("").trim(); }
|
||||
uuid = comma "password" equals uuid:[^,]+ { proxy.uuid = uuid.join("").trim(); }
|
||||
|
||||
method = comma "method" equals cipher:cipher {
|
||||
proxy.cipher = cipher;
|
||||
};
|
||||
cipher = ("aes-128-cfb"/"aes-128-ctr"/"aes-128-gcm"/"aes-192-cfb"/"aes-192-ctr"/"aes-192-gcm"/"aes-256-cfb"/"aes-256-ctr"/"aes-256-gcm"/"bf-cfb"/"cast5-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"des-cfb"/"none"/"rc2-cfb"/"rc4-md5-6"/"rc4-md5"/"salsa20"/"xchacha20-ietf-poly1305");
|
||||
cipher = ("aes-128-cfb"/"aes-128-ctr"/"aes-128-gcm"/"aes-192-cfb"/"aes-192-ctr"/"aes-192-gcm"/"aes-256-cfb"/"aes-256-ctr"/"aes-256-gcm"/"bf-cfb"/"cast5-cfb"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"des-cfb"/"none"/"rc2-cfb"/"rc4-md5-6"/"rc4-md5"/"salsa20"/"xchacha20-ietf-poly1305"/"2022-blake3-aes-128-gcm"/"2022-blake3-aes-256-gcm");
|
||||
aead = comma "aead" equals flag:bool { proxy.aead = flag; }
|
||||
|
||||
udp_relay = comma "udp-relay" equals flag:bool { proxy.udp = flag; }
|
||||
udp_over_tcp = comma "udp-over-tcp" equals flag:bool { throw new Error("UDP over TCP is not supported"); }
|
||||
udp_over_tcp_new = comma "udp-over-tcp" equals param:$[^=,]+ { if (param === "sp.v1") { proxy["udp-over-tcp"] = true; proxy["udp-over-tcp-version"] = 1; } else if (param === "sp.v2") { proxy["udp-over-tcp"] = true; proxy["udp-over-tcp-version"] = 2; } else if (param === "true") { proxy["_ssr_python_uot"] = true; } else { throw new Error("Invalid value for udp-over-tcp"); } }
|
||||
|
||||
fast_open = comma "fast-open" equals flag:bool { proxy.tfo = flag; }
|
||||
|
||||
over_tls = comma "over-tls" equals flag:bool { proxy.tls = flag; }
|
||||
|
||||
@@ -55,10 +55,11 @@ shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/
|
||||
vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls/sni/tls_fingerprint/tls_verification/fast_open/tfo/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
// Surfboard 与 Surge 默认不一致, 不管 Surfboard https://getsurfboard.com/docs/profile-format/proxy/external-proxy/vmess
|
||||
if (proxy.aead) {
|
||||
proxy.alterId = 0;
|
||||
} else {
|
||||
proxy.alterId = proxy.alterId || 0;
|
||||
proxy.alterId = 1;
|
||||
}
|
||||
handleWebsocket();
|
||||
handleShadowTLS();
|
||||
@@ -104,11 +105,11 @@ wireguard = tag equals "wireguard" (section_name/no_error_alert/ip_version/under
|
||||
proxy.type = "wireguard-surge";
|
||||
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";
|
||||
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";
|
||||
handleShadowTLS();
|
||||
}
|
||||
@@ -120,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)* {
|
||||
proxy.type = "direct";
|
||||
}
|
||||
|
||||
address = comma server:server comma port:port {
|
||||
proxy.server = server;
|
||||
proxy.port = port;
|
||||
@@ -178,8 +178,8 @@ username = & {
|
||||
peg$currPos = end;
|
||||
return true;
|
||||
}
|
||||
} { proxy.username = $.username; }
|
||||
password = comma match:[^,]+ { proxy.password = match.join(""); }
|
||||
} { proxy.username = $.username.trim().replace(/^"(.*?)"$/, '$1').replace(/^'(.*?)'$/, '$1'); }
|
||||
password = comma match:[^,]+ { proxy.password = match.join("").replace(/^"(.*)"$/, '$1').replace(/^'(.*?)'$/, '$1'); }
|
||||
|
||||
tls = comma "tls" equals flag:bool { proxy.tls = flag; }
|
||||
sni = comma "sni" equals sni:("off"/domain) {
|
||||
@@ -195,7 +195,7 @@ tls_fingerprint = comma "server-cert-fingerprint-sha256" equals tls_fingerprint:
|
||||
snell_psk = comma "psk" equals match:[^,]+ { proxy.psk = match.join(""); }
|
||||
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'); }
|
||||
vmess_uuid = comma "username" equals match:[^,]+ { proxy.uuid = match.join(""); }
|
||||
vmess_aead = comma "vmess-aead" equals flag:bool { proxy.aead = flag; }
|
||||
@@ -211,11 +211,11 @@ ws_headers = comma "ws-headers" equals headers:$[^,]+ {
|
||||
const result = {};
|
||||
pairs.forEach(pair => {
|
||||
const [key, value] = pair.trim().split(":");
|
||||
result[key.trim()] = value.trim();
|
||||
result[key.trim()] = value.trim().replace(/^"(.*?)"$/, '$1').replace(/^'(.*?)'$/, '$1');
|
||||
})
|
||||
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_host = comma "obfs-host" equals host:domain { obfs.host = host; };
|
||||
|
||||
@@ -53,10 +53,11 @@ shadowsocks = tag equals "ss" address (method/passwordk/obfs/obfs_host/obfs_uri/
|
||||
vmess = tag equals "vmess" address (vmess_uuid/vmess_aead/ws/ws_path/ws_headers/method/ip_version/underlying_proxy/tos/allow_other_interface/interface/test_url/test_udp/test_timeout/hybrid/no_error_alert/tls/sni/tls_fingerprint/tls_verification/fast_open/tfo/udp_relay/shadow_tls_version/shadow_tls_sni/shadow_tls_password/block_quic/others)* {
|
||||
proxy.type = "vmess";
|
||||
proxy.cipher = proxy.cipher || "none";
|
||||
// Surfboard 与 Surge 默认不一致, 不管 Surfboard https://getsurfboard.com/docs/profile-format/proxy/external-proxy/vmess
|
||||
if (proxy.aead) {
|
||||
proxy.alterId = 0;
|
||||
} else {
|
||||
proxy.alterId = proxy.alterId || 0;
|
||||
proxy.alterId = 1;
|
||||
}
|
||||
handleWebsocket();
|
||||
handleShadowTLS();
|
||||
@@ -175,8 +176,8 @@ username = & {
|
||||
peg$currPos = end;
|
||||
return true;
|
||||
}
|
||||
} { proxy.username = $.username; }
|
||||
password = comma match:[^,]+ { proxy.password = match.join(""); }
|
||||
} { proxy.username = $.username.trim().replace(/^"(.*?)"$/, '$1').replace(/^'(.*?)'$/, '$1'); }
|
||||
password = comma match:[^,]+ { proxy.password = match.join("").replace(/^"(.*)"$/, '$1').replace(/^'(.*?)'$/, '$1'); }
|
||||
|
||||
tls = comma "tls" equals flag:bool { proxy.tls = flag; }
|
||||
sni = comma "sni" equals sni:("off"/domain) {
|
||||
@@ -192,7 +193,7 @@ tls_fingerprint = comma "server-cert-fingerprint-sha256" equals tls_fingerprint:
|
||||
snell_psk = comma "psk" equals match:[^,]+ { proxy.psk = match.join(""); }
|
||||
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'); }
|
||||
vmess_uuid = comma "username" equals match:[^,]+ { proxy.uuid = match.join(""); }
|
||||
vmess_aead = comma "vmess-aead" equals flag:bool { proxy.aead = flag; }
|
||||
@@ -208,11 +209,11 @@ ws_headers = comma "ws-headers" equals headers:$[^,]+ {
|
||||
const result = {};
|
||||
pairs.forEach(pair => {
|
||||
const [key, value] = pair.trim().split(":");
|
||||
result[key.trim()] = value.trim();
|
||||
result[key.trim()] = value.trim().replace(/^"(.*?)"$/, '$1').replace(/^'(.*?)'$/, '$1');
|
||||
})
|
||||
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_host = comma "obfs-host" equals host:domain { obfs.host = host; };
|
||||
|
||||
@@ -50,6 +50,26 @@ function Base64Encoded() {
|
||||
return { name, test, parse };
|
||||
}
|
||||
|
||||
function fallbackBase64Encoded() {
|
||||
const name = 'Fallback Base64 Pre-processor';
|
||||
|
||||
const test = function (raw) {
|
||||
return true;
|
||||
};
|
||||
const parse = function (raw) {
|
||||
const decoded = Base64.decode(raw);
|
||||
if (!/^\w+(:\/\/|\s*?=\s*?)\w+/m.test(decoded)) {
|
||||
$.error(
|
||||
`Fallback Base64 Pre-processor error: decoded line does not start with protocol`,
|
||||
);
|
||||
return raw;
|
||||
}
|
||||
|
||||
return decoded;
|
||||
};
|
||||
return { name, test, parse };
|
||||
}
|
||||
|
||||
function Clash() {
|
||||
const name = 'Clash Pre-processor';
|
||||
const test = function (raw) {
|
||||
@@ -75,6 +95,8 @@ function Clash() {
|
||||
// 是否被引号包裹
|
||||
if (/^(['"]).*\1$/.test(afterTrim)) {
|
||||
return `short-id: ${afterTrim}`;
|
||||
} else if (['null'].includes(afterTrim)) {
|
||||
return `short-id: ${afterTrim}`;
|
||||
} else {
|
||||
return `short-id: "${afterTrim}"`;
|
||||
}
|
||||
@@ -161,4 +183,11 @@ function FullConfig() {
|
||||
return { name, test, parse };
|
||||
}
|
||||
|
||||
export default [HTML(), Clash(), Base64Encoded(), SSD(), FullConfig()];
|
||||
export default [
|
||||
HTML(),
|
||||
Clash(),
|
||||
Base64Encoded(),
|
||||
SSD(),
|
||||
FullConfig(),
|
||||
fallbackBase64Encoded(),
|
||||
];
|
||||
|
||||
@@ -284,7 +284,15 @@ function SortOperator(order = 'asc') {
|
||||
}
|
||||
|
||||
// sort by regex
|
||||
function RegexSortOperator(expressions) {
|
||||
function RegexSortOperator(input) {
|
||||
const order = input.order || 'asc';
|
||||
let expressions = input.expressions;
|
||||
if (Array.isArray(input)) {
|
||||
expressions = input;
|
||||
}
|
||||
if (!Array.isArray(expressions)) {
|
||||
expressions = [];
|
||||
}
|
||||
return {
|
||||
name: 'Regex Sort Operator',
|
||||
func: (proxies) => {
|
||||
@@ -295,8 +303,13 @@ function RegexSortOperator(expressions) {
|
||||
if (oA && !oB) return -1;
|
||||
if (oB && !oA) return 1;
|
||||
if (oA && oB) return oA < oB ? -1 : 1;
|
||||
if ((!oA && !oB) || (oA && oB && oA === oB))
|
||||
return a.name < b.name ? -1 : 1; // fallback to normal sort
|
||||
if (order === 'original') {
|
||||
return 0;
|
||||
} else if (order === 'desc') {
|
||||
return a.name < b.name ? 1 : -1;
|
||||
} else {
|
||||
return a.name < b.name ? -1 : 1;
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -610,9 +623,11 @@ const DOMAIN_RESOLVERS = {
|
||||
const cached = resourceCache.get(id);
|
||||
if (!noCache && cached) return cached;
|
||||
const resp = await $.http.get({
|
||||
url: `http://223.6.6.6/resolve?edns_client_subnet=${edns}/24&name=${encodeURIComponent(
|
||||
domain,
|
||||
)}&type=${type === 'IPv6' ? 'AAAA' : 'A'}&short=1`,
|
||||
url: `http://223.6.6.6/resolve?edns_client_subnet=${edns}/${
|
||||
isIPv4(edns) ? 24 : 56
|
||||
}&name=${encodeURIComponent(domain)}&type=${
|
||||
type === 'IPv6' ? 'AAAA' : 'A'
|
||||
}&short=1`,
|
||||
headers: {
|
||||
accept: 'application/dns-json',
|
||||
},
|
||||
@@ -1128,6 +1143,10 @@ function createDynamicFunction(name, script, $arguments, $options) {
|
||||
'$httpClient',
|
||||
'$notification',
|
||||
'ProxyUtils',
|
||||
'yaml',
|
||||
'Buffer',
|
||||
'b64d',
|
||||
'b64e',
|
||||
'scriptResourceCache',
|
||||
'flowUtils',
|
||||
'produceArtifact',
|
||||
@@ -1145,6 +1164,10 @@ function createDynamicFunction(name, script, $arguments, $options) {
|
||||
// eslint-disable-next-line no-undef
|
||||
$notification,
|
||||
ProxyUtils,
|
||||
ProxyUtils.yaml,
|
||||
ProxyUtils.Buffer,
|
||||
ProxyUtils.Base64.decode,
|
||||
ProxyUtils.Base64.encode,
|
||||
scriptResourceCache,
|
||||
flowUtils,
|
||||
produceArtifact,
|
||||
@@ -1157,6 +1180,10 @@ function createDynamicFunction(name, script, $arguments, $options) {
|
||||
'$substore',
|
||||
'lodash',
|
||||
'ProxyUtils',
|
||||
'yaml',
|
||||
'Buffer',
|
||||
'b64d',
|
||||
'b64e',
|
||||
'scriptResourceCache',
|
||||
'flowUtils',
|
||||
'produceArtifact',
|
||||
@@ -1168,6 +1195,10 @@ function createDynamicFunction(name, script, $arguments, $options) {
|
||||
$,
|
||||
lodash,
|
||||
ProxyUtils,
|
||||
ProxyUtils.yaml,
|
||||
ProxyUtils.Buffer,
|
||||
ProxyUtils.Base64.decode,
|
||||
ProxyUtils.Base64.encode,
|
||||
scriptResourceCache,
|
||||
flowUtils,
|
||||
produceArtifact,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { isPresent } from '@/core/proxy-utils/producers/utils';
|
||||
import $ from '@/core/app';
|
||||
|
||||
export default function Clash_Producer() {
|
||||
const type = 'ALL';
|
||||
@@ -40,12 +41,17 @@ export default function Clash_Producer() {
|
||||
'chacha20-ietf-poly1305',
|
||||
'xchacha20-ietf-poly1305',
|
||||
].includes(proxy.cipher)) ||
|
||||
(proxy.type === 'snell' && String(proxy.version) === '4') ||
|
||||
(proxy.type === 'snell' && proxy.version >= 4) ||
|
||||
(proxy.type === 'vless' &&
|
||||
(typeof proxy.flow !== 'undefined' ||
|
||||
proxy['reality-opts']))
|
||||
) {
|
||||
return false;
|
||||
} else if (proxy['underlying-proxy'] || proxy['dialer-proxy']) {
|
||||
$.error(
|
||||
`Clash 不支持前置代理字段. 已过滤节点 ${proxy.name}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
@@ -128,6 +134,18 @@ export default function Clash_Producer() {
|
||||
proxy['h2-opts'].headers.host = [host];
|
||||
}
|
||||
}
|
||||
if (proxy.network === 'ws') {
|
||||
const wsPath = proxy['ws-opts']?.path;
|
||||
const reg = /^(.*?)(?:\?ed=(\d+))?$/;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [_, path = '', ed = ''] = reg.exec(wsPath);
|
||||
proxy['ws-opts'].path = path;
|
||||
if (ed !== '') {
|
||||
proxy['ws-opts']['early-data-header-name'] =
|
||||
'Sec-WebSocket-Protocol';
|
||||
proxy['ws-opts']['max-early-data'] = parseInt(ed, 10);
|
||||
}
|
||||
}
|
||||
if (proxy['plugin-opts']?.tls) {
|
||||
if (isPresent(proxy, 'skip-cert-verify')) {
|
||||
proxy['plugin-opts']['skip-cert-verify'] =
|
||||
@@ -152,11 +170,6 @@ export default function Clash_Producer() {
|
||||
}
|
||||
delete proxy['tls-fingerprint'];
|
||||
|
||||
if (proxy['underlying-proxy']) {
|
||||
proxy['dialer-proxy'] = proxy['underlying-proxy'];
|
||||
}
|
||||
delete proxy['underlying-proxy'];
|
||||
|
||||
if (isPresent(proxy, 'tls') && typeof proxy.tls !== 'boolean') {
|
||||
delete proxy.tls;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
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() {
|
||||
const type = 'ALL';
|
||||
const produce = (proxies, type, opts = {}) => {
|
||||
const list = proxies
|
||||
.filter((proxy) => {
|
||||
if (opts['include-unsupported-proxy']) return true;
|
||||
if (proxy.type === 'snell' && String(proxy.version) === '4') {
|
||||
if (proxy.type === 'snell' && proxy.version >= 4) {
|
||||
return false;
|
||||
} else if (['juicity'].includes(proxy.type)) {
|
||||
return false;
|
||||
@@ -190,6 +198,18 @@ export default function ClashMeta_Producer() {
|
||||
proxy['h2-opts'].headers.host = [host];
|
||||
}
|
||||
}
|
||||
if (proxy.network === 'ws') {
|
||||
const wsPath = proxy['ws-opts']?.path;
|
||||
const reg = /^(.*?)(?:\?ed=(\d+))?$/;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [_, path = '', ed = ''] = reg.exec(wsPath);
|
||||
proxy['ws-opts'].path = path;
|
||||
if (ed !== '') {
|
||||
proxy['ws-opts']['early-data-header-name'] =
|
||||
'Sec-WebSocket-Protocol';
|
||||
proxy['ws-opts']['max-early-data'] = parseInt(ed, 10);
|
||||
}
|
||||
}
|
||||
|
||||
if (proxy['plugin-opts']?.tls) {
|
||||
if (isPresent(proxy, 'skip-cert-verify')) {
|
||||
@@ -242,6 +262,11 @@ export default function ClashMeta_Producer() {
|
||||
delete proxy[`${proxy.network}-opts`]['_grpc-type'];
|
||||
delete proxy[`${proxy.network}-opts`]['_grpc-authority'];
|
||||
}
|
||||
|
||||
if (proxy['ip-version']) {
|
||||
proxy['ip-version'] =
|
||||
ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
}
|
||||
return proxy;
|
||||
});
|
||||
|
||||
|
||||
@@ -371,12 +371,37 @@ export default function Egern_Producer() {
|
||||
};
|
||||
}
|
||||
}
|
||||
if (
|
||||
['ss'].includes(original.type) &&
|
||||
proxy.shadow_tls &&
|
||||
original['udp-port'] > 0 &&
|
||||
original['udp-port'] <= 65535
|
||||
) {
|
||||
proxy['udp_port'] = original['udp-port'];
|
||||
}
|
||||
|
||||
delete proxy.subName;
|
||||
delete proxy.collectionName;
|
||||
delete proxy.id;
|
||||
delete proxy.resolved;
|
||||
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') {
|
||||
for (const key in proxy) {
|
||||
if (proxy[key] == null || /^_/i.test(key)) {
|
||||
|
||||
@@ -21,9 +21,9 @@ export default function Loon_Producer() {
|
||||
case 'trojan':
|
||||
return trojan(proxy);
|
||||
case 'vmess':
|
||||
return vmess(proxy);
|
||||
return vmess(proxy, opts['include-unsupported-proxy']);
|
||||
case 'vless':
|
||||
return vless(proxy);
|
||||
return vless(proxy, opts['include-unsupported-proxy']);
|
||||
case 'http':
|
||||
return http(proxy);
|
||||
case 'socks5':
|
||||
@@ -133,6 +133,13 @@ function shadowsocks(proxy) {
|
||||
// tfo
|
||||
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// block-quic
|
||||
if (proxy['block-quic'] === 'on') {
|
||||
result.append(',block-quic=true');
|
||||
} else if (proxy['block-quic'] === 'off') {
|
||||
result.append(',block-quic=false');
|
||||
}
|
||||
|
||||
// udp
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
@@ -144,7 +151,7 @@ function shadowsocks(proxy) {
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
function shadowsocksr(proxy, includeUnsupportedProxy) {
|
||||
function shadowsocksr(proxy) {
|
||||
const result = new Result(proxy);
|
||||
result.append(
|
||||
`${proxy.name}=shadowsocksr,${proxy.server},${proxy.port},${proxy.cipher},"${proxy.password}"`,
|
||||
@@ -203,6 +210,13 @@ function shadowsocksr(proxy, includeUnsupportedProxy) {
|
||||
// tfo
|
||||
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// block-quic
|
||||
if (proxy['block-quic'] === 'on') {
|
||||
result.append(',block-quic=true');
|
||||
} else if (proxy['block-quic'] === 'off') {
|
||||
result.append(',block-quic=false');
|
||||
}
|
||||
|
||||
// udp
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
@@ -259,6 +273,13 @@ function trojan(proxy) {
|
||||
// tfo
|
||||
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// block-quic
|
||||
if (proxy['block-quic'] === 'on') {
|
||||
result.append(',block-quic=true');
|
||||
} else if (proxy['block-quic'] === 'off') {
|
||||
result.append(',block-quic=false');
|
||||
}
|
||||
|
||||
// udp
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
@@ -270,6 +291,8 @@ function trojan(proxy) {
|
||||
}
|
||||
|
||||
function vmess(proxy) {
|
||||
const isReality = !!proxy['reality-opts'];
|
||||
|
||||
const result = new Result(proxy);
|
||||
result.append(
|
||||
`${proxy.name}=vmess,${proxy.server},${proxy.port},${proxy.cipher},"${proxy.uuid}"`,
|
||||
@@ -317,20 +340,32 @@ function vmess(proxy) {
|
||||
'skip-cert-verify',
|
||||
);
|
||||
|
||||
// sni
|
||||
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni');
|
||||
result.appendIfPresent(
|
||||
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
||||
'tls-fingerprint',
|
||||
);
|
||||
result.appendIfPresent(
|
||||
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
|
||||
'tls-pubkey-sha256',
|
||||
);
|
||||
if (isReality) {
|
||||
result.appendIfPresent(`,sni=${proxy.sni}`, 'sni');
|
||||
result.appendIfPresent(
|
||||
`,public-key="${proxy['reality-opts']['public-key']}"`,
|
||||
'reality-opts.public-key',
|
||||
);
|
||||
result.appendIfPresent(
|
||||
`,short-id=${proxy['reality-opts']['short-id']}`,
|
||||
'reality-opts.short-id',
|
||||
);
|
||||
} else {
|
||||
// sni
|
||||
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni');
|
||||
result.appendIfPresent(
|
||||
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
||||
'tls-fingerprint',
|
||||
);
|
||||
result.appendIfPresent(
|
||||
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
|
||||
'tls-pubkey-sha256',
|
||||
);
|
||||
}
|
||||
|
||||
// AEAD
|
||||
if (isPresent(proxy, 'aead')) {
|
||||
result.append(`,alterId=0`);
|
||||
result.append(`,alterId=${proxy.aead ? 0 : 1}`);
|
||||
} else {
|
||||
result.append(`,alterId=${proxy.alterId}`);
|
||||
}
|
||||
@@ -338,6 +373,13 @@ function vmess(proxy) {
|
||||
// tfo
|
||||
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// block-quic
|
||||
if (proxy['block-quic'] === 'on') {
|
||||
result.append(',block-quic=true');
|
||||
} else if (proxy['block-quic'] === 'off') {
|
||||
result.append(',block-quic=false');
|
||||
}
|
||||
|
||||
// udp
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
@@ -348,9 +390,17 @@ function vmess(proxy) {
|
||||
}
|
||||
|
||||
function vless(proxy) {
|
||||
if (typeof proxy.flow !== 'undefined' || proxy['reality-opts']) {
|
||||
throw new Error(`VLESS XTLS/REALITY is not supported`);
|
||||
let isXtls = false;
|
||||
const isReality = !!proxy['reality-opts'];
|
||||
|
||||
if (typeof proxy.flow !== 'undefined') {
|
||||
if (['xtls-rprx-vision'].includes(proxy.flow)) {
|
||||
isXtls = true;
|
||||
} else {
|
||||
throw new Error(`VLESS flow(${proxy.flow}) is not supported`);
|
||||
}
|
||||
}
|
||||
|
||||
const result = new Result(proxy);
|
||||
result.append(
|
||||
`${proxy.name}=vless,${proxy.server},${proxy.port},"${proxy.uuid}"`,
|
||||
@@ -398,20 +448,42 @@ function vless(proxy) {
|
||||
'skip-cert-verify',
|
||||
);
|
||||
|
||||
// sni
|
||||
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni');
|
||||
result.appendIfPresent(
|
||||
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
||||
'tls-fingerprint',
|
||||
);
|
||||
result.appendIfPresent(
|
||||
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
|
||||
'tls-pubkey-sha256',
|
||||
);
|
||||
if (isXtls) {
|
||||
result.appendIfPresent(`,flow=${proxy.flow}`, 'flow');
|
||||
}
|
||||
if (isReality) {
|
||||
result.appendIfPresent(`,sni=${proxy.sni}`, 'sni');
|
||||
result.appendIfPresent(
|
||||
`,public-key="${proxy['reality-opts']['public-key']}"`,
|
||||
'reality-opts.public-key',
|
||||
);
|
||||
result.appendIfPresent(
|
||||
`,short-id=${proxy['reality-opts']['short-id']}`,
|
||||
'reality-opts.short-id',
|
||||
);
|
||||
} else {
|
||||
// sni
|
||||
result.appendIfPresent(`,tls-name=${proxy.sni}`, 'sni');
|
||||
result.appendIfPresent(
|
||||
`,tls-cert-sha256=${proxy['tls-fingerprint']}`,
|
||||
'tls-fingerprint',
|
||||
);
|
||||
result.appendIfPresent(
|
||||
`,tls-pubkey-sha256=${proxy['tls-pubkey-sha256']}`,
|
||||
'tls-pubkey-sha256',
|
||||
);
|
||||
}
|
||||
|
||||
// tfo
|
||||
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// block-quic
|
||||
if (proxy['block-quic'] === 'on') {
|
||||
result.append(',block-quic=true');
|
||||
} else if (proxy['block-quic'] === 'off') {
|
||||
result.append(',block-quic=false');
|
||||
}
|
||||
|
||||
// udp
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
@@ -439,6 +511,14 @@ function http(proxy) {
|
||||
|
||||
// tfo
|
||||
result.appendIfPresent(`,tfo=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// block-quic
|
||||
if (proxy['block-quic'] === 'on') {
|
||||
result.append(',block-quic=true');
|
||||
} else if (proxy['block-quic'] === 'off') {
|
||||
result.append(',block-quic=false');
|
||||
}
|
||||
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-mode=${ip_version}`, 'ip-version');
|
||||
|
||||
@@ -465,6 +545,13 @@ function socks5(proxy) {
|
||||
// tfo
|
||||
result.appendIfPresent(`,tfo=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// block-quic
|
||||
if (proxy['block-quic'] === 'on') {
|
||||
result.append(',block-quic=true');
|
||||
} else if (proxy['block-quic'] === 'off') {
|
||||
result.append(',block-quic=false');
|
||||
}
|
||||
|
||||
// udp
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
@@ -539,6 +626,13 @@ function wireguard(proxy) {
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-mode=${ip_version}`, 'ip-version');
|
||||
|
||||
// block-quic
|
||||
if (proxy['block-quic'] === 'on') {
|
||||
result.append(',block-quic=true');
|
||||
} else if (proxy['block-quic'] === 'off') {
|
||||
result.append(',block-quic=false');
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@@ -573,6 +667,13 @@ function hysteria2(proxy) {
|
||||
// tfo
|
||||
result.appendIfPresent(`,fast-open=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// block-quic
|
||||
if (proxy['block-quic'] === 'on') {
|
||||
result.append(',block-quic=true');
|
||||
} else if (proxy['block-quic'] === 'off') {
|
||||
result.append(',block-quic=false');
|
||||
}
|
||||
|
||||
// udp
|
||||
if (proxy.udp) {
|
||||
result.append(`,udp=true`);
|
||||
|
||||
@@ -58,6 +58,8 @@ function shadowsocks(proxy) {
|
||||
'aes-256-gcm',
|
||||
'chacha20-ietf-poly1305',
|
||||
'xchacha20-ietf-poly1305',
|
||||
'2022-blake3-aes-128-gcm',
|
||||
'2022-blake3-aes-256-gcm',
|
||||
].includes(proxy.cipher)
|
||||
) {
|
||||
throw new Error(`cipher ${proxy.cipher} is not supported`);
|
||||
@@ -128,6 +130,20 @@ function shadowsocks(proxy) {
|
||||
// udp
|
||||
appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
|
||||
|
||||
// udp over tcp
|
||||
if (proxy['_ssr_python_uot']) {
|
||||
append(`,udp-over-tcp=true`);
|
||||
} else if (proxy['udp-over-tcp']) {
|
||||
if (
|
||||
!proxy['udp-over-tcp-version'] ||
|
||||
proxy['udp-over-tcp-version'] === 1
|
||||
) {
|
||||
append(`,udp-over-tcp=sp.v1`);
|
||||
} else if (proxy['udp-over-tcp-version'] === 2) {
|
||||
append(`,udp-over-tcp=sp.v2`);
|
||||
}
|
||||
}
|
||||
|
||||
// server_check_url
|
||||
result.appendIfPresent(
|
||||
`,server_check_url=${proxy['test-url']}`,
|
||||
@@ -389,6 +405,8 @@ function vless(proxy) {
|
||||
else append(`,obfs=ws`);
|
||||
} else if (proxy.network === 'http') {
|
||||
append(`,obfs=http`);
|
||||
} else if (['tcp'].includes(proxy.network)) {
|
||||
if (proxy.tls) append(`,obfs=over-tls`);
|
||||
} else if (!['tcp'].includes(proxy.network)) {
|
||||
throw new Error(`network ${proxy.network} is unsupported`);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { isPresent } from '@/core/proxy-utils/producers/utils';
|
||||
import $ from '@/core/app';
|
||||
|
||||
export default function Shadowrocket_Producer() {
|
||||
const type = 'ALL';
|
||||
@@ -6,9 +7,9 @@ export default function Shadowrocket_Producer() {
|
||||
const list = proxies
|
||||
.filter((proxy) => {
|
||||
if (opts['include-unsupported-proxy']) return true;
|
||||
if (proxy.type === 'snell' && String(proxy.version) === '4') {
|
||||
if (proxy.type === 'snell' && proxy.version >= 4) {
|
||||
return false;
|
||||
} else if (['mieru', 'anytls'].includes(proxy.type)) {
|
||||
} else if (['mieru'].includes(proxy.type)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -165,6 +166,18 @@ export default function Shadowrocket_Producer() {
|
||||
proxy['h2-opts'].headers.host = [host];
|
||||
}
|
||||
}
|
||||
if (proxy.network === 'ws') {
|
||||
const wsPath = proxy['ws-opts']?.path;
|
||||
const reg = /^(.*?)(?:\?ed=(\d+))?$/;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [_, path = '', ed = ''] = reg.exec(wsPath);
|
||||
proxy['ws-opts'].path = path;
|
||||
if (ed !== '') {
|
||||
proxy['ws-opts']['early-data-header-name'] =
|
||||
'Sec-WebSocket-Protocol';
|
||||
proxy['ws-opts']['max-early-data'] = parseInt(ed, 10);
|
||||
}
|
||||
}
|
||||
if (proxy['plugin-opts']?.tls) {
|
||||
if (isPresent(proxy, 'skip-cert-verify')) {
|
||||
proxy['plugin-opts']['skip-cert-verify'] =
|
||||
|
||||
@@ -2,6 +2,26 @@ import ClashMeta_Producer from './clashmeta';
|
||||
import $ from '@/core/app';
|
||||
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) => {
|
||||
parsedProxy.detour = proxy['dialer-proxy'] || proxy.detour;
|
||||
};
|
||||
@@ -31,12 +51,34 @@ const smuxParser = (smux, proxy) => {
|
||||
if (smux['min-streams'])
|
||||
proxy.multiplex.min_streams = parseInt(`${smux['min-streams']}`, 10);
|
||||
if (smux.padding) proxy.multiplex.padding = true;
|
||||
if (smux['brutal-opts']?.up || smux['brutal-opts']?.down) {
|
||||
proxy.multiplex.brutal = {
|
||||
enabled: true,
|
||||
};
|
||||
if (smux['brutal-opts']?.up)
|
||||
proxy.multiplex.brutal.up_mbps = parseInt(
|
||||
`${smux['brutal-opts']?.up}`,
|
||||
10,
|
||||
);
|
||||
if (smux['brutal-opts']?.down)
|
||||
proxy.multiplex.brutal.down_mbps = parseInt(
|
||||
`${smux['brutal-opts']?.down}`,
|
||||
10,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const wsParser = (proxy, parsedProxy) => {
|
||||
const transport = { type: 'ws', headers: {} };
|
||||
if (proxy['ws-opts']) {
|
||||
const { path: wsPath = '', headers: wsHeaders = {} } = proxy['ws-opts'];
|
||||
const {
|
||||
path: wsPath = '',
|
||||
headers: wsHeaders = {},
|
||||
'max-early-data': max_early_data,
|
||||
'early-data-header-name': early_data_header_name,
|
||||
} = proxy['ws-opts'];
|
||||
transport.early_data_header_name = early_data_header_name;
|
||||
transport.max_early_data = parseInt(max_early_data, 10);
|
||||
if (wsPath !== '') transport.path = `${wsPath}`;
|
||||
if (Object.keys(wsHeaders).length > 0) {
|
||||
const headers = {};
|
||||
@@ -139,7 +181,7 @@ const h1Parser = (proxy, parsedProxy) => {
|
||||
host = `${host}`.split(',').map((i) => i.trim());
|
||||
if (host.length > 0) transport.host = host;
|
||||
}
|
||||
if (!transport.host) return;
|
||||
// if (!transport.host) return;
|
||||
if (proxy['http-path'] && proxy['http-path'] !== '') {
|
||||
const path = proxy['http-path'];
|
||||
if (Array.isArray(path)) {
|
||||
@@ -148,7 +190,7 @@ const h1Parser = (proxy, parsedProxy) => {
|
||||
}
|
||||
if (parsedProxy.tls.insecure)
|
||||
parsedProxy.tls.server_name = transport.host[0];
|
||||
if (transport.host.length === 1) transport.host = transport.host[0];
|
||||
if (transport.host?.length === 1) transport.host = transport.host[0];
|
||||
for (const key of Object.keys(transport.headers)) {
|
||||
const value = transport.headers[key];
|
||||
if (value.length === 1) transport.headers[key] = value[0];
|
||||
@@ -263,6 +305,7 @@ const sshParser = (proxy = {}) => {
|
||||
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
||||
tfoParser(proxy, parsedProxy);
|
||||
detourParser(proxy, parsedProxy);
|
||||
ipVersionParser(proxy, parsedProxy);
|
||||
return parsedProxy;
|
||||
};
|
||||
|
||||
@@ -290,6 +333,7 @@ const httpParser = (proxy = {}) => {
|
||||
tfoParser(proxy, parsedProxy);
|
||||
detourParser(proxy, parsedProxy);
|
||||
tlsParser(proxy, parsedProxy);
|
||||
ipVersionParser(proxy, parsedProxy);
|
||||
return parsedProxy;
|
||||
};
|
||||
|
||||
@@ -312,6 +356,7 @@ const socks5Parser = (proxy = {}) => {
|
||||
networkParser(proxy, parsedProxy);
|
||||
tfoParser(proxy, parsedProxy);
|
||||
detourParser(proxy, parsedProxy);
|
||||
ipVersionParser(proxy, parsedProxy);
|
||||
return parsedProxy;
|
||||
};
|
||||
|
||||
@@ -323,6 +368,17 @@ const shadowTLSParser = (proxy = {}) => {
|
||||
password: proxy.password,
|
||||
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 = {
|
||||
tag: `${proxy.name}_shadowtls`,
|
||||
type: 'shadowtls',
|
||||
@@ -345,6 +401,7 @@ const shadowTLSParser = (proxy = {}) => {
|
||||
tfoParser(proxy, stPart);
|
||||
detourParser(proxy, stPart);
|
||||
smuxParser(proxy.smux, ssPart);
|
||||
ipVersionParser(proxy, stPart);
|
||||
return { type: 'ss-with-st', ssPart, stPart };
|
||||
};
|
||||
const ssParser = (proxy = {}) => {
|
||||
@@ -359,12 +416,22 @@ const ssParser = (proxy = {}) => {
|
||||
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
|
||||
throw 'invalid port';
|
||||
if (proxy.uot) parsedProxy.udp_over_tcp = true;
|
||||
if (proxy['udp-over-tcp']) parsedProxy.udp_over_tcp = true;
|
||||
if (proxy['udp-over-tcp']) {
|
||||
parsedProxy.udp_over_tcp = {
|
||||
enabled: true,
|
||||
version:
|
||||
!proxy['udp-over-tcp-version'] ||
|
||||
proxy['udp-over-tcp-version'] === 1
|
||||
? 1
|
||||
: 2,
|
||||
};
|
||||
}
|
||||
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
||||
networkParser(proxy, parsedProxy);
|
||||
tfoParser(proxy, parsedProxy);
|
||||
detourParser(proxy, parsedProxy);
|
||||
smuxParser(proxy.smux, parsedProxy);
|
||||
ipVersionParser(proxy, parsedProxy);
|
||||
if (proxy.plugin) {
|
||||
const optArr = [];
|
||||
if (proxy.plugin === 'obfs') {
|
||||
@@ -443,6 +510,7 @@ const ssrParser = (proxy = {}) => {
|
||||
tfoParser(proxy, parsedProxy);
|
||||
detourParser(proxy, parsedProxy);
|
||||
smuxParser(proxy.smux, parsedProxy);
|
||||
ipVersionParser(proxy, parsedProxy);
|
||||
return parsedProxy;
|
||||
};
|
||||
|
||||
@@ -481,6 +549,7 @@ const vmessParser = (proxy = {}) => {
|
||||
detourParser(proxy, parsedProxy);
|
||||
tlsParser(proxy, parsedProxy);
|
||||
smuxParser(proxy.smux, parsedProxy);
|
||||
ipVersionParser(proxy, parsedProxy);
|
||||
return parsedProxy;
|
||||
};
|
||||
|
||||
@@ -495,15 +564,20 @@ const vlessParser = (proxy = {}) => {
|
||||
};
|
||||
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
|
||||
throw 'invalid port';
|
||||
if (proxy.xudp) parsedProxy.packet_encoding = 'xudp';
|
||||
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 === 'h2') h2Parser(proxy, parsedProxy);
|
||||
if (proxy.network === 'http') h1Parser(proxy, parsedProxy);
|
||||
if (proxy.network === 'grpc') grpcParser(proxy, parsedProxy);
|
||||
networkParser(proxy, parsedProxy);
|
||||
tfoParser(proxy, parsedProxy);
|
||||
detourParser(proxy, parsedProxy);
|
||||
smuxParser(proxy.smux, parsedProxy);
|
||||
tlsParser(proxy, parsedProxy);
|
||||
ipVersionParser(proxy, parsedProxy);
|
||||
return parsedProxy;
|
||||
};
|
||||
const trojanParser = (proxy = {}) => {
|
||||
@@ -525,6 +599,7 @@ const trojanParser = (proxy = {}) => {
|
||||
detourParser(proxy, parsedProxy);
|
||||
tlsParser(proxy, parsedProxy);
|
||||
smuxParser(proxy.smux, parsedProxy);
|
||||
ipVersionParser(proxy, parsedProxy);
|
||||
return parsedProxy;
|
||||
};
|
||||
const hysteriaParser = (proxy = {}) => {
|
||||
@@ -543,12 +618,13 @@ const hysteriaParser = (proxy = {}) => {
|
||||
if (proxy['fast-open']) parsedProxy.udp_fragment = true;
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const reg = new RegExp('^[0-9]+[ \t]*[KMGT]*[Bb]ps$');
|
||||
if (reg.test(`${proxy.up}`)) {
|
||||
// sing-box 跟文档不一致, 但是懒得全转, 只处理最常见的 Mbps
|
||||
if (reg.test(`${proxy.up}`) && !`${proxy.up}`.endsWith('Mbps')) {
|
||||
parsedProxy.up = `${proxy.up}`;
|
||||
} else {
|
||||
parsedProxy.up_mbps = parseInt(`${proxy.up}`, 10);
|
||||
}
|
||||
if (reg.test(`${proxy.down}`)) {
|
||||
if (reg.test(`${proxy.down}`) && !`${proxy.down}`.endsWith('Mbps')) {
|
||||
parsedProxy.down = `${proxy.down}`;
|
||||
} else {
|
||||
parsedProxy.down_mbps = parseInt(`${proxy.down}`, 10);
|
||||
@@ -573,9 +649,10 @@ const hysteriaParser = (proxy = {}) => {
|
||||
detourParser(proxy, parsedProxy);
|
||||
tfoParser(proxy, parsedProxy);
|
||||
smuxParser(proxy.smux, parsedProxy);
|
||||
ipVersionParser(proxy, parsedProxy);
|
||||
return parsedProxy;
|
||||
};
|
||||
const hysteria2Parser = (proxy = {}, includeUnsupportedProxy) => {
|
||||
const hysteria2Parser = (proxy = {}) => {
|
||||
const parsedProxy = {
|
||||
tag: proxy.name,
|
||||
type: 'hysteria2',
|
||||
@@ -606,6 +683,7 @@ const hysteria2Parser = (proxy = {}, includeUnsupportedProxy) => {
|
||||
tfoParser(proxy, parsedProxy);
|
||||
detourParser(proxy, parsedProxy);
|
||||
smuxParser(proxy.smux, parsedProxy);
|
||||
ipVersionParser(proxy, parsedProxy);
|
||||
return parsedProxy;
|
||||
};
|
||||
const tuic5Parser = (proxy = {}) => {
|
||||
@@ -637,6 +715,7 @@ const tuic5Parser = (proxy = {}) => {
|
||||
detourParser(proxy, parsedProxy);
|
||||
tlsParser(proxy, parsedProxy);
|
||||
smuxParser(proxy.smux, parsedProxy);
|
||||
ipVersionParser(proxy, parsedProxy);
|
||||
return parsedProxy;
|
||||
};
|
||||
const anytlsParser = (proxy = {}) => {
|
||||
@@ -652,8 +731,14 @@ const anytlsParser = (proxy = {}) => {
|
||||
parsedProxy.idle_session_check_interval = `${proxy['idle-session-check-interval']}s`;
|
||||
if (/^\d+$/.test(proxy['idle-session-timeout']))
|
||||
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);
|
||||
tlsParser(proxy, parsedProxy);
|
||||
ipVersionParser(proxy, parsedProxy);
|
||||
return parsedProxy;
|
||||
};
|
||||
|
||||
@@ -711,6 +796,7 @@ const wireguardParser = (proxy = {}) => {
|
||||
tfoParser(proxy, parsedProxy);
|
||||
detourParser(proxy, parsedProxy);
|
||||
smuxParser(proxy.smux, parsedProxy);
|
||||
ipVersionParser(proxy, parsedProxy);
|
||||
return parsedProxy;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { isPresent } from '@/core/proxy-utils/producers/utils';
|
||||
import $ from '@/core/app';
|
||||
|
||||
export default function Stash_Producer() {
|
||||
const type = 'ALL';
|
||||
@@ -39,21 +40,20 @@ export default function Stash_Producer() {
|
||||
'xchacha20',
|
||||
'chacha20-ietf-poly1305',
|
||||
'xchacha20-ietf-poly1305',
|
||||
...(opts['include-unsupported-proxy']
|
||||
? [
|
||||
'2022-blake3-aes-128-gcm',
|
||||
'2022-blake3-aes-256-gcm',
|
||||
]
|
||||
: []),
|
||||
'2022-blake3-aes-128-gcm',
|
||||
'2022-blake3-aes-256-gcm',
|
||||
].includes(proxy.cipher)) ||
|
||||
(proxy.type === 'snell' && String(proxy.version) === '4') ||
|
||||
(opts['include-unsupported-proxy']
|
||||
? proxy.type === 'vless' &&
|
||||
proxy['reality-opts'] &&
|
||||
!['xtls-rprx-vision'].includes(proxy.flow)
|
||||
: proxy.type === 'vless' && proxy['reality-opts'])
|
||||
(proxy.type === 'snell' && proxy.version >= 4) ||
|
||||
(proxy.type === 'vless' &&
|
||||
proxy['reality-opts'] &&
|
||||
!['xtls-rprx-vision'].includes(proxy.flow))
|
||||
) {
|
||||
return false;
|
||||
} else if (proxy['underlying-proxy'] || proxy['dialer-proxy']) {
|
||||
$.error(
|
||||
`Stash 暂不支持前置代理字段. 已过滤节点 ${proxy.name}. 请使用 代理的转发链 https://stash.wiki/proxy-protocols/proxy-groups#relay`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
@@ -238,6 +238,18 @@ export default function Stash_Producer() {
|
||||
proxy['h2-opts'].headers.host = [host];
|
||||
}
|
||||
}
|
||||
if (proxy.network === 'ws') {
|
||||
const wsPath = proxy['ws-opts']?.path;
|
||||
const reg = /^(.*?)(?:\?ed=(\d+))?$/;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [_, path = '', ed = ''] = reg.exec(wsPath);
|
||||
proxy['ws-opts'].path = path;
|
||||
if (ed !== '') {
|
||||
proxy['ws-opts']['early-data-header-name'] =
|
||||
'Sec-WebSocket-Protocol';
|
||||
proxy['ws-opts']['max-early-data'] = parseInt(ed, 10);
|
||||
}
|
||||
}
|
||||
if (proxy['plugin-opts']?.tls) {
|
||||
if (isPresent(proxy, 'skip-cert-verify')) {
|
||||
proxy['plugin-opts']['skip-cert-verify'] =
|
||||
@@ -261,11 +273,6 @@ export default function Stash_Producer() {
|
||||
}
|
||||
delete proxy['tls-fingerprint'];
|
||||
|
||||
if (proxy['underlying-proxy']) {
|
||||
proxy['dialer-proxy'] = proxy['underlying-proxy'];
|
||||
}
|
||||
delete proxy['underlying-proxy'];
|
||||
|
||||
if (isPresent(proxy, 'tls') && typeof proxy.tls !== 'boolean') {
|
||||
delete proxy.tls;
|
||||
}
|
||||
|
||||
@@ -370,9 +370,9 @@ function vmess(proxy, includeUnsupportedProxy) {
|
||||
function ssh(proxy) {
|
||||
const result = new Result(proxy);
|
||||
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
|
||||
// 需配合 Keystore
|
||||
@@ -439,8 +439,8 @@ function http(proxy) {
|
||||
const result = new Result(proxy);
|
||||
const type = proxy.tls ? 'https' : 'http';
|
||||
result.append(`${proxy.name}=${type},${proxy.server},${proxy.port}`);
|
||||
result.appendIfPresent(`,${proxy.username}`, 'username');
|
||||
result.appendIfPresent(`,"${proxy.password}"`, 'password');
|
||||
result.appendIfPresent(`,username="${proxy.username}"`, 'username');
|
||||
result.appendIfPresent(`,password="${proxy.password}"`, 'password');
|
||||
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||
@@ -565,8 +565,8 @@ function socks5(proxy) {
|
||||
const result = new Result(proxy);
|
||||
const type = proxy.tls ? 'socks5-tls' : 'socks5';
|
||||
result.append(`${proxy.name}=${type},${proxy.server},${proxy.port}`);
|
||||
result.appendIfPresent(`,${proxy.username}`, 'username');
|
||||
result.appendIfPresent(`,"${proxy.password}"`, 'password');
|
||||
result.appendIfPresent(`,username="${proxy.username}"`, 'username');
|
||||
result.appendIfPresent(`,password="${proxy.password}"`, 'password');
|
||||
|
||||
const ip_version = ipVersions[proxy['ip-version']] || proxy['ip-version'];
|
||||
result.appendIfPresent(`,ip-version=${ip_version}`, 'ip-version');
|
||||
|
||||
@@ -12,7 +12,7 @@ export default function URI_Producer() {
|
||||
delete proxy.resolved;
|
||||
delete proxy['no-resolve'];
|
||||
for (const key in proxy) {
|
||||
if (proxy[key] == null || /^_/i.test(key)) {
|
||||
if (proxy[key] == null) {
|
||||
delete proxy[key];
|
||||
}
|
||||
}
|
||||
@@ -23,13 +23,19 @@ export default function URI_Producer() {
|
||||
) {
|
||||
delete proxy.tls;
|
||||
}
|
||||
if (proxy.server && isIPv6(proxy.server)) {
|
||||
if (
|
||||
!['vmess'].includes(proxy.type) &&
|
||||
proxy.server &&
|
||||
isIPv6(proxy.server)
|
||||
) {
|
||||
proxy.server = `[${proxy.server}]`;
|
||||
}
|
||||
switch (proxy.type) {
|
||||
case 'socks5':
|
||||
result = `socks://${encodeURIComponent(
|
||||
Base64.encode(`${proxy.username}:${proxy.password}`),
|
||||
Base64.encode(
|
||||
`${proxy.username ?? ''}:${proxy.password ?? ''}`,
|
||||
),
|
||||
)}@${proxy.server}:${proxy.port}#${proxy.name}`;
|
||||
break;
|
||||
case 'ss':
|
||||
@@ -113,12 +119,17 @@ export default function URI_Producer() {
|
||||
v: '2',
|
||||
ps: proxy.name,
|
||||
add: proxy.server,
|
||||
port: proxy.port,
|
||||
port: `${proxy.port}`,
|
||||
id: proxy.uuid,
|
||||
type,
|
||||
aid: proxy.alterId || 0,
|
||||
aid: `${proxy.alterId || 0}`,
|
||||
scy: proxy.cipher,
|
||||
net,
|
||||
type,
|
||||
tls: proxy.tls ? 'tls' : '',
|
||||
alpn: Array.isArray(proxy.alpn)
|
||||
? proxy.alpn.join(',')
|
||||
: proxy.alpn,
|
||||
fp: proxy['client-fingerprint'],
|
||||
};
|
||||
if (proxy.tls && proxy.sni) {
|
||||
result.sni = proxy.sni;
|
||||
@@ -129,16 +140,7 @@ export default function URI_Producer() {
|
||||
proxy[`${proxy.network}-opts`]?.path;
|
||||
let vmessTransportHost =
|
||||
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)) {
|
||||
result.path =
|
||||
proxy[`${proxy.network}-opts`]?.[
|
||||
@@ -150,6 +152,31 @@ export default function URI_Producer() {
|
||||
'gun';
|
||||
result.host =
|
||||
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));
|
||||
@@ -474,7 +501,7 @@ export default function URI_Producer() {
|
||||
hysteriaParams.push(`obfsParam=${proxy[key]}`);
|
||||
} else if (['sni'].includes(key)) {
|
||||
hysteriaParams.push(`peer=${proxy[key]}`);
|
||||
} else if (proxy[key]) {
|
||||
} else if (proxy[key] && !/^_/i.test(key)) {
|
||||
hysteriaParams.push(
|
||||
`${i}=${encodeURIComponent(proxy[key])}`,
|
||||
);
|
||||
@@ -531,7 +558,13 @@ export default function URI_Producer() {
|
||||
proxy[key]
|
||||
) {
|
||||
tuicParams.push(`${i.replace(/-/g, '_')}=1`);
|
||||
} else if (proxy[key]) {
|
||||
} else if (
|
||||
['congestion-controller'].includes(key)
|
||||
) {
|
||||
tuicParams.push(
|
||||
`congestion_control=${proxy[key]}`,
|
||||
);
|
||||
} else if (proxy[key] && !/^_/i.test(key)) {
|
||||
tuicParams.push(
|
||||
`${i.replace(
|
||||
/-/g,
|
||||
@@ -579,7 +612,11 @@ export default function URI_Producer() {
|
||||
if (proxy[key]) {
|
||||
anytlsParams.push(`insecure=1`);
|
||||
}
|
||||
} else if (proxy[key]) {
|
||||
} else if (['udp'].includes(key)) {
|
||||
if (proxy[key]) {
|
||||
anytlsParams.push(`udp=1`);
|
||||
}
|
||||
} else if (proxy[key] && !/^_/i.test(key)) {
|
||||
anytlsParams.push(
|
||||
`${i.replace(/-/g, '_')}=${encodeURIComponent(
|
||||
proxy[key],
|
||||
@@ -616,7 +653,7 @@ export default function URI_Producer() {
|
||||
if (proxy[key]) {
|
||||
wireguardParams.push(`${key}=1`);
|
||||
}
|
||||
} else if (proxy[key]) {
|
||||
} else if (proxy[key] && !/^_/i.test(key)) {
|
||||
wireguardParams.push(
|
||||
`${key}=${encodeURIComponent(proxy[key])}`,
|
||||
);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
* @documentation: https://www.notion.so/Sub-Store-6259586994d34c11a4ced5c406264b46
|
||||
*/
|
||||
import { version } from '../package.json';
|
||||
import $ from '@/core/app';
|
||||
console.log(
|
||||
`
|
||||
┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅
|
||||
@@ -18,7 +19,6 @@ console.log(
|
||||
┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅
|
||||
`,
|
||||
);
|
||||
|
||||
import migrate from '@/utils/migration';
|
||||
import serve from '@/restful';
|
||||
|
||||
|
||||
@@ -89,8 +89,10 @@ async function doSync() {
|
||||
const allSubs = $.read(SUBS_KEY);
|
||||
const allCols = $.read(COLLECTIONS_KEY);
|
||||
const subNames = [];
|
||||
let enabledCount = 0;
|
||||
allArtifacts.map((artifact) => {
|
||||
if (artifact.sync && artifact.source) {
|
||||
enabledCount++;
|
||||
if (artifact.type === 'subscription') {
|
||||
const subName = artifact.source;
|
||||
const sub = findByName(allSubs, subName);
|
||||
@@ -111,6 +113,13 @@ async function doSync() {
|
||||
}
|
||||
});
|
||||
|
||||
if (enabledCount === 0) {
|
||||
$.info(
|
||||
`需同步的配置: ${enabledCount}, 总数: ${allArtifacts.length}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (subNames.length > 0) {
|
||||
await Promise.all(
|
||||
subNames.map(async (subName) => {
|
||||
|
||||
@@ -14,10 +14,12 @@ let resourceUrl = typeof $resourceUrl !== 'undefined' ? $resourceUrl : '';
|
||||
`
|
||||
┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅
|
||||
Sub-Store -- v${version}
|
||||
Loon -- ${$loon}
|
||||
┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅
|
||||
`,
|
||||
);
|
||||
|
||||
const build = $loon.match(/\((\d+)\)$/)?.[1];
|
||||
let arg;
|
||||
if (typeof $argument != 'undefined') {
|
||||
arg = Object.fromEntries(
|
||||
@@ -26,23 +28,28 @@ let resourceUrl = typeof $resourceUrl !== 'undefined' ? $resourceUrl : '';
|
||||
} else {
|
||||
arg = {};
|
||||
}
|
||||
console.log(`arg: ${JSON.stringify(arg)}`);
|
||||
|
||||
const RESOURCE_TYPE = {
|
||||
PROXY: 1,
|
||||
RULE: 2,
|
||||
};
|
||||
|
||||
result = resource;
|
||||
if (!arg.resourceUrlOnly) {
|
||||
result = resource;
|
||||
}
|
||||
|
||||
if (resourceType === RESOURCE_TYPE.PROXY) {
|
||||
try {
|
||||
let proxies = ProxyUtils.parse(resource);
|
||||
result = ProxyUtils.produce(proxies, 'Loon', undefined, {
|
||||
'include-unsupported-proxy': arg?.includeUnsupportedProxy,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log('解析器: 使用 resource 出现错误');
|
||||
console.log(e.message ?? e);
|
||||
if (!arg.resourceUrlOnly) {
|
||||
try {
|
||||
let proxies = ProxyUtils.parse(resource);
|
||||
result = ProxyUtils.produce(proxies, 'Loon', undefined, {
|
||||
'include-unsupported-proxy':
|
||||
arg?.includeUnsupportedProxy || build >= 842,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log('解析器: 使用 resource 出现错误');
|
||||
console.log(e.message ?? e);
|
||||
}
|
||||
}
|
||||
if ((!result || /^\s*$/.test(result)) && resourceUrl) {
|
||||
console.log(`解析器: 尝试从 ${resourceUrl} 获取订阅`);
|
||||
@@ -59,18 +66,21 @@ let resourceUrl = typeof $resourceUrl !== 'undefined' ? $resourceUrl : '';
|
||||
);
|
||||
let proxies = ProxyUtils.parse(raw);
|
||||
result = ProxyUtils.produce(proxies, 'Loon', undefined, {
|
||||
'include-unsupported-proxy': arg?.includeUnsupportedProxy,
|
||||
'include-unsupported-proxy':
|
||||
arg?.includeUnsupportedProxy || build >= 842,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e.message ?? e);
|
||||
}
|
||||
}
|
||||
} else if (resourceType === RESOURCE_TYPE.RULE) {
|
||||
try {
|
||||
const rules = RuleUtils.parse(resource);
|
||||
result = RuleUtils.produce(rules, 'Loon');
|
||||
} catch (e) {
|
||||
console.log(e.message ?? e);
|
||||
if (!arg.resourceUrlOnly) {
|
||||
try {
|
||||
const rules = RuleUtils.parse(resource);
|
||||
result = RuleUtils.produce(rules, 'Loon');
|
||||
} catch (e) {
|
||||
console.log(e.message ?? e);
|
||||
}
|
||||
}
|
||||
if ((!result || /^\s*$/.test(result)) && resourceUrl) {
|
||||
console.log(`解析器: 尝试从 ${resourceUrl} 获取规则`);
|
||||
|
||||
@@ -112,7 +112,6 @@ function replaceArtifact(req, res) {
|
||||
|
||||
async function getArtifact(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
const allArtifacts = $.read(ARTIFACTS_KEY);
|
||||
const artifact = findByName(allArtifacts, name);
|
||||
|
||||
@@ -163,7 +162,6 @@ function createArtifact(req, res) {
|
||||
function updateArtifact(req, res) {
|
||||
const allArtifacts = $.read(ARTIFACTS_KEY);
|
||||
let oldName = req.params.name;
|
||||
oldName = decodeURIComponent(oldName);
|
||||
const artifact = findByName(allArtifacts, oldName);
|
||||
if (artifact) {
|
||||
$.info(`正在更新远程配置:${artifact.name}`);
|
||||
@@ -197,7 +195,6 @@ function updateArtifact(req, res) {
|
||||
|
||||
async function deleteArtifact(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
$.info(`正在删除远程配置:${name}`);
|
||||
const allArtifacts = $.read(ARTIFACTS_KEY);
|
||||
try {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { deleteByName, findByName, updateByName } from '@/utils/database';
|
||||
import { COLLECTIONS_KEY, ARTIFACTS_KEY } from '@/constants';
|
||||
import { COLLECTIONS_KEY, ARTIFACTS_KEY, FILES_KEY } from '@/constants';
|
||||
import { failed, success } from '@/restful/response';
|
||||
import $ from '@/core/app';
|
||||
import { RequestInvalidError, ResourceNotFoundError } from '@/restful/errors';
|
||||
import { formatDateTime } from '@/utils';
|
||||
|
||||
export default function register($app) {
|
||||
if (!$.read(COLLECTIONS_KEY)) $.write({}, COLLECTIONS_KEY);
|
||||
@@ -51,7 +52,6 @@ function createCollection(req, res) {
|
||||
function getCollection(req, res) {
|
||||
let { name } = req.params;
|
||||
let { raw } = req.query;
|
||||
name = decodeURIComponent(name);
|
||||
const allCols = $.read(COLLECTIONS_KEY);
|
||||
const collection = findByName(allCols, name);
|
||||
if (collection) {
|
||||
@@ -60,16 +60,9 @@ function getCollection(req, res) {
|
||||
.set(
|
||||
'content-disposition',
|
||||
`attachment; filename="${encodeURIComponent(
|
||||
`sub-store_collection_${name}_${new Date()
|
||||
.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
day: 'numeric',
|
||||
month: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
})
|
||||
.replace(/\D/g, '')}.json`,
|
||||
`sub-store_collection_${name}_${formatDateTime(
|
||||
new Date(),
|
||||
)}.json`,
|
||||
)}"`,
|
||||
)
|
||||
.send(JSON.stringify(collection));
|
||||
@@ -90,7 +83,6 @@ function getCollection(req, res) {
|
||||
|
||||
function updateCollection(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
let collection = req.body;
|
||||
const allCols = $.read(COLLECTIONS_KEY);
|
||||
const oldCol = findByName(allCols, name);
|
||||
@@ -112,7 +104,18 @@ function updateCollection(req, res) {
|
||||
artifact.source = newCol.name;
|
||||
}
|
||||
}
|
||||
// update all files referring this collection
|
||||
const allFiles = $.read(FILES_KEY) || [];
|
||||
for (const file of allFiles) {
|
||||
if (
|
||||
file.sourceType === 'collection' &&
|
||||
file.sourceName === oldCol.name
|
||||
) {
|
||||
file.sourceName = newCol.name;
|
||||
}
|
||||
}
|
||||
$.write(allArtifacts, ARTIFACTS_KEY);
|
||||
$.write(allFiles, FILES_KEY);
|
||||
}
|
||||
|
||||
updateByName(allCols, name, newCol);
|
||||
@@ -132,7 +135,6 @@ function updateCollection(req, res) {
|
||||
|
||||
function deleteCollection(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
$.info(`正在删除组合订阅:${name}`);
|
||||
let allCols = $.read(COLLECTIONS_KEY);
|
||||
deleteByName(allCols, name);
|
||||
|
||||
@@ -88,8 +88,6 @@ export default function register($app) {
|
||||
|
||||
async function downloadSubscription(req, res) {
|
||||
let { name, nezhaIndex } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
nezhaIndex = decodeURIComponent(nezhaIndex);
|
||||
|
||||
const useMihomoExternal = req.query.target === 'SurgeMac';
|
||||
|
||||
@@ -111,7 +109,17 @@ async function downloadSubscription(req, res) {
|
||||
proxy,
|
||||
noCache,
|
||||
} = req.query;
|
||||
let $options = {};
|
||||
let $options = {
|
||||
_req: {
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
params: req.params,
|
||||
headers: req.headers,
|
||||
body: req.body,
|
||||
},
|
||||
};
|
||||
if (req.query.$options) {
|
||||
try {
|
||||
// 支持 `#${encodeURIComponent(JSON.stringify({arg1: "1"}))}`
|
||||
@@ -130,7 +138,6 @@ async function downloadSubscription(req, res) {
|
||||
$.info(`传入 $options: ${JSON.stringify($options)}`);
|
||||
}
|
||||
if (url) {
|
||||
url = decodeURIComponent(url);
|
||||
$.info(`指定远程订阅 URL: ${url}`);
|
||||
if (!/^https?:\/\//.test(url)) {
|
||||
content = url;
|
||||
@@ -138,32 +145,25 @@ async function downloadSubscription(req, res) {
|
||||
}
|
||||
}
|
||||
if (content) {
|
||||
content = decodeURIComponent(content);
|
||||
$.info(`指定本地订阅: ${content}`);
|
||||
}
|
||||
if (proxy) {
|
||||
proxy = decodeURIComponent(proxy);
|
||||
$.info(`指定远程订阅使用代理/策略 proxy: ${proxy}`);
|
||||
}
|
||||
if (ua) {
|
||||
ua = decodeURIComponent(ua);
|
||||
$.info(`指定远程订阅 User-Agent: ${ua}`);
|
||||
}
|
||||
|
||||
if (mergeSources) {
|
||||
mergeSources = decodeURIComponent(mergeSources);
|
||||
$.info(`指定合并来源: ${mergeSources}`);
|
||||
}
|
||||
if (ignoreFailedRemoteSub != null && ignoreFailedRemoteSub !== '') {
|
||||
ignoreFailedRemoteSub = decodeURIComponent(ignoreFailedRemoteSub);
|
||||
$.info(`指定忽略失败的远程订阅: ${ignoreFailedRemoteSub}`);
|
||||
}
|
||||
if (produceType) {
|
||||
produceType = decodeURIComponent(produceType);
|
||||
$.info(`指定生产类型: ${produceType}`);
|
||||
}
|
||||
if (includeUnsupportedProxy) {
|
||||
includeUnsupportedProxy = decodeURIComponent(includeUnsupportedProxy);
|
||||
$.info(
|
||||
`包含官方/商店版/未续费订阅不支持的协议: ${includeUnsupportedProxy}`,
|
||||
);
|
||||
@@ -249,7 +249,7 @@ async function downloadSubscription(req, res) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$arguments.noFlow) {
|
||||
if (!$arguments.noFlow && /^https?/.test(url)) {
|
||||
// forward flow headers
|
||||
flowInfo = await getFlowHeaders(
|
||||
$arguments?.insecure ? `${url}#insecure` : url,
|
||||
@@ -352,8 +352,6 @@ async function downloadSubscription(req, res) {
|
||||
|
||||
async function downloadCollection(req, res) {
|
||||
let { name, nezhaIndex } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
nezhaIndex = decodeURIComponent(nezhaIndex);
|
||||
|
||||
const useMihomoExternal = req.query.target === 'SurgeMac';
|
||||
|
||||
@@ -376,7 +374,17 @@ async function downloadCollection(req, res) {
|
||||
noCache,
|
||||
} = req.query;
|
||||
|
||||
let $options = {};
|
||||
let $options = {
|
||||
_req: {
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
params: req.params,
|
||||
headers: req.headers,
|
||||
body: req.body,
|
||||
},
|
||||
};
|
||||
if (req.query.$options) {
|
||||
try {
|
||||
// 支持 `#${encodeURIComponent(JSON.stringify({arg1: "1"}))}`
|
||||
@@ -396,21 +404,17 @@ async function downloadCollection(req, res) {
|
||||
}
|
||||
|
||||
if (proxy) {
|
||||
proxy = decodeURIComponent(proxy);
|
||||
$.info(`指定远程订阅使用代理/策略 proxy: ${proxy}`);
|
||||
}
|
||||
|
||||
if (ignoreFailedRemoteSub != null && ignoreFailedRemoteSub !== '') {
|
||||
ignoreFailedRemoteSub = decodeURIComponent(ignoreFailedRemoteSub);
|
||||
$.info(`指定忽略失败的远程订阅: ${ignoreFailedRemoteSub}`);
|
||||
}
|
||||
if (produceType) {
|
||||
produceType = decodeURIComponent(produceType);
|
||||
$.info(`指定生产类型: ${produceType}`);
|
||||
}
|
||||
|
||||
if (includeUnsupportedProxy) {
|
||||
includeUnsupportedProxy = decodeURIComponent(includeUnsupportedProxy);
|
||||
$.info(
|
||||
`包含官方/商店版/未续费订阅不支持的协议: ${includeUnsupportedProxy}`,
|
||||
);
|
||||
@@ -486,7 +490,7 @@ async function downloadCollection(req, res) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$arguments.noFlow) {
|
||||
if (!$arguments.noFlow && /^https?:/.test(url)) {
|
||||
subUserInfoOfSub = await getFlowHeaders(
|
||||
$arguments?.insecure ? `${url}#insecure` : url,
|
||||
$arguments.flowUserAgent,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { deleteByName, findByName, updateByName } from '@/utils/database';
|
||||
import { getFlowHeaders, normalizeFlowHeader } from '@/utils/flow';
|
||||
import { FILES_KEY } from '@/constants';
|
||||
import { FILES_KEY, ARTIFACTS_KEY } from '@/constants';
|
||||
import { failed, success } from '@/restful/response';
|
||||
import $ from '@/core/app';
|
||||
import {
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
InternalServerError,
|
||||
} from '@/restful/errors';
|
||||
import { produceArtifact } from '@/restful/sync';
|
||||
import { formatDateTime } from '@/utils';
|
||||
|
||||
export default function register($app) {
|
||||
if (!$.read(FILES_KEY)) $.write([], FILES_KEY);
|
||||
@@ -50,9 +51,8 @@ function createFile(req, res) {
|
||||
|
||||
async function getFile(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
|
||||
$.info(`正在下载文件:${name}`);
|
||||
const reqUA = req.headers['user-agent'] || req.headers['User-Agent'];
|
||||
$.info(`正在下载文件:${name}\n请求 User-Agent: ${reqUA}`);
|
||||
let {
|
||||
url,
|
||||
subInfoUrl,
|
||||
@@ -63,8 +63,19 @@ async function getFile(req, res) {
|
||||
ignoreFailedRemoteFile,
|
||||
proxy,
|
||||
noCache,
|
||||
produceType,
|
||||
} = req.query;
|
||||
let $options = {};
|
||||
let $options = {
|
||||
_req: {
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
path: req.path,
|
||||
query: req.query,
|
||||
params: req.params,
|
||||
headers: req.headers,
|
||||
body: req.body,
|
||||
},
|
||||
};
|
||||
if (req.query.$options) {
|
||||
try {
|
||||
// 支持 `#${encodeURIComponent(JSON.stringify({arg1: "1"}))}`
|
||||
@@ -83,40 +94,35 @@ async function getFile(req, res) {
|
||||
$.info(`传入 $options: ${JSON.stringify($options)}`);
|
||||
}
|
||||
if (url) {
|
||||
url = decodeURIComponent(url);
|
||||
$.info(`指定远程文件 URL: ${url}`);
|
||||
}
|
||||
if (proxy) {
|
||||
proxy = decodeURIComponent(proxy);
|
||||
$.info(`指定远程订阅使用代理/策略 proxy: ${proxy}`);
|
||||
}
|
||||
if (ua) {
|
||||
ua = decodeURIComponent(ua);
|
||||
$.info(`指定远程文件 User-Agent: ${ua}`);
|
||||
}
|
||||
if (subInfoUrl) {
|
||||
subInfoUrl = decodeURIComponent(subInfoUrl);
|
||||
$.info(`指定获取流量的 subInfoUrl: ${subInfoUrl}`);
|
||||
}
|
||||
if (subInfoUserAgent) {
|
||||
subInfoUserAgent = decodeURIComponent(subInfoUserAgent);
|
||||
$.info(`指定获取流量的 subInfoUserAgent: ${subInfoUserAgent}`);
|
||||
}
|
||||
if (content) {
|
||||
content = decodeURIComponent(content);
|
||||
$.info(`指定本地文件: ${content}`);
|
||||
}
|
||||
if (mergeSources) {
|
||||
mergeSources = decodeURIComponent(mergeSources);
|
||||
$.info(`指定合并来源: ${mergeSources}`);
|
||||
}
|
||||
if (ignoreFailedRemoteFile != null && ignoreFailedRemoteFile !== '') {
|
||||
ignoreFailedRemoteFile = decodeURIComponent(ignoreFailedRemoteFile);
|
||||
$.info(`指定忽略失败的远程文件: ${ignoreFailedRemoteFile}`);
|
||||
}
|
||||
if (noCache) {
|
||||
$.info(`指定不使用缓存: ${noCache}`);
|
||||
}
|
||||
if (produceType) {
|
||||
$.info(`指定生产类型: ${produceType}`);
|
||||
}
|
||||
|
||||
const allFiles = $.read(FILES_KEY);
|
||||
const file = findByName(allFiles, name);
|
||||
@@ -133,6 +139,8 @@ async function getFile(req, res) {
|
||||
$options,
|
||||
proxy,
|
||||
noCache,
|
||||
produceType,
|
||||
all: true,
|
||||
});
|
||||
|
||||
try {
|
||||
@@ -167,9 +175,15 @@ async function getFile(req, res) {
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
res.set('Content-Type', 'text/plain; charset=utf-8').send(
|
||||
output ?? '',
|
||||
);
|
||||
res.set('Content-Type', 'text/plain; charset=utf-8');
|
||||
if (output?.$options?._res?.headers) {
|
||||
Object.entries(output.$options._res.headers).forEach(
|
||||
([key, value]) => {
|
||||
res.set(key, value);
|
||||
},
|
||||
);
|
||||
}
|
||||
res.send(output?.$content ?? '');
|
||||
} catch (err) {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 下载文件失败`,
|
||||
@@ -201,7 +215,6 @@ async function getFile(req, res) {
|
||||
function getWholeFile(req, res) {
|
||||
let { name } = req.params;
|
||||
let { raw } = req.query;
|
||||
name = decodeURIComponent(name);
|
||||
const allFiles = $.read(FILES_KEY);
|
||||
const file = findByName(allFiles, name);
|
||||
if (file) {
|
||||
@@ -210,16 +223,9 @@ function getWholeFile(req, res) {
|
||||
.set(
|
||||
'content-disposition',
|
||||
`attachment; filename="${encodeURIComponent(
|
||||
`sub-store_file_${name}_${new Date()
|
||||
.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
day: 'numeric',
|
||||
month: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
})
|
||||
.replace(/\D/g, '')}.json`,
|
||||
`sub-store_file_${name}_${formatDateTime(
|
||||
new Date(),
|
||||
)}.json`,
|
||||
)}"`,
|
||||
)
|
||||
.send(JSON.stringify(file));
|
||||
@@ -240,7 +246,6 @@ function getWholeFile(req, res) {
|
||||
|
||||
function updateFile(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
let file = req.body;
|
||||
const allFiles = $.read(FILES_KEY);
|
||||
const oldFile = findByName(allFiles, name);
|
||||
@@ -251,6 +256,20 @@ function updateFile(req, res) {
|
||||
};
|
||||
$.info(`正在更新文件:${name}...`);
|
||||
|
||||
if (name !== newFile.name) {
|
||||
// update all artifacts referring this collection
|
||||
const allArtifacts = $.read(ARTIFACTS_KEY) || [];
|
||||
for (const artifact of allArtifacts) {
|
||||
if (
|
||||
artifact.type === 'file' &&
|
||||
artifact.source === oldFile.name
|
||||
) {
|
||||
artifact.source = newFile.name;
|
||||
}
|
||||
}
|
||||
$.write(allArtifacts, ARTIFACTS_KEY);
|
||||
}
|
||||
|
||||
updateByName(allFiles, name, newFile);
|
||||
$.write(allFiles, FILES_KEY);
|
||||
success(res, newFile);
|
||||
@@ -268,7 +287,6 @@ function updateFile(req, res) {
|
||||
|
||||
function deleteFile(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
$.info(`正在删除文件:${name}`);
|
||||
let allFiles = $.read(FILES_KEY);
|
||||
deleteByName(allFiles, name);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Base64 } from 'js-base64';
|
||||
import _ from 'lodash';
|
||||
import express from '@/vendor/express';
|
||||
import $ from '@/core/app';
|
||||
import migrate from '@/utils/migration';
|
||||
import download from '@/utils/download';
|
||||
import download, { downloadFile } from '@/utils/download';
|
||||
import { syncArtifacts, produceArtifact } from '@/restful/sync';
|
||||
import { gistBackupAction } from '@/restful/miscs';
|
||||
import { TOKENS_KEY } from '@/constants';
|
||||
@@ -29,6 +31,77 @@ export default function serve() {
|
||||
host = eval('process.env.SUB_STORE_BACKEND_API_HOST') || '::';
|
||||
}
|
||||
const $app = express({ substore: $, port, host });
|
||||
if ($.env.isNode) {
|
||||
const be_merge = eval('process.env.SUB_STORE_BACKEND_MERGE');
|
||||
const be_prefix = eval('process.env.SUB_STORE_BACKEND_PREFIX');
|
||||
const fe_be_path = eval('process.env.SUB_STORE_FRONTEND_BACKEND_PATH');
|
||||
const fe_path = eval('process.env.SUB_STORE_FRONTEND_PATH');
|
||||
if (be_prefix || be_merge) {
|
||||
if (!fe_be_path.startsWith('/')) {
|
||||
throw new Error(
|
||||
'SUB_STORE_FRONTEND_BACKEND_PATH should start with /',
|
||||
);
|
||||
}
|
||||
if (be_merge) {
|
||||
$.info(`[BACKEND] MERGE mode is [ON].`);
|
||||
$.info(`[BACKEND && FRONTEND] ${host}:${port}`);
|
||||
}
|
||||
$.info(`[BACKEND PREFIX] ${host}:${port}${fe_be_path}`);
|
||||
$app.use((req, res, next) => {
|
||||
if (req.path.startsWith(fe_be_path)) {
|
||||
req.url = req.url.replace(fe_be_path, '') || '/';
|
||||
if (be_merge && req.url.startsWith('/api/')) {
|
||||
req.query['share'] = 'true';
|
||||
}
|
||||
next();
|
||||
return;
|
||||
}
|
||||
const pathname =
|
||||
decodeURIComponent(req._parsedUrl.pathname) || '/';
|
||||
if (
|
||||
be_merge &&
|
||||
req.path.startsWith('/share/') &&
|
||||
req.query.token
|
||||
) {
|
||||
if (req.method.toLowerCase() !== 'get') {
|
||||
res.status(405).send('Method not allowed');
|
||||
return;
|
||||
}
|
||||
const tokens = $.read(TOKENS_KEY) || [];
|
||||
const token = tokens.find(
|
||||
(t) =>
|
||||
t.token === req.query.token &&
|
||||
`/share/${t.type}/${t.name}` === pathname &&
|
||||
(t.exp == null || t.exp > Date.now()),
|
||||
);
|
||||
if (token) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (be_merge && fe_path && req.path.indexOf('/', 1) == -1) {
|
||||
if (req.path.indexOf('.') == -1) {
|
||||
req.url = '/index.html';
|
||||
}
|
||||
const express_ = eval(`require("express")`);
|
||||
const mime_ = eval(`require("mime-types")`);
|
||||
const path_ = eval(`require("path")`);
|
||||
const staticFileMiddleware = express_.static(fe_path, {
|
||||
setHeaders: (res, path) => {
|
||||
const type = mime_.contentType(path_.extname(path));
|
||||
if (type) {
|
||||
res.set('Content-Type', type);
|
||||
}
|
||||
},
|
||||
});
|
||||
staticFileMiddleware(req, res, next);
|
||||
return;
|
||||
}
|
||||
res.status(404).end();
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
// register routes
|
||||
registerCollectionRoutes($app);
|
||||
registerSubscriptionRoutes($app);
|
||||
@@ -48,10 +121,11 @@ export default function serve() {
|
||||
$app.start();
|
||||
|
||||
if ($.env.isNode) {
|
||||
// Deprecated: SUB_STORE_BACKEND_CRON
|
||||
const backend_sync_cron =
|
||||
eval('process.env.SUB_STORE_BACKEND_SYNC_CRON') ||
|
||||
eval('process.env.SUB_STORE_BACKEND_CRON');
|
||||
// Deprecated: SUB_STORE_BACKEND_CRON, SUB_STORE_CRON
|
||||
const backend_sync_cron = eval(
|
||||
'process.env.SUB_STORE_BACKEND_SYNC_CRON',
|
||||
);
|
||||
|
||||
if (backend_sync_cron) {
|
||||
$.info(`[SYNC CRON] ${backend_sync_cron} enabled`);
|
||||
const { CronJob } = eval(`require("cron")`);
|
||||
@@ -74,6 +148,17 @@ export default function serve() {
|
||||
true, // start
|
||||
// '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
|
||||
// 每 2 小时处理一次单条订阅 a, 每 3 小时处理一次组合订阅 b
|
||||
@@ -164,9 +249,64 @@ export default function serve() {
|
||||
// 'Asia/Shanghai' // timeZone
|
||||
);
|
||||
}
|
||||
const mmdb_cron = eval('process.env.SUB_STORE_MMDB_CRON');
|
||||
const countryFile = eval('process.env.SUB_STORE_MMDB_COUNTRY_PATH');
|
||||
const countryUrl = eval('process.env.SUB_STORE_MMDB_COUNTRY_URL');
|
||||
const asnFile = eval('process.env.SUB_STORE_MMDB_ASN_PATH');
|
||||
const asnUrl = eval('process.env.SUB_STORE_MMDB_ASN_URL');
|
||||
if (mmdb_cron && ((countryFile && countryUrl) || (asnFile && asnUrl))) {
|
||||
$.info(`[MMDB CRON] ${mmdb_cron} enabled`);
|
||||
const { CronJob } = eval(`require("cron")`);
|
||||
new CronJob(
|
||||
mmdb_cron,
|
||||
async function () {
|
||||
try {
|
||||
$.info(`[MMDB CRON] ${mmdb_cron} started`);
|
||||
if (countryFile && countryUrl) {
|
||||
try {
|
||||
$.info(
|
||||
`[MMDB CRON] downloading ${countryUrl} to ${countryFile}`,
|
||||
);
|
||||
await downloadFile(countryUrl, countryFile);
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`[MMDB CRON] ${countryUrl} download failed: ${
|
||||
e.message ?? e
|
||||
}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (asnFile && asnUrl) {
|
||||
try {
|
||||
$.info(
|
||||
`[MMDB CRON] downloading ${asnUrl} to ${asnFile}`,
|
||||
);
|
||||
await downloadFile(asnUrl, asnFile);
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`[MMDB CRON] ${asnUrl} download failed: ${
|
||||
e.message ?? e
|
||||
}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$.info(`[MMDB CRON] ${mmdb_cron} finished`);
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`[MMDB CRON] ${mmdb_cron} error: ${e.message ?? e}`,
|
||||
);
|
||||
}
|
||||
}, // onTick
|
||||
null, // onComplete
|
||||
true, // start
|
||||
// 'Asia/Shanghai' // timeZone
|
||||
);
|
||||
}
|
||||
const path = eval(`require("path")`);
|
||||
const fs = eval(`require("fs")`);
|
||||
const data_url = eval('process.env.SUB_STORE_DATA_URL');
|
||||
const data_url_post = eval('process.env.SUB_STORE_DATA_URL_POST');
|
||||
const fe_be_path = eval('process.env.SUB_STORE_FRONTEND_BACKEND_PATH');
|
||||
const fe_port = eval('process.env.SUB_STORE_FRONTEND_PORT') || 3001;
|
||||
const fe_host =
|
||||
@@ -175,7 +315,8 @@ export default function serve() {
|
||||
const fe_abs_path = path.resolve(
|
||||
fe_path || path.join(__dirname, 'frontend'),
|
||||
);
|
||||
if (fe_path) {
|
||||
const be_merge = eval('process.env.SUB_STORE_BACKEND_MERGE');
|
||||
if (fe_path && !be_merge) {
|
||||
try {
|
||||
fs.accessSync(path.join(fe_abs_path, 'index.html'));
|
||||
} catch (e) {
|
||||
@@ -194,11 +335,15 @@ export default function serve() {
|
||||
|
||||
const staticFileMiddleware = express_.static(fe_path);
|
||||
|
||||
let be_share_rewrite = '/share/:type/:name';
|
||||
let be_api_rewrite = '';
|
||||
let be_download_rewrite = '';
|
||||
let be_api = '/api/';
|
||||
let be_download = '/download/';
|
||||
let be_share = '/share/';
|
||||
let be_download_rewrite = '';
|
||||
let be_api_rewrite = '';
|
||||
let be_share_rewrite = `${be_share}:type/:name`;
|
||||
let prefix = eval('process.env.SUB_STORE_BACKEND_PREFIX')
|
||||
? fe_be_path
|
||||
: '';
|
||||
if (fe_be_path) {
|
||||
if (!fe_be_path.startsWith('/')) {
|
||||
throw new Error(
|
||||
@@ -215,9 +360,9 @@ export default function serve() {
|
||||
app.use(
|
||||
be_share_rewrite,
|
||||
createProxyMiddleware({
|
||||
target: `http://127.0.0.1:${port}`,
|
||||
target: `http://127.0.0.1:${port}${prefix}`,
|
||||
changeOrigin: true,
|
||||
pathRewrite: (path, req) => {
|
||||
pathRewrite: async (path, req) => {
|
||||
if (req.method.toLowerCase() !== 'get')
|
||||
throw new Error('Method not allowed');
|
||||
const tokens = $.read(TOKENS_KEY) || [];
|
||||
@@ -229,34 +374,26 @@ export default function serve() {
|
||||
(t.exp == null || t.exp > Date.now()),
|
||||
);
|
||||
if (!token) throw new Error('Forbbiden');
|
||||
return path;
|
||||
return req.originalUrl;
|
||||
},
|
||||
}),
|
||||
);
|
||||
app.use(
|
||||
be_api_rewrite,
|
||||
createProxyMiddleware({
|
||||
target: `http://127.0.0.1:${port}`,
|
||||
pathRewrite: (path) => {
|
||||
const newPath = path.startsWith(be_api_rewrite)
|
||||
? path.replace(be_api_rewrite, be_api)
|
||||
: path;
|
||||
return newPath.includes('?')
|
||||
? `${newPath}&share=true`
|
||||
: `${newPath}?share=true`;
|
||||
target: `http://127.0.0.1:${port}${prefix}${be_api}`,
|
||||
pathRewrite: async (path) => {
|
||||
return path.includes('?')
|
||||
? `${path}&share=true`
|
||||
: `${path}?share=true`;
|
||||
},
|
||||
}),
|
||||
);
|
||||
app.use(
|
||||
be_download_rewrite,
|
||||
createProxyMiddleware({
|
||||
target: `http://127.0.0.1:${port}`,
|
||||
target: `http://127.0.0.1:${port}${prefix}${be_download}`,
|
||||
changeOrigin: true,
|
||||
pathRewrite: (path) => {
|
||||
return path.startsWith(be_download_rewrite)
|
||||
? path.replace(be_download_rewrite, be_download)
|
||||
: path;
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -276,10 +413,10 @@ export default function serve() {
|
||||
$.info(`[FRONTEND] ${fe_address}:${fe_port}`);
|
||||
if (fe_be_path) {
|
||||
$.info(
|
||||
`[FRONTEND -> BACKEND] ${fe_address}:${fe_port}${be_api_rewrite} -> http://127.0.0.1:${port}${be_api}`,
|
||||
`[FRONTEND -> BACKEND] ${fe_address}:${fe_port}${be_api_rewrite} -> ${host}:${port}${prefix}${be_api}`,
|
||||
);
|
||||
$.info(
|
||||
`[FRONTEND -> BACKEND] ${fe_address}:${fe_port}${be_download_rewrite} -> http://127.0.0.1:${port}${be_download}`,
|
||||
`[FRONTEND -> BACKEND] ${fe_address}:${fe_port}${be_download_rewrite} -> ${host}:${port}${prefix}${be_download}`,
|
||||
);
|
||||
$.info(
|
||||
`[SHARE BACKEND] ${fe_address}:${fe_port}${be_share_rewrite}`,
|
||||
@@ -290,10 +427,39 @@ export default function serve() {
|
||||
if (data_url) {
|
||||
$.info(`[BACKEND] downloading data from ${data_url}`);
|
||||
download(data_url)
|
||||
.then((content) => {
|
||||
$.write(content, '#sub-store');
|
||||
.then(async (content) => {
|
||||
try {
|
||||
content = JSON.parse(Base64.decode(content));
|
||||
if (Object.keys(content.settings).length === 0) {
|
||||
throw new Error(
|
||||
'备份文件应该至少包含 settings 字段',
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
try {
|
||||
content = JSON.parse(content);
|
||||
if (Object.keys(content.settings).length === 0) {
|
||||
throw new Error(
|
||||
'备份文件应该至少包含 settings 字段',
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
$.error(
|
||||
`Gist 备份文件校验失败, 无法还原\nReason: ${
|
||||
err.message ?? err
|
||||
}`,
|
||||
);
|
||||
throw new Error('Gist 备份文件校验失败, 无法还原');
|
||||
}
|
||||
}
|
||||
if (data_url_post) {
|
||||
$.info('[BACKEND] executing post-processing script');
|
||||
eval(data_url_post);
|
||||
}
|
||||
|
||||
$.cache = JSON.parse(content);
|
||||
$.write(JSON.stringify(content, null, ` `), '#sub-store');
|
||||
|
||||
$.cache = content;
|
||||
$.persistCache();
|
||||
|
||||
migrate();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Base64 } from 'js-base64';
|
||||
import _ from 'lodash';
|
||||
import $ from '@/core/app';
|
||||
import { ENV } from '@/vendor/open-api';
|
||||
import { failed, success } from '@/restful/response';
|
||||
@@ -14,6 +16,7 @@ import { InternalServerError, RequestInvalidError } from '@/restful/errors';
|
||||
import Gist from '@/utils/gist';
|
||||
import migrate from '@/utils/migration';
|
||||
import env from '@/utils/env';
|
||||
import { formatDateTime } from '@/utils';
|
||||
|
||||
export default function register($app) {
|
||||
// utils
|
||||
@@ -28,16 +31,7 @@ export default function register($app) {
|
||||
.set(
|
||||
'content-disposition',
|
||||
`attachment; filename="${encodeURIComponent(
|
||||
`sub-store_data_${new Date()
|
||||
.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
day: 'numeric',
|
||||
month: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
})
|
||||
.replace(/\D/g, '')}.json`,
|
||||
`sub-store_data_${formatDateTime(new Date())}.json`,
|
||||
)}"`,
|
||||
)
|
||||
.send(
|
||||
@@ -47,21 +41,47 @@ export default function register($app) {
|
||||
);
|
||||
})
|
||||
.post((req, res) => {
|
||||
const { content } = req.body;
|
||||
$.write(content, '#sub-store');
|
||||
let { content } = req.body;
|
||||
try {
|
||||
content = JSON.parse(Base64.decode(content));
|
||||
if (Object.keys(content.settings).length === 0) {
|
||||
throw new Error('备份文件应该至少包含 settings 字段');
|
||||
}
|
||||
} catch (err) {
|
||||
try {
|
||||
content = JSON.parse(content);
|
||||
if (Object.keys(content.settings).length === 0) {
|
||||
throw new Error('备份文件应该至少包含 settings 字段');
|
||||
}
|
||||
} catch (err) {
|
||||
$.error(
|
||||
`备份文件校验失败, 无法还原\nReason: ${
|
||||
err.message ?? err
|
||||
}`,
|
||||
);
|
||||
throw new Error('备份文件校验失败, 无法还原');
|
||||
}
|
||||
}
|
||||
$.write(JSON.stringify(content, null, ` `), '#sub-store');
|
||||
if ($.env.isNode) {
|
||||
$.cache = JSON.parse(content);
|
||||
$.cache = content;
|
||||
$.persistCache();
|
||||
}
|
||||
migrate();
|
||||
success(res);
|
||||
});
|
||||
|
||||
// Redirect sub.store to vercel webpage
|
||||
$app.get('/', async (req, res) => {
|
||||
// 302 redirect
|
||||
res.set('location', 'https://sub-store.vercel.app/').status(302).end();
|
||||
});
|
||||
if (ENV().isNode) {
|
||||
$app.get('/', getEnv);
|
||||
} else {
|
||||
// Redirect sub.store to vercel webpage
|
||||
$app.get('/', async (req, res) => {
|
||||
// 302 redirect
|
||||
res.set('location', 'https://sub-store.vercel.app/')
|
||||
.status(302)
|
||||
.end();
|
||||
});
|
||||
}
|
||||
|
||||
// handle preflight request for QX
|
||||
if (ENV().isQX) {
|
||||
@@ -79,7 +99,19 @@ function getEnv(req, res) {
|
||||
if (req.query.share) {
|
||||
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) {
|
||||
@@ -94,7 +126,7 @@ async function refresh(_, res) {
|
||||
success(res);
|
||||
}
|
||||
|
||||
async function gistBackupAction(action) {
|
||||
async function gistBackupAction(action, keep, encode) {
|
||||
// read token
|
||||
const { gistToken, syncPlatform } = $.read(SETTINGS_KEY);
|
||||
if (!gistToken) throw new Error('GitHub Token is required for backup!');
|
||||
@@ -104,6 +136,9 @@ async function gistBackupAction(action) {
|
||||
key: GIST_BACKUP_KEY,
|
||||
syncPlatform,
|
||||
});
|
||||
let currentContent = $.read('#sub-store');
|
||||
currentContent = currentContent ? JSON.parse(currentContent) : {};
|
||||
if ($.env.isNode) currentContent = JSON.parse(JSON.stringify($.cache));
|
||||
let content;
|
||||
const settings = $.read(SETTINGS_KEY);
|
||||
const updated = settings.syncTime;
|
||||
@@ -111,7 +146,18 @@ async function gistBackupAction(action) {
|
||||
case 'upload':
|
||||
try {
|
||||
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));
|
||||
if (encode === 'plaintext') {
|
||||
content.settings.gistToken =
|
||||
'恢复后请重新设置 GitHub Token';
|
||||
content = JSON.stringify(content, null, ` `);
|
||||
} else {
|
||||
content = Base64.encode(
|
||||
JSON.stringify(content, null, ` `),
|
||||
);
|
||||
}
|
||||
|
||||
$.info(`下载备份, 与本地内容对比...`);
|
||||
const onlineContent = await gist.download(
|
||||
GIST_BACKUP_FILE_NAME,
|
||||
@@ -128,7 +174,14 @@ async function gistBackupAction(action) {
|
||||
settings.syncTime = new Date().getTime();
|
||||
$.write(settings, SETTINGS_KEY);
|
||||
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));
|
||||
if (encode === 'plaintext') {
|
||||
content.settings.gistToken = '恢复后请重新设置 GitHub Token';
|
||||
content = JSON.stringify(content, null, ` `);
|
||||
} else {
|
||||
content = Base64.encode(JSON.stringify(content, null, ` `));
|
||||
}
|
||||
$.info(`上传备份中...`);
|
||||
try {
|
||||
await gist.upload({
|
||||
@@ -146,21 +199,34 @@ async function gistBackupAction(action) {
|
||||
$.info(`还原备份中...`);
|
||||
content = await gist.download(GIST_BACKUP_FILE_NAME);
|
||||
try {
|
||||
if (Object.keys(JSON.parse(content).settings).length === 0) {
|
||||
content = JSON.parse(Base64.decode(content));
|
||||
if (Object.keys(content.settings).length === 0) {
|
||||
throw new Error('备份文件应该至少包含 settings 字段');
|
||||
}
|
||||
} catch (err) {
|
||||
$.error(
|
||||
`Gist 备份文件校验失败, 无法还原\nReason: ${
|
||||
err.message ?? err
|
||||
}`,
|
||||
);
|
||||
throw new Error('Gist 备份文件校验失败, 无法还原');
|
||||
try {
|
||||
content = JSON.parse(content);
|
||||
if (Object.keys(content.settings).length === 0) {
|
||||
throw new Error('备份文件应该至少包含 settings 字段');
|
||||
}
|
||||
} catch (err) {
|
||||
$.error(
|
||||
`Gist 备份文件校验失败, 无法还原\nReason: ${
|
||||
err.message ?? err
|
||||
}`,
|
||||
);
|
||||
throw new Error('Gist 备份文件校验失败, 无法还原');
|
||||
}
|
||||
}
|
||||
if (keep) {
|
||||
$.info(`保留原有设置 ${keep}`);
|
||||
keep.split(',').forEach((path) => {
|
||||
_.set(content, path, _.get(currentContent, path));
|
||||
});
|
||||
}
|
||||
// restore settings
|
||||
$.write(content, '#sub-store');
|
||||
$.write(JSON.stringify(content, null, ` `), '#sub-store');
|
||||
if ($.env.isNode) {
|
||||
content = JSON.parse(content);
|
||||
$.cache = content;
|
||||
$.persistCache();
|
||||
}
|
||||
@@ -172,7 +238,7 @@ async function gistBackupAction(action) {
|
||||
}
|
||||
}
|
||||
async function gistBackup(req, res) {
|
||||
const { action } = req.query;
|
||||
const { action, keep, encode } = req.query;
|
||||
// read token
|
||||
const { gistToken } = $.read(SETTINGS_KEY);
|
||||
if (!gistToken) {
|
||||
@@ -185,7 +251,7 @@ async function gistBackup(req, res) {
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
await gistBackupAction(action);
|
||||
await gistBackupAction(action, keep, encode);
|
||||
success(res);
|
||||
} catch (err) {
|
||||
$.error(
|
||||
|
||||
@@ -43,7 +43,6 @@ function createModule(req, res) {
|
||||
|
||||
function getModule(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
const allModules = $.read(MODULES_KEY);
|
||||
const module = findByName(allModules, name);
|
||||
if (module) {
|
||||
@@ -64,7 +63,6 @@ function getModule(req, res) {
|
||||
|
||||
function updateModule(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
let module = req.body;
|
||||
const allModules = $.read(MODULES_KEY);
|
||||
const oldModule = findByName(allModules, name);
|
||||
@@ -92,7 +90,6 @@ function updateModule(req, res) {
|
||||
|
||||
function deleteModule(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
$.info(`正在删除模块:${name}`);
|
||||
let allModules = $.read(MODULES_KEY);
|
||||
deleteByName(allModules, name);
|
||||
|
||||
@@ -31,7 +31,12 @@ async function previewFile(req, res) {
|
||||
.filter((i) => i.length)
|
||||
.map(async (url) => {
|
||||
try {
|
||||
return await download(url, file.ua);
|
||||
return await download(
|
||||
url,
|
||||
file.ua,
|
||||
undefined,
|
||||
file.proxy,
|
||||
);
|
||||
} catch (err) {
|
||||
errors[url] = err;
|
||||
$.error(
|
||||
@@ -42,15 +47,22 @@ async function previewFile(req, res) {
|
||||
}),
|
||||
);
|
||||
|
||||
if (
|
||||
!file.ignoreFailedRemoteFile &&
|
||||
Object.keys(errors).length > 0
|
||||
) {
|
||||
throw new Error(
|
||||
`文件 ${file.name} 的远程文件 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!file.ignoreFailedRemoteFile) {
|
||||
throw new Error(
|
||||
`文件 ${file.name} 的远程文件 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (file.ignoreFailedRemoteFile === 'enabled') {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 预览文件失败`,
|
||||
`❌ ${file.name}`,
|
||||
`远程文件 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (file.mergeSources === 'localFirst') {
|
||||
content.unshift(file.content);
|
||||
@@ -131,12 +143,22 @@ async function compareSub(req, res) {
|
||||
}),
|
||||
);
|
||||
|
||||
if (!sub.ignoreFailedRemoteSub && Object.keys(errors).length > 0) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!sub.ignoreFailedRemoteSub) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (sub.ignoreFailedRemoteSub === 'enabled') {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 预览订阅失败`,
|
||||
`❌ ${sub.name}`,
|
||||
`远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (sub.mergeSources === 'localFirst') {
|
||||
content.unshift(sub.content);
|
||||
@@ -239,15 +261,25 @@ async function compareCollection(req, res) {
|
||||
}
|
||||
}),
|
||||
);
|
||||
if (
|
||||
!sub.ignoreFailedRemoteSub &&
|
||||
Object.keys(errors).length > 0
|
||||
) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!sub.ignoreFailedRemoteSub) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (
|
||||
sub.ignoreFailedRemoteSub === 'enabled'
|
||||
) {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 预览订阅失败`,
|
||||
`❌ ${sub.name}`,
|
||||
`远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (sub.mergeSources === 'localFirst') {
|
||||
raw.unshift(sub.content);
|
||||
@@ -279,20 +311,28 @@ async function compareCollection(req, res) {
|
||||
errors[name] = err;
|
||||
|
||||
$.error(
|
||||
`❌ 处理组合订阅 ${collection.name} 中的子订阅: ${sub.name}时出现错误:${err}!`,
|
||||
`❌ 处理组合订阅 ${collection.name} 中的子订阅: ${sub.name} 时出现错误:${err}!`,
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
if (
|
||||
!collection.ignoreFailedRemoteSub &&
|
||||
Object.keys(errors).length > 0
|
||||
) {
|
||||
throw new Error(
|
||||
`组合订阅 ${collection.name} 中的子订阅 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!collection.ignoreFailedRemoteSub) {
|
||||
throw new Error(
|
||||
`组合订阅 ${collection.name} 的子订阅 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (collection.ignoreFailedRemoteSub === 'enabled') {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 预览组合订阅失败`,
|
||||
`❌ ${collection.name}`,
|
||||
`子订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
// merge proxies with the original order
|
||||
const original = Array.prototype.concat.apply(
|
||||
|
||||
@@ -12,7 +12,9 @@ export function failed(resp, error, statusCode) {
|
||||
code: error.code,
|
||||
type: error.type,
|
||||
message: error.message,
|
||||
details: error.details,
|
||||
details: resp.req?.route?.path?.startsWith('/share/')
|
||||
? '详情请查看日志'
|
||||
: error.details,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ async function updateSettings(req, res) {
|
||||
|
||||
export async function updateAvatar() {
|
||||
const settings = $.read(SETTINGS_KEY);
|
||||
const { githubUser: username, syncPlatform } = settings;
|
||||
const { githubUser: username, syncPlatform, githubProxy } = settings;
|
||||
if (username) {
|
||||
if (syncPlatform === 'gitlab') {
|
||||
try {
|
||||
@@ -92,7 +92,9 @@ export async function updateAvatar() {
|
||||
try {
|
||||
const data = await $.http
|
||||
.get({
|
||||
url: `https://api.github.com/users/${encodeURIComponent(
|
||||
url: `${
|
||||
githubProxy ? `${githubProxy}/` : ''
|
||||
}https://api.github.com/users/${encodeURIComponent(
|
||||
username,
|
||||
)}`,
|
||||
headers: {
|
||||
@@ -134,11 +136,15 @@ export async function updateArtifactStore() {
|
||||
settings.artifactStore = url;
|
||||
settings.artifactStoreStatus = 'VALID';
|
||||
} else {
|
||||
$.error(`找不到 Sub-Store Gist`);
|
||||
$.error(`找不到 Sub-Store Gist (${ARTIFACT_REPOSITORY_KEY})`);
|
||||
settings.artifactStoreStatus = 'NOT FOUND';
|
||||
}
|
||||
} catch (err) {
|
||||
$.error(`查找 Sub-Store Gist 时发生错误: ${err.message ?? err}`);
|
||||
$.error(
|
||||
`查找 Sub-Store Gist (${ARTIFACT_REPOSITORY_KEY}) 时发生错误: ${
|
||||
err.message ?? err
|
||||
}`,
|
||||
);
|
||||
settings.artifactStoreStatus = 'ERROR';
|
||||
}
|
||||
$.write(settings, SETTINGS_KEY);
|
||||
|
||||
@@ -5,7 +5,12 @@ import {
|
||||
RequestInvalidError,
|
||||
} from './errors';
|
||||
import { deleteByName, findByName, updateByName } from '@/utils/database';
|
||||
import { SUBS_KEY, COLLECTIONS_KEY, ARTIFACTS_KEY } from '@/constants';
|
||||
import {
|
||||
SUBS_KEY,
|
||||
COLLECTIONS_KEY,
|
||||
ARTIFACTS_KEY,
|
||||
FILES_KEY,
|
||||
} from '@/constants';
|
||||
import {
|
||||
getFlowHeaders,
|
||||
parseFlowHeaders,
|
||||
@@ -13,6 +18,7 @@ import {
|
||||
} from '@/utils/flow';
|
||||
import { success, failed } from './response';
|
||||
import $ from '@/core/app';
|
||||
import { formatDateTime } from '@/utils';
|
||||
|
||||
if (!$.read(SUBS_KEY)) $.write({}, SUBS_KEY);
|
||||
|
||||
@@ -33,10 +39,8 @@ export default function register($app) {
|
||||
// subscriptions API
|
||||
async function getFlowInfo(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
let { url } = req.query;
|
||||
if (url) {
|
||||
url = decodeURIComponent(url);
|
||||
$.info(`指定远程订阅 URL: ${url}`);
|
||||
}
|
||||
const allSubs = $.read(SUBS_KEY);
|
||||
@@ -134,7 +138,7 @@ async function getFlowInfo(req, res) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($arguments.noFlow) {
|
||||
if ($arguments.noFlow || !/^https?/.test(url)) {
|
||||
failed(
|
||||
res,
|
||||
new RequestInvalidError(
|
||||
@@ -226,6 +230,7 @@ async function getFlowInfo(req, res) {
|
||||
|
||||
function createSubscription(req, res) {
|
||||
const sub = req.body;
|
||||
delete sub.subscriptions;
|
||||
$.info(`正在创建订阅: ${sub.name}`);
|
||||
if (/\//.test(sub.name)) {
|
||||
failed(
|
||||
@@ -256,25 +261,18 @@ function createSubscription(req, res) {
|
||||
function getSubscription(req, res) {
|
||||
let { name } = req.params;
|
||||
let { raw } = req.query;
|
||||
name = decodeURIComponent(name);
|
||||
const allSubs = $.read(SUBS_KEY);
|
||||
const sub = findByName(allSubs, name);
|
||||
delete sub.subscriptions;
|
||||
if (sub) {
|
||||
if (raw) {
|
||||
res.set('content-type', 'application/json')
|
||||
.set(
|
||||
'content-disposition',
|
||||
`attachment; filename="${encodeURIComponent(
|
||||
`sub-store_subscription_${name}_${new Date()
|
||||
.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
day: 'numeric',
|
||||
month: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
})
|
||||
.replace(/\D/g, '')}.json`,
|
||||
`sub-store_subscription_${name}_${formatDateTime(
|
||||
new Date(),
|
||||
)}.json`,
|
||||
)}"`,
|
||||
)
|
||||
.send(JSON.stringify(sub));
|
||||
@@ -295,8 +293,8 @@ function getSubscription(req, res) {
|
||||
|
||||
function updateSubscription(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name); // the original name
|
||||
let sub = req.body;
|
||||
delete sub.subscriptions;
|
||||
const allSubs = $.read(SUBS_KEY);
|
||||
const oldSub = findByName(allSubs, name);
|
||||
if (oldSub) {
|
||||
@@ -326,9 +324,20 @@ function updateSubscription(req, res) {
|
||||
artifact.source = sub.name;
|
||||
}
|
||||
}
|
||||
// update all files referring this subscription
|
||||
const allFiles = $.read(FILES_KEY) || [];
|
||||
for (const file of allFiles) {
|
||||
if (
|
||||
file.sourceType === 'subscription' &&
|
||||
file.sourceName == name
|
||||
) {
|
||||
file.sourceName = sub.name;
|
||||
}
|
||||
}
|
||||
|
||||
$.write(allCols, COLLECTIONS_KEY);
|
||||
$.write(allArtifacts, ARTIFACTS_KEY);
|
||||
$.write(allFiles, FILES_KEY);
|
||||
}
|
||||
updateByName(allSubs, name, newSub);
|
||||
$.write(allSubs, SUBS_KEY);
|
||||
@@ -347,7 +356,6 @@ function updateSubscription(req, res) {
|
||||
|
||||
function deleteSubscription(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
$.info(`删除订阅:${name}...`);
|
||||
// delete from subscriptions
|
||||
let allSubs = $.read(SUBS_KEY);
|
||||
|
||||
@@ -40,6 +40,7 @@ async function produceArtifact({
|
||||
$options,
|
||||
proxy,
|
||||
noCache,
|
||||
all,
|
||||
}) {
|
||||
platform = platform || 'JSON';
|
||||
|
||||
@@ -89,12 +90,23 @@ async function produceArtifact({
|
||||
if (ignoreFailedRemoteSub != null && ignoreFailedRemoteSub !== '') {
|
||||
subIgnoreFailedRemoteSub = ignoreFailedRemoteSub;
|
||||
}
|
||||
if (!subIgnoreFailedRemoteSub && Object.keys(errors).length > 0) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!subIgnoreFailedRemoteSub) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (subIgnoreFailedRemoteSub === 'enabled') {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 处理订阅失败`,
|
||||
`❌ ${sub.name}`,
|
||||
`远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (mergeSources === 'localFirst') {
|
||||
raw.unshift(content);
|
||||
@@ -138,12 +150,23 @@ async function produceArtifact({
|
||||
if (ignoreFailedRemoteSub != null && ignoreFailedRemoteSub !== '') {
|
||||
subIgnoreFailedRemoteSub = ignoreFailedRemoteSub;
|
||||
}
|
||||
if (!subIgnoreFailedRemoteSub && Object.keys(errors).length > 0) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!subIgnoreFailedRemoteSub) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (subIgnoreFailedRemoteSub === 'enabled') {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 处理订阅失败`,
|
||||
`❌ ${sub.name}`,
|
||||
`远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (sub.mergeSources === 'localFirst') {
|
||||
raw.unshift(sub.content);
|
||||
@@ -151,6 +174,9 @@ async function produceArtifact({
|
||||
raw.push(sub.content);
|
||||
}
|
||||
}
|
||||
if (produceType === 'raw') {
|
||||
return JSON.stringify((Array.isArray(raw) ? raw : [raw]).flat());
|
||||
}
|
||||
// parse proxies
|
||||
let proxies = (Array.isArray(raw) ? raw : [raw])
|
||||
.map((i) => ProxyUtils.parse(i))
|
||||
@@ -264,15 +290,25 @@ async function produceArtifact({
|
||||
}
|
||||
}),
|
||||
);
|
||||
if (
|
||||
!sub.ignoreFailedRemoteSub &&
|
||||
Object.keys(errors).length > 0
|
||||
) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!sub.ignoreFailedRemoteSub) {
|
||||
throw new Error(
|
||||
`订阅 ${sub.name} 的远程订阅 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (
|
||||
sub.ignoreFailedRemoteSub === 'enabled'
|
||||
) {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 处理订阅失败`,
|
||||
`❌ ${sub.name}`,
|
||||
`远程订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (sub.mergeSources === 'localFirst') {
|
||||
raw.unshift(sub.content);
|
||||
@@ -327,15 +363,23 @@ async function produceArtifact({
|
||||
if (ignoreFailedRemoteSub != null && ignoreFailedRemoteSub !== '') {
|
||||
collectionIgnoreFailedRemoteSub = ignoreFailedRemoteSub;
|
||||
}
|
||||
if (
|
||||
!collectionIgnoreFailedRemoteSub &&
|
||||
Object.keys(errors).length > 0
|
||||
) {
|
||||
throw new Error(
|
||||
`组合订阅 ${name} 中的子订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!collectionIgnoreFailedRemoteSub) {
|
||||
throw new Error(
|
||||
`组合订阅 ${collection.name} 的子订阅 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (collectionIgnoreFailedRemoteSub === 'enabled') {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 处理组合订阅失败`,
|
||||
`❌ ${collection.name}`,
|
||||
`子订阅 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// merge proxies with the original order
|
||||
@@ -411,7 +455,6 @@ async function produceArtifact({
|
||||
const file = findByName(allFiles, name);
|
||||
if (!file) throw new Error(`找不到文件 ${name}`);
|
||||
let raw = '';
|
||||
console.log(file);
|
||||
if (file.type !== 'mihomoProfile') {
|
||||
if (
|
||||
content &&
|
||||
@@ -506,15 +549,23 @@ async function produceArtifact({
|
||||
) {
|
||||
fileIgnoreFailedRemoteFile = ignoreFailedRemoteFile;
|
||||
}
|
||||
if (
|
||||
!fileIgnoreFailedRemoteFile &&
|
||||
Object.keys(errors).length > 0
|
||||
) {
|
||||
throw new Error(
|
||||
`文件 ${file.name} 的远程文件 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
if (!fileIgnoreFailedRemoteFile) {
|
||||
throw new Error(
|
||||
`文件 ${file.name} 的远程文件 ${Object.keys(
|
||||
errors,
|
||||
).join(', ')} 发生错误, 请查看日志`,
|
||||
);
|
||||
} else if (fileIgnoreFailedRemoteFile === 'enabled') {
|
||||
$.notify(
|
||||
`🌍 Sub-Store 处理文件失败`,
|
||||
`❌ ${file.name}`,
|
||||
`远程文件 ${Object.keys(errors).join(
|
||||
', ',
|
||||
)} 发生错误, 请查看日志`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (file.mergeSources === 'localFirst') {
|
||||
raw.unshift(file.content);
|
||||
@@ -523,6 +574,9 @@ async function produceArtifact({
|
||||
}
|
||||
}
|
||||
}
|
||||
if (produceType === 'raw') {
|
||||
return JSON.stringify((Array.isArray(raw) ? raw : [raw]).flat());
|
||||
}
|
||||
const files = (Array.isArray(raw) ? raw : [raw]).flat();
|
||||
let filesContent = files
|
||||
.filter((i) => i != null && i !== '')
|
||||
@@ -542,7 +596,7 @@ async function produceArtifact({
|
||||
)
|
||||
: { $content: filesContent, $files: files, $options };
|
||||
|
||||
return processed?.$content ?? '';
|
||||
return (all ? processed : processed?.$content) ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -557,8 +611,10 @@ async function syncArtifacts() {
|
||||
const allSubs = $.read(SUBS_KEY);
|
||||
const allCols = $.read(COLLECTIONS_KEY);
|
||||
const subNames = [];
|
||||
let enabledCount = 0;
|
||||
allArtifacts.map((artifact) => {
|
||||
if (artifact.sync && artifact.source) {
|
||||
enabledCount++;
|
||||
if (artifact.type === 'subscription') {
|
||||
const subName = artifact.source;
|
||||
const sub = findByName(allSubs, subName);
|
||||
@@ -579,6 +635,13 @@ async function syncArtifacts() {
|
||||
}
|
||||
});
|
||||
|
||||
if (enabledCount === 0) {
|
||||
$.info(
|
||||
`需同步的配置: ${enabledCount}, 总数: ${allArtifacts.length}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (subNames.length > 0) {
|
||||
await Promise.all(
|
||||
subNames.map(async (subName) => {
|
||||
@@ -729,7 +792,6 @@ async function syncAllArtifacts(_, res) {
|
||||
|
||||
async function syncArtifact(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
$.info(`开始同步远程配置 ${name}...`);
|
||||
const allArtifacts = $.read(ARTIFACTS_KEY);
|
||||
const artifact = findByName(allArtifacts, name);
|
||||
|
||||
@@ -17,7 +17,6 @@ export default function register($app) {
|
||||
|
||||
function deleteToken(req, res) {
|
||||
let { token } = req.params;
|
||||
token = decodeURIComponent(token);
|
||||
$.info(`正在删除:${token}`);
|
||||
let allTokens = $.read(TOKENS_KEY);
|
||||
deleteByName(allTokens, token, 'token');
|
||||
@@ -53,6 +52,16 @@ async function signToken(req, res) {
|
||||
try {
|
||||
const { payload, options } = req.body;
|
||||
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;
|
||||
if (token != null) {
|
||||
if (typeof token !== 'string' || token.length < 1) {
|
||||
@@ -65,7 +74,12 @@ async function signToken(req, res) {
|
||||
);
|
||||
}
|
||||
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(
|
||||
res,
|
||||
new RequestInvalidError(
|
||||
@@ -75,16 +89,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') {
|
||||
const collections = $.read(COLLECTIONS_KEY) || [];
|
||||
const collection = collections.find((c) => c.name === name);
|
||||
@@ -153,7 +158,12 @@ async function signToken(req, res) {
|
||||
if (!token) {
|
||||
do {
|
||||
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({
|
||||
...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 { hex_md5 } from '@/vendor/md5';
|
||||
import { getPolicyDescriptor } from '@/utils';
|
||||
@@ -11,7 +11,11 @@ import {
|
||||
validCheck,
|
||||
} from '@/utils/flow';
|
||||
import $ from '@/core/app';
|
||||
import { findByName } from '@/utils/database';
|
||||
import { produceArtifact } from '@/restful/sync';
|
||||
import PROXY_PREPROCESSORS from '@/core/proxy-utils/preprocessors';
|
||||
import { ProxyUtils } from '@/core/proxy-utils';
|
||||
|
||||
const clashPreprocessor = PROXY_PREPROCESSORS.find(
|
||||
(processor) => processor.name === 'Clash Pre-processor',
|
||||
);
|
||||
@@ -130,22 +134,53 @@ export default async function download(
|
||||
}
|
||||
}
|
||||
|
||||
// const downloadUrlMatch = url.match(/^\/api\/(file|module)\/(.+)/);
|
||||
// if (downloadUrlMatch) {
|
||||
// let type = downloadUrlMatch?.[1];
|
||||
// let name = downloadUrlMatch?.[2];
|
||||
// if (name == null) {
|
||||
// throw new Error(`本地 ${type} URL 无效: ${url}`);
|
||||
// }
|
||||
// name = decodeURIComponent(name);
|
||||
// const key = type === 'module' ? MODULES_KEY : FILES_KEY;
|
||||
// const item = findByName($.read(key), name);
|
||||
// if (!item) {
|
||||
// throw new Error(`找不到本地 ${type}: ${name}`);
|
||||
// }
|
||||
const downloadUrlMatch = url
|
||||
.split('#')[0]
|
||||
.match(/^\/api\/(file|module)\/(.+)/);
|
||||
if (downloadUrlMatch) {
|
||||
let type = '';
|
||||
try {
|
||||
type = downloadUrlMatch?.[1];
|
||||
let name = downloadUrlMatch?.[2];
|
||||
if (name == null) {
|
||||
throw new Error(`本地 ${type} URL 无效: ${url}`);
|
||||
}
|
||||
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)) {
|
||||
return tasks.get(id);
|
||||
@@ -225,6 +260,20 @@ export default async function download(
|
||||
shouldCache = false;
|
||||
}
|
||||
}
|
||||
if (preprocess) {
|
||||
try {
|
||||
const proxies = ProxyUtils.parse(body);
|
||||
if (!Array.isArray(proxies) || proxies.length === 0) {
|
||||
$.error(`URL ${url} 不包含有效节点, 不缓存`);
|
||||
shouldCache = false;
|
||||
}
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`URL ${url} 尝试解析节点失败 ${e.message ?? e}, 不缓存`,
|
||||
);
|
||||
shouldCache = false;
|
||||
}
|
||||
}
|
||||
if (shouldCache) {
|
||||
resourceCache.set(id, body);
|
||||
if (customCacheKey) {
|
||||
@@ -273,3 +322,25 @@ export default async function download(
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function downloadFile(url, file) {
|
||||
const undici = eval("require('undici')");
|
||||
const fs = eval("require('fs')");
|
||||
const { pipeline } = eval("require('stream/promises')");
|
||||
const { Agent, interceptors, request } = undici;
|
||||
$.info(`Downloading file...\nURL: ${url}\nFile: ${file}`);
|
||||
const { body, statusCode } = await request(url, {
|
||||
dispatcher: new Agent().compose(
|
||||
interceptors.redirect({
|
||||
maxRedirections: 3,
|
||||
throwOnRedirect: true,
|
||||
}),
|
||||
),
|
||||
});
|
||||
if (statusCode !== 200)
|
||||
throw new Error(`Failed to download file from ${url}`);
|
||||
const fileStream = fs.createWriteStream(file);
|
||||
await pipeline(body, fileStream);
|
||||
$.info(`File downloaded from ${url} to ${file}`);
|
||||
return file;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ export async function getFlowHeaders(
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($arguments?.noFlow) {
|
||||
if ($arguments?.noFlow || !/^https?/.test(url)) {
|
||||
return;
|
||||
}
|
||||
const { isStash, isLoon, isShadowRocket, isQX } = ENV();
|
||||
@@ -230,7 +230,7 @@ export function flowTransfer(flow, unit = 'B') {
|
||||
let unitIndex = unitList.indexOf(unit);
|
||||
|
||||
return flow < 1024 || unitIndex === unitList.length - 1
|
||||
? { value: flow.toFixed(1), unit: unit }
|
||||
? { value: (Math.round(flow * 100) / 100).toString(), unit: unit }
|
||||
: flowTransfer(flow / 1024, unitList[++unitIndex]);
|
||||
}
|
||||
|
||||
@@ -334,7 +334,28 @@ export function normalizeFlowHeader(flowHeaders) {
|
||||
if (!kvMap.has(key)) {
|
||||
try {
|
||||
// 解码 URI 组件并保留原始值作为 fallback
|
||||
const decodedValue = decodeURIComponent(encodedValue);
|
||||
let decodedValue = decodeURIComponent(encodedValue);
|
||||
if (
|
||||
['upload', 'download', 'total', 'expire'].includes(
|
||||
key,
|
||||
)
|
||||
) {
|
||||
try {
|
||||
decodedValue = Number(decodedValue).toFixed(0);
|
||||
if (
|
||||
['expire'].includes(key) &&
|
||||
decodedValue <= 0
|
||||
) {
|
||||
decodedValue = '';
|
||||
}
|
||||
} catch (e) {
|
||||
$.error(
|
||||
`Failed to convert value for key "${key}=${encodedValue}": ${
|
||||
e.message ?? e
|
||||
}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
kvMap.set(key, decodedValue);
|
||||
} catch (e) {
|
||||
kvMap.set(key, encodedValue);
|
||||
|
||||
@@ -46,6 +46,7 @@ const ISOFlags = {
|
||||
'🇭🇰': ['HK', 'HKG', 'HKT', 'HKBN', 'HGC', 'WTT', 'CMI'],
|
||||
'🇭🇷': ['HR', 'HRV'],
|
||||
'🇭🇺': ['HU', 'HUN'],
|
||||
'🇮🇶': ['IQ', 'IRQ'], // 伊拉克
|
||||
'🇯🇴': ['JO', 'JOR'],
|
||||
'🇯🇵': ['JP', 'JPN', 'TYO'],
|
||||
'🇰🇪': ['KE', 'KEN'],
|
||||
@@ -99,6 +100,7 @@ const ISOFlags = {
|
||||
'🇸🇬': ['SG', 'SGP'],
|
||||
'🇸🇮': ['SI', 'SVN'],
|
||||
'🇸🇰': ['SK', 'SVK'],
|
||||
'🇹🇬': ['TG', 'TGO'], // 多哥
|
||||
'🇹🇭': ['TH', 'THA'],
|
||||
'🇹🇳': ['TN', 'TUN'],
|
||||
'🇹🇷': ['TR', 'TUR'],
|
||||
@@ -220,6 +222,7 @@ export function getFlag(name) {
|
||||
],
|
||||
'🇭🇷': ['Croatia', '克罗地亚', '克羅地亞'],
|
||||
'🇭🇺': ['Hungary', '匈牙利'],
|
||||
'🇮🇶': ['Iraq', '伊拉克', '巴格达', 'Baghdad'], // 伊拉克
|
||||
'🇯🇴': ['Jordan', '约旦'],
|
||||
'🇯🇵': [
|
||||
'Japan',
|
||||
@@ -338,6 +341,7 @@ export function getFlag(name) {
|
||||
],
|
||||
'🇸🇮': ['Slovenia', '斯洛文尼亚'],
|
||||
'🇸🇰': ['Slovakia', '斯洛伐克'],
|
||||
'🇹🇬': ['Togo', '多哥', '洛美', 'Lomé', 'Lome'], // 多哥
|
||||
'🇹🇭': ['Thailand', '泰国', '泰國', '曼谷'],
|
||||
'🇹🇳': ['Tunisia', '突尼斯'],
|
||||
'🇹🇷': ['Turkey', '土耳其', '伊斯坦布尔', 'Istanbul'],
|
||||
|
||||
@@ -9,7 +9,11 @@ import { SETTINGS_KEY } from '@/constants';
|
||||
export default class Gist {
|
||||
constructor({ token, key, syncPlatform }) {
|
||||
const { isStash, isLoon, isShadowRocket, isQX } = ENV();
|
||||
const { defaultProxy, defaultTimeout: timeout } = $.read(SETTINGS_KEY);
|
||||
const {
|
||||
defaultProxy,
|
||||
defaultTimeout: timeout,
|
||||
githubProxy,
|
||||
} = $.read(SETTINGS_KEY);
|
||||
let proxy = defaultProxy;
|
||||
if ($.env.isNode) {
|
||||
proxy =
|
||||
@@ -63,7 +67,9 @@ export default class Gist {
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.141 Safari/537.36',
|
||||
};
|
||||
this.http = HTTP({
|
||||
baseURL: 'https://api.github.com',
|
||||
baseURL: `${
|
||||
githubProxy ? `${githubProxy}/` : ''
|
||||
}https://api.github.com`,
|
||||
headers: {
|
||||
...this.headers,
|
||||
...(isStash && proxy
|
||||
@@ -280,7 +286,7 @@ export default class Gist {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
} else {
|
||||
return Promise.reject('找不到 Sub-Store Gist');
|
||||
return Promise.reject(`找不到 Sub-Store Gist (${this.key})`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,32 @@ function isValidUUID(uuid) {
|
||||
);
|
||||
}
|
||||
|
||||
function formatDateTime(date, format = 'YYYY-MM-DD_HH-mm-ss') {
|
||||
const d = date instanceof Date ? date : new Date(date);
|
||||
|
||||
if (isNaN(d.getTime())) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const pad = (num) => String(num).padStart(2, '0');
|
||||
|
||||
const replacements = {
|
||||
YYYY: d.getFullYear(),
|
||||
MM: pad(d.getMonth() + 1),
|
||||
DD: pad(d.getDate()),
|
||||
HH: pad(d.getHours()),
|
||||
mm: pad(d.getMinutes()),
|
||||
ss: pad(d.getSeconds()),
|
||||
};
|
||||
|
||||
return format.replace(
|
||||
/YYYY|MM|DD|HH|mm|ss/g,
|
||||
(match) => replacements[match],
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
formatDateTime,
|
||||
isValidUUID,
|
||||
ipAddress,
|
||||
isIPv4,
|
||||
|
||||
@@ -4,6 +4,8 @@ import {
|
||||
SCHEMA_VERSION_KEY,
|
||||
ARTIFACTS_KEY,
|
||||
RULES_KEY,
|
||||
FILES_KEY,
|
||||
TOKENS_KEY,
|
||||
} from '@/constants';
|
||||
import $ from '@/core/app';
|
||||
|
||||
@@ -55,7 +57,17 @@ function doMigrationV2() {
|
||||
const newRules = Object.values(rules);
|
||||
$.write(newRules, RULES_KEY);
|
||||
|
||||
// 5. delete builtin rules
|
||||
// 5. migrate files
|
||||
const files = $.read(FILES_KEY) || {};
|
||||
const newFiles = Object.values(files);
|
||||
$.write(newFiles, FILES_KEY);
|
||||
|
||||
// 6. migrate tokens
|
||||
const tokens = $.read(TOKENS_KEY) || {};
|
||||
const newTokens = Object.values(tokens);
|
||||
$.write(newTokens, TOKENS_KEY);
|
||||
|
||||
// 7. delete builtin rules
|
||||
delete $.cache.builtin;
|
||||
$.info('Migration complete!');
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ export function getPlatformFromUserAgent({ ua, UA, accept }) {
|
||||
return 'Clash';
|
||||
} else if (ua.indexOf('v2ray') !== -1) {
|
||||
return 'V2Ray';
|
||||
} else if (ua.indexOf('sing-box') !== -1) {
|
||||
} else if (ua.indexOf('sing-box') !== -1 || ua.indexOf('singbox') !== -1) {
|
||||
return 'sing-box';
|
||||
} else if (accept.indexOf('application/json') === 0) {
|
||||
return 'JSON';
|
||||
@@ -61,31 +61,41 @@ export function getPlatformFromHeaders(headers) {
|
||||
return getPlatformFromUserAgent({ ua, UA, accept });
|
||||
}
|
||||
export function shouldIncludeUnsupportedProxy(platform, ua) {
|
||||
try {
|
||||
const target = getPlatformFromUserAgent({
|
||||
UA: ua,
|
||||
ua: ua.toLowerCase(),
|
||||
});
|
||||
if (!['Stash', 'Egern'].includes(target)) {
|
||||
return false;
|
||||
}
|
||||
const version = coerce(ua).version;
|
||||
if (
|
||||
platform === 'Stash' &&
|
||||
target === 'Stash' &&
|
||||
gte(version, '2.8.0')
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
platform === 'Egern' &&
|
||||
target === 'Egern' &&
|
||||
gte(version, '1.29.0')
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
$.error(`获取版本号失败: ${e}`);
|
||||
}
|
||||
// try {
|
||||
// const target = getPlatformFromUserAgent({
|
||||
// UA: ua,
|
||||
// ua: ua.toLowerCase(),
|
||||
// });
|
||||
// if (!['Stash', 'Egern', 'Loon'].includes(target)) {
|
||||
// return false;
|
||||
// }
|
||||
// const coerceVersion = coerce(ua);
|
||||
// $.log(JSON.stringify(coerceVersion, null, 2));
|
||||
// const { version } = coerceVersion;
|
||||
// if (
|
||||
// platform === 'Stash' &&
|
||||
// target === 'Stash' &&
|
||||
// gte(version, '3.1.0')
|
||||
// ) {
|
||||
// return true;
|
||||
// }
|
||||
// if (
|
||||
// platform === 'Egern' &&
|
||||
// target === 'Egern' &&
|
||||
// gte(version, '1.29.0')
|
||||
// ) {
|
||||
// return true;
|
||||
// }
|
||||
// // Loon 的 UA 不规范, version 取出来是 build
|
||||
// if (
|
||||
// platform === 'Loon' &&
|
||||
// target === 'Loon' &&
|
||||
// gte(version, '842.0.0')
|
||||
// ) {
|
||||
// return true;
|
||||
// }
|
||||
// } catch (e) {
|
||||
// $.error(`获取版本号失败: ${e}`);
|
||||
// }
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -34,4 +34,6 @@ export default {
|
||||
load,
|
||||
safeDump,
|
||||
dump,
|
||||
parse: safeLoad,
|
||||
stringify: safeDump,
|
||||
};
|
||||
|
||||
13
backend/src/vendor/express.js
vendored
13
backend/src/vendor/express.js
vendored
@@ -17,10 +17,12 @@ export default function express({ substore: $, port, host }) {
|
||||
const express_ = eval(`require("express")`);
|
||||
const bodyParser = eval(`require("body-parser")`);
|
||||
const app = express_();
|
||||
const limit = eval('process.env.SUB_STORE_BODY_JSON_LIMIT') || '1mb';
|
||||
$.info(`[BACKEND] body JSON limit: ${limit}`);
|
||||
app.use(
|
||||
bodyParser.json({
|
||||
verify: rawBodySaver,
|
||||
limit: eval('process.env.SUB_STORE_BODY_JSON_LIMIT') || '1mb',
|
||||
limit,
|
||||
}),
|
||||
);
|
||||
app.use(
|
||||
@@ -34,9 +36,12 @@ export default function express({ substore: $, port, host }) {
|
||||
|
||||
// adapter
|
||||
app.start = () => {
|
||||
app.get('*', function (req, res) {
|
||||
res.status(404).end();
|
||||
});
|
||||
const listener = app.listen(port, host, () => {
|
||||
const { address, port } = listener.address();
|
||||
$.info(`[BACKEND] ${address}:${port}`);
|
||||
$.info(`[BACKEND] listening on ${address}:${port}`);
|
||||
});
|
||||
};
|
||||
return app;
|
||||
@@ -265,7 +270,7 @@ function extractURL(url) {
|
||||
let hashes = url.slice(url.indexOf('?') + 1).split('&');
|
||||
for (let i = 0; i < hashes.length; i++) {
|
||||
const hash = hashes[i].split('=');
|
||||
query[hash[0]] = hash[1];
|
||||
query[hash[0]] = decodeURIComponent(hash[1]);
|
||||
}
|
||||
}
|
||||
return {
|
||||
@@ -289,7 +294,7 @@ function extractPathParams(pattern, path) {
|
||||
while (path[j] !== '/' && j < path.length) {
|
||||
val.push(path[j++]);
|
||||
}
|
||||
params[key.join('')] = val.join('');
|
||||
params[key.join('')] = decodeURIComponent(val.join(''));
|
||||
} else {
|
||||
if (pattern[i] !== path[j]) {
|
||||
return null;
|
||||
|
||||
238
backend/src/vendor/open-api.js
vendored
238
backend/src/vendor/open-api.js
vendored
@@ -9,7 +9,36 @@ const isShadowRocket = 'undefined' !== typeof $rocket;
|
||||
const isEgern = 'object' == typeof egern;
|
||||
const isLanceX = 'undefined' != typeof $native;
|
||||
const isGUIforCores = typeof $Plugins !== 'undefined';
|
||||
import { Base64 } from 'js-base64';
|
||||
|
||||
function isPlainObject(obj) {
|
||||
return (
|
||||
obj !== null &&
|
||||
typeof obj === 'object' &&
|
||||
[null, Object.prototype].includes(Object.getPrototypeOf(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 {
|
||||
constructor(name = 'untitled', debug = false) {
|
||||
this.name = name;
|
||||
@@ -18,6 +47,10 @@ export class OpenAPI {
|
||||
this.http = HTTP();
|
||||
this.env = ENV();
|
||||
|
||||
if (isNode) {
|
||||
const dotenv = eval(`require("dotenv")`);
|
||||
dotenv.config();
|
||||
}
|
||||
this.node = (() => {
|
||||
if (isNode) {
|
||||
const fs = eval("require('fs')");
|
||||
@@ -58,29 +91,64 @@ export class OpenAPI {
|
||||
const basePath =
|
||||
eval('process.env.SUB_STORE_DATA_BASE_PATH') || '.';
|
||||
let rootPath = `${basePath}/root.json`;
|
||||
const backupRootPath = `${basePath}/root_${Date.now()}.json`;
|
||||
|
||||
this.log(`Root path: ${rootPath}`);
|
||||
if (!this.node.fs.existsSync(rootPath)) {
|
||||
if (this.node.fs.existsSync(rootPath)) {
|
||||
try {
|
||||
this.root = JSON.parse(
|
||||
this.node.fs.readFileSync(`${rootPath}`),
|
||||
);
|
||||
} catch (e) {
|
||||
this.node.fs.copyFileSync(rootPath, backupRootPath);
|
||||
this.error(
|
||||
`Failed to parse ${rootPath}: ${e.message}. Backup created at ${backupRootPath}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!isPlainObject(this.root)) {
|
||||
this.node.fs.writeFileSync(rootPath, JSON.stringify({}), {
|
||||
flag: 'wx',
|
||||
flag: 'w',
|
||||
});
|
||||
this.root = {};
|
||||
} else {
|
||||
this.root = JSON.parse(
|
||||
this.node.fs.readFileSync(`${rootPath}`),
|
||||
);
|
||||
}
|
||||
|
||||
// create a json file with the given name if not exists
|
||||
let fpath = `${basePath}/${this.name}.json`;
|
||||
const backupPath = `${basePath}/${this.name}_${Date.now()}.json`;
|
||||
|
||||
this.log(`Data path: ${fpath}`);
|
||||
if (!this.node.fs.existsSync(fpath)) {
|
||||
if (this.node.fs.existsSync(fpath)) {
|
||||
try {
|
||||
this.cache = JSON.parse(
|
||||
this.node.fs.readFileSync(`${fpath}`, 'utf-8'),
|
||||
);
|
||||
if (!isPlainObject(this.cache))
|
||||
throw new Error('Invalid Data');
|
||||
} catch (e) {
|
||||
try {
|
||||
const str = Base64.decode(
|
||||
this.node.fs.readFileSync(`${fpath}`, 'utf-8'),
|
||||
);
|
||||
this.cache = JSON.parse(str);
|
||||
this.node.fs.writeFileSync(fpath, str, {
|
||||
flag: 'w',
|
||||
});
|
||||
if (!isPlainObject(this.cache))
|
||||
throw new Error('Invalid Data');
|
||||
} catch (e) {
|
||||
this.node.fs.copyFileSync(fpath, backupPath);
|
||||
this.error(
|
||||
`Failed to parse ${fpath}: ${e.message}. Backup created at ${backupPath}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isPlainObject(this.cache)) {
|
||||
this.node.fs.writeFileSync(fpath, JSON.stringify({}), {
|
||||
flag: 'wx',
|
||||
flag: 'w',
|
||||
});
|
||||
this.cache = {};
|
||||
} else {
|
||||
this.cache = JSON.parse(this.node.fs.readFileSync(`${fpath}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -337,45 +405,130 @@ export function HTTP(defaultOptions = { baseURL: '' }) {
|
||||
opts: options.opts,
|
||||
});
|
||||
} else if (isLoon || isSurge || isNode) {
|
||||
worker = new Promise((resolve, reject) => {
|
||||
const request = isNode
|
||||
? eval("require('request')")
|
||||
: $httpClient;
|
||||
worker = new Promise(async (resolve, reject) => {
|
||||
const body = options.body;
|
||||
const opts = JSON.parse(JSON.stringify(options));
|
||||
opts.body = body;
|
||||
|
||||
if (!isNode && opts.timeout) {
|
||||
opts.timeout = opts.timeout || 8000;
|
||||
if (opts.timeout) {
|
||||
opts.timeout++;
|
||||
let unit = 'ms';
|
||||
// 这些客户端单位为 s
|
||||
if (isSurge || isStash || isShadowRocket) {
|
||||
opts.timeout = Math.ceil(opts.timeout / 1000);
|
||||
unit = 's';
|
||||
if (isNaN(opts.timeout)) {
|
||||
opts.timeout = 8000;
|
||||
}
|
||||
if (!isNode) {
|
||||
let unit = 'ms';
|
||||
// 这些客户端单位为 s
|
||||
if (isSurge || isStash || isShadowRocket) {
|
||||
opts.timeout = Math.ceil(opts.timeout / 1000);
|
||||
unit = 's';
|
||||
}
|
||||
// Loon 为 ms
|
||||
// console.log(`[httpClient timeout] ${opts.timeout}${unit}`);
|
||||
}
|
||||
// Loon 为 ms
|
||||
// console.log(`[httpClient timeout] ${opts.timeout}${unit}`);
|
||||
}
|
||||
request[method.toLowerCase()](opts, (err, response, body) => {
|
||||
// if (err) {
|
||||
// console.log(err);
|
||||
// } else {
|
||||
// console.log({
|
||||
// statusCode:
|
||||
// response.status || response.statusCode,
|
||||
// headers: response.headers,
|
||||
// body,
|
||||
// });
|
||||
// }
|
||||
|
||||
if (err) reject(err);
|
||||
else
|
||||
resolve({
|
||||
statusCode: response.status || response.statusCode,
|
||||
headers: response.headers,
|
||||
body,
|
||||
if (isNode) {
|
||||
const undici = eval("require('undici')");
|
||||
const { socksDispatcher } = eval("require('fetch-socks')");
|
||||
const {
|
||||
ProxyAgent,
|
||||
EnvHttpProxyAgent,
|
||||
request,
|
||||
interceptors,
|
||||
} = undici;
|
||||
const agentOpts = {
|
||||
connect: {
|
||||
rejectUnauthorized:
|
||||
opts.strictSSL === false ||
|
||||
opts.insecure === true
|
||||
? false
|
||||
: true,
|
||||
},
|
||||
bodyTimeout: opts.timeout,
|
||||
headersTimeout: opts.timeout,
|
||||
};
|
||||
try {
|
||||
const url = new URL(opts.url);
|
||||
if (url.username || url.password) {
|
||||
opts.headers = {
|
||||
...(opts.headers || {}),
|
||||
Authorization: `Basic ${Buffer.from(
|
||||
`${url.username || ''}:${
|
||||
url.password || ''
|
||||
}`,
|
||||
).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, {
|
||||
...opts,
|
||||
method: method.toUpperCase(),
|
||||
dispatcher: dispatcher.compose(
|
||||
interceptors.redirect({
|
||||
maxRedirections: 3,
|
||||
throwOnMaxRedirects: true,
|
||||
}),
|
||||
),
|
||||
});
|
||||
});
|
||||
resolve({
|
||||
statusCode: response.statusCode,
|
||||
headers: response.headers,
|
||||
body:
|
||||
opts.encoding === null
|
||||
? await response.body.arrayBuffer()
|
||||
: await response.body.text(),
|
||||
});
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
} else {
|
||||
$httpClient[method.toLowerCase()](
|
||||
opts,
|
||||
(err, response, body) => {
|
||||
// if (err) {
|
||||
// console.log(err);
|
||||
// } else {
|
||||
// console.log({
|
||||
// statusCode:
|
||||
// response.status || response.statusCode,
|
||||
// headers: response.headers,
|
||||
// body,
|
||||
// });
|
||||
// }
|
||||
|
||||
if (err) reject(err);
|
||||
else
|
||||
resolve({
|
||||
statusCode:
|
||||
response.status || response.statusCode,
|
||||
headers: response.headers,
|
||||
body,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
} else if (isGUIforCores) {
|
||||
worker = new Promise(async (resolve, reject) => {
|
||||
@@ -385,6 +538,7 @@ export function HTTP(defaultOptions = { baseURL: '' }) {
|
||||
url: options.url,
|
||||
headers: options.headers,
|
||||
body: options.body,
|
||||
autoTransformBody: false,
|
||||
options: {
|
||||
Proxy: options.proxy,
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,15 +13,22 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
// 5. `_subName` 为单条订阅名, `_subDisplayName` 为单条订阅显示名
|
||||
// 6. `_collectionName` 为组合订阅名, `_collectionDisplayName` 为组合订阅显示名
|
||||
// 7. `tls-fingerprint` 为 tls 指纹
|
||||
// 8. `underlying-proxy` 为前置代理
|
||||
// 8. `underlying-proxy` 为前置代理, 不同平台会自动转换
|
||||
// 例如 $server['underlying-proxy'] = '名称'
|
||||
// 只给 mihomo 输出的话, `dialer-proxy` 也行
|
||||
// 只给 sing-box 输出的话, `detour` 也行
|
||||
// 只给 Egern 输出的话, `prev_hop` 也行
|
||||
// 只给 Shadowrocket 输出的话, `chain` 也行
|
||||
// 输出到 Clash/Stash 时, 会过滤掉配置了前置代理的节点, 并提示使用对应的功能.
|
||||
// 9. `trojan`, `tuic`, `hysteria`, `hysteria2`, `juicity` 会在解析时设置 `tls`: true (会使用 tls 类协议的通用逻辑), 输出时删除
|
||||
// 10. `sni` 在某些协议里会自动与 `servername` 转换
|
||||
// 11. 读取节点的 ca-str 和 _ca (后端文件路径) 字段, 自动计算 fingerprint (参考 https://t.me/zhetengsha/1512)
|
||||
// 12. 以 Surge 为例, 最新的参数一般我都会跟进, 以 Surge 文档为例, 一些常用的: TUIC/Hysteria 2 的 `ecn`, Snell 的 `reuse` 连接复用, QUIC 策略 block-quic`, Hysteria 2 下载带宽 `down`
|
||||
// 13. `test-url` 为测延迟链接, `test-timeout` 为测延迟超时
|
||||
// 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`
|
||||
// 17. `block-quic` 支持 `auto`, `on`, `off`. 不同的平台不一定都支持, 会自动转换
|
||||
|
||||
// require 为 Node.js 的 require, 在 Node.js 运行环境下 可以用来引入模块
|
||||
// 例如在 Node.js 环境下, 将文件内容写入 /tmp/1.txt 文件
|
||||
@@ -40,8 +47,27 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
// 先这样处理 encodeURIComponent('arg1=a&arg2=b')
|
||||
// /api/file/foo?$options=arg1%3Da%26arg2%3Db
|
||||
|
||||
// 默认会带上 _req 字段, 结构为
|
||||
// {
|
||||
// method,
|
||||
// url,
|
||||
// path,
|
||||
// query,
|
||||
// params,
|
||||
// headers,
|
||||
// body,
|
||||
// }
|
||||
// console.log($options)
|
||||
|
||||
// 若设置 $options._res.headers
|
||||
// 则会在输出文件时设置响应头, 例如:
|
||||
|
||||
// $options._res = {
|
||||
// headers: {
|
||||
// 'X-Custom': '1'
|
||||
// }
|
||||
// }
|
||||
|
||||
// targetPlatform 为输出的目标平台
|
||||
|
||||
// lodash
|
||||
@@ -79,6 +105,12 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
|
||||
// 其他平台同理, 持久化缓存数据在 JSON 里
|
||||
|
||||
// 当配合脚本使用时, 可以在脚本的前面添加一个脚本操作, 实现保留 1 小时的缓存. 这样比较灵活
|
||||
|
||||
// async function operator() {
|
||||
// scriptResourceCache._cleanup(undefined, 1 * 3600 * 1000);
|
||||
// }
|
||||
|
||||
// ProxyUtils 为节点处理工具
|
||||
// 可参考 https://t.me/zhetengsha/1066
|
||||
// const ProxyUtils = {
|
||||
@@ -96,9 +128,14 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
// getISO, // 获取 ISO 3166-1 alpha-2 代码
|
||||
// Gist, // Gist 类
|
||||
// download, // 内部的下载方法, 见 backend/src/utils/download.js
|
||||
// downloadFile, // 下载二进制文件, 见 backend/src/utils/download.js
|
||||
// MMDB, // Node.js 环境 可用于模拟 Surge/Loon 的 $utils.ipasn, $utils.ipaso, $utils.geoip. 具体见 https://t.me/zhetengsha/1269
|
||||
// isValidUUID, // 辅助判断是否为有效的 UUID
|
||||
// Buffer, // https://github.com/feross/buffer
|
||||
// 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://t.me/zhetengsha/970 和 脚本筛选支持节点快捷脚本 https://t.me/zhetengsha/1009
|
||||
// ⚠️ 注意: 函数式(即本文件这样的 function operator() {}) 和快捷操作(下面使用 $server) 只能二选一
|
||||
@@ -114,6 +151,18 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
// });
|
||||
// $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 完整配置
|
||||
|
||||
// let proxies = await produceArtifact({
|
||||
@@ -198,14 +247,14 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
// 这个历史遗留原因, 是有点复杂. 提供一个例子, 用来取当前脚本所在的组合订阅或单条订阅名称
|
||||
|
||||
// let name = ''
|
||||
// for (const [key, value] of Object.entries(env.source)) {
|
||||
// for (const [key, value] of Object.entries(context.source)) {
|
||||
// if (!key.startsWith('_')) {
|
||||
// name = value.displayName || value.name
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// if (!name) {
|
||||
// const collection = env.source._collection
|
||||
// const collection = context.source._collection
|
||||
// name = collection.displayName || collection.name
|
||||
// }
|
||||
|
||||
|
||||
BIN
support.nodeseek.com_page_promotion_id=8.png
Normal file
BIN
support.nodeseek.com_page_promotion_id=8.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 129 KiB |
Reference in New Issue
Block a user