Skip to content

Commit 690b2c1

Browse files
committed
feat: clash 系/sing-box mux 逻辑处理; dev 保持两种
1 parent 0883ec9 commit 690b2c1

10 files changed

Lines changed: 200 additions & 146 deletions

File tree

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,13 @@ pnpm i
104104
```
105105

106106
```
107-
SUB_STORE_BACKEND_API_PORT=3000 pnpm dev
107+
SUB_STORE_BACKEND_API_PORT=3000 pnpm esbuild:dev
108+
```
109+
110+
or this one if you're using `Termux`
111+
112+
```
113+
SUB_STORE_BACKEND_API_PORT=3000 pnpm run --parallel "/^dev:.*/"
108114
```
109115

110116
### Build

backend/dev-esbuild.js

Lines changed: 21 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -1,148 +1,27 @@
11
#!/usr/bin/env node
2-
const path = require('path');
3-
const { spawn } = require('child_process');
4-
const { context } = require('esbuild');
5-
6-
const outfile = path.join(__dirname, 'sub-store.min.js');
7-
8-
let serverProcess = null;
9-
let buildContext = null;
10-
let shuttingDown = false;
11-
let restartQueue = Promise.resolve();
12-
13-
function log(message) {
14-
console.log(`[dev:esbuild] ${message}`);
15-
}
16-
17-
function startServer() {
18-
const child = spawn(process.execPath, [outfile], {
19-
cwd: __dirname,
20-
stdio: 'inherit',
21-
});
22-
23-
serverProcess = child;
24-
log(`server started (pid ${child.pid})`);
25-
26-
child.on('exit', (code, signal) => {
27-
if (serverProcess === child) {
28-
serverProcess = null;
29-
}
30-
31-
if (!shuttingDown && signal !== 'SIGTERM') {
32-
log(
33-
`server exited${code === null ? '' : ` with code ${code}`}${
34-
signal ? ` (${signal})` : ''
35-
}`,
36-
);
37-
}
38-
});
39-
}
40-
41-
async function stopServer() {
42-
const child = serverProcess;
43-
if (!child) {
44-
return;
45-
}
46-
47-
serverProcess = null;
48-
49-
if (child.exitCode !== null || child.signalCode !== null) {
50-
return;
51-
}
52-
53-
await new Promise((resolve) => {
54-
const timeout = setTimeout(() => {
55-
child.kill('SIGKILL');
56-
}, 3000);
57-
58-
child.once('exit', () => {
59-
clearTimeout(timeout);
60-
resolve();
61-
});
62-
63-
child.kill('SIGTERM');
64-
});
65-
}
66-
67-
async function restartServer() {
68-
if (serverProcess) {
69-
log('restarting server');
70-
await stopServer();
71-
}
72-
73-
if (!shuttingDown) {
74-
startServer();
75-
}
76-
}
77-
78-
async function shutdown(signal) {
79-
if (shuttingDown) {
80-
return;
81-
}
82-
83-
shuttingDown = true;
84-
log(`received ${signal}, shutting down`);
85-
86-
if (buildContext) {
87-
await buildContext.dispose();
88-
}
89-
90-
await restartQueue.catch((error) => {
91-
console.error(error);
92-
});
93-
await stopServer();
94-
}
2+
const { build } = require('esbuild');
953

964
!(async () => {
97-
buildContext = await context({
98-
entryPoints: ['src/main.js'],
99-
bundle: true,
100-
minify: false,
101-
sourcemap: false,
102-
platform: 'node',
103-
format: 'cjs',
104-
outfile,
105-
logOverride: {
106-
'direct-eval': 'silent',
107-
},
108-
plugins: [
109-
{
110-
name: 'restart-server-on-build',
111-
setup(build) {
112-
build.onStart(() => {
113-
log('building');
114-
});
115-
116-
build.onEnd((result) => {
117-
if (result.errors.length > 0) {
118-
log(`build failed with ${result.errors.length} error(s)`);
119-
return;
120-
}
121-
122-
log('build succeeded');
123-
restartQueue = restartQueue
124-
.catch((error) => {
125-
console.error(error);
126-
})
127-
.then(() => restartServer());
128-
return restartQueue;
129-
});
130-
},
5+
const artifacts = [{ src: 'src/main.js', dest: 'sub-store.min.js' }];
6+
7+
for await (const artifact of artifacts) {
8+
await build({
9+
entryPoints: [artifact.src],
10+
bundle: true,
11+
minify: false,
12+
sourcemap: false,
13+
platform: 'node',
14+
format: 'cjs',
15+
outfile: artifact.dest,
16+
logOverride: {
17+
'direct-eval': 'silent',
13118
},
132-
],
133-
});
134-
135-
process.on('SIGINT', () => {
136-
shutdown('SIGINT').finally(() => process.exit(0));
137-
});
138-
process.on('SIGTERM', () => {
139-
shutdown('SIGTERM').finally(() => process.exit(0));
140-
});
141-
142-
await buildContext.watch();
143-
log('watching for changes');
19+
});
20+
}
14421
})()
145-
.catch((error) => {
146-
console.error(error);
147-
process.exitCode = 1;
22+
.catch((e) => {
23+
console.log(e);
24+
})
25+
.finally(() => {
26+
console.log('done');
14827
});

backend/esbuild-dev.js

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#!/usr/bin/env node
2+
const path = require('path');
3+
const { spawn } = require('child_process');
4+
const { context } = require('esbuild');
5+
6+
const outfile = path.join(__dirname, 'sub-store.min.js');
7+
8+
let serverProcess = null;
9+
let buildContext = null;
10+
let shuttingDown = false;
11+
let restartQueue = Promise.resolve();
12+
13+
function log(message) {
14+
console.log(`[dev:esbuild] ${message}`);
15+
}
16+
17+
function startServer() {
18+
const child = spawn(process.execPath, [outfile], {
19+
cwd: __dirname,
20+
stdio: 'inherit',
21+
});
22+
23+
serverProcess = child;
24+
log(`server started (pid ${child.pid})`);
25+
26+
child.on('exit', (code, signal) => {
27+
if (serverProcess === child) {
28+
serverProcess = null;
29+
}
30+
31+
if (!shuttingDown && signal !== 'SIGTERM') {
32+
log(
33+
`server exited${code === null ? '' : ` with code ${code}`}${
34+
signal ? ` (${signal})` : ''
35+
}`,
36+
);
37+
}
38+
});
39+
}
40+
41+
async function stopServer() {
42+
const child = serverProcess;
43+
if (!child) {
44+
return;
45+
}
46+
47+
serverProcess = null;
48+
49+
if (child.exitCode !== null || child.signalCode !== null) {
50+
return;
51+
}
52+
53+
await new Promise((resolve) => {
54+
const timeout = setTimeout(() => {
55+
child.kill('SIGKILL');
56+
}, 3000);
57+
58+
child.once('exit', () => {
59+
clearTimeout(timeout);
60+
resolve();
61+
});
62+
63+
child.kill('SIGTERM');
64+
});
65+
}
66+
67+
async function restartServer() {
68+
if (serverProcess) {
69+
log('restarting server');
70+
await stopServer();
71+
}
72+
73+
if (!shuttingDown) {
74+
startServer();
75+
}
76+
}
77+
78+
async function shutdown(signal) {
79+
if (shuttingDown) {
80+
return;
81+
}
82+
83+
shuttingDown = true;
84+
log(`received ${signal}, shutting down`);
85+
86+
if (buildContext) {
87+
await buildContext.dispose();
88+
}
89+
90+
await restartQueue.catch((error) => {
91+
console.error(error);
92+
});
93+
await stopServer();
94+
}
95+
96+
!(async () => {
97+
buildContext = await context({
98+
entryPoints: ['src/main.js'],
99+
bundle: true,
100+
minify: false,
101+
sourcemap: false,
102+
platform: 'node',
103+
format: 'cjs',
104+
outfile,
105+
logOverride: {
106+
'direct-eval': 'silent',
107+
},
108+
plugins: [
109+
{
110+
name: 'restart-server-on-build',
111+
setup(build) {
112+
build.onStart(() => {
113+
log('building');
114+
});
115+
116+
build.onEnd((result) => {
117+
if (result.errors.length > 0) {
118+
log(`build failed with ${result.errors.length} error(s)`);
119+
return;
120+
}
121+
122+
log('build succeeded');
123+
restartQueue = restartQueue
124+
.catch((error) => {
125+
console.error(error);
126+
})
127+
.then(() => restartServer());
128+
return restartQueue;
129+
});
130+
},
131+
},
132+
],
133+
});
134+
135+
process.on('SIGINT', () => {
136+
shutdown('SIGINT').finally(() => process.exit(0));
137+
});
138+
process.on('SIGTERM', () => {
139+
shutdown('SIGTERM').finally(() => process.exit(0));
140+
});
141+
142+
await buildContext.watch();
143+
log('watching for changes');
144+
})()
145+
.catch((error) => {
146+
console.error(error);
147+
process.exitCode = 1;
148+
});

backend/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
{
22
"name": "sub-store",
3-
"version": "2.21.73",
3+
"version": "2.21.74",
44
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
55
"main": "src/main.js",
66
"scripts": {
77
"preinstall": "npx only-allow pnpm",
88
"test": "mocha src/test/**/*.spec.js --require @babel/register --recursive",
99
"serve": "node sub-store.min.js",
1010
"start": "nodemon -w src -w package.json --exec babel-node src/main.js",
11-
"dev": "node dev-esbuild.js",
11+
"dev:esbuild": "nodemon -w src -w package.json dev-esbuild.js",
12+
"dev:run": "nodemon -w sub-store.min.js sub-store.min.js",
13+
"esbuild:dev": "node esbuild-dev.js",
1214
"bundle:esbuild": "node bundle-esbuild.js",
1315
"changelog": "conventional-changelog -p cli -i CHANGELOG.md -s"
1416
},

backend/src/core/proxy-utils/parsers/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,9 @@ function URI_SS() {
319319
'skip-cert-verify': ['1', 'true', 1, true].includes(
320320
params['skip-cert-verify'],
321321
),
322-
mux: params.mux,
322+
mux: /^\d+$/.test(params.mux)
323+
? parseInt(params.mux, 10)
324+
: undefined,
323325
};
324326
break;
325327
case 'shadow-tls': {

backend/src/core/proxy-utils/producers/clash.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ export default function Clash_Producer() {
160160
proxy[`${proxy.network}-opts`].path = '/';
161161
}
162162
}
163+
let mux = proxy['plugin-opts']?.mux;
164+
if (mux != null && typeof mux !== 'boolean') {
165+
proxy['plugin-opts'].mux = mux > 0 ? true : false;
166+
}
163167
if (proxy['plugin-opts']?.tls) {
164168
if (isPresent(proxy, 'skip-cert-verify')) {
165169
proxy['plugin-opts']['skip-cert-verify'] =

backend/src/core/proxy-utils/producers/clashmeta.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,10 @@ export default function ClashMeta_Producer() {
239239
}
240240
}
241241

242+
let mux = proxy['plugin-opts']?.mux;
243+
if (mux != null && typeof mux !== 'boolean') {
244+
proxy['plugin-opts'].mux = mux > 0 ? true : false;
245+
}
242246
if (proxy['plugin-opts']?.tls) {
243247
if (isPresent(proxy, 'skip-cert-verify')) {
244248
proxy['plugin-opts']['skip-cert-verify'] =

backend/src/core/proxy-utils/producers/shadowrocket.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,10 @@ export default function Shadowrocket_Producer() {
214214
proxy[`${proxy.network}-opts`].path = '/';
215215
}
216216
}
217+
let mux = proxy['plugin-opts']?.mux;
218+
if (mux != null && typeof mux !== 'boolean') {
219+
proxy['plugin-opts'].mux = mux > 0 ? true : false;
220+
}
217221
if (proxy['plugin-opts']?.tls) {
218222
if (isPresent(proxy, 'skip-cert-verify')) {
219223
proxy['plugin-opts']['skip-cert-verify'] =

0 commit comments

Comments
 (0)