Skip to content

Commit 916ae98

Browse files
committed
feat: 调整生成 mihomo 配置的逻辑, 内容在脚本操作前生成; 文件路由支持 download 参数启用下载(文件名为显示名称)
1 parent 4b97309 commit 916ae98

7 files changed

Lines changed: 109 additions & 39 deletions

File tree

backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "sub-store",
3-
"version": "2.28.1",
3+
"version": "2.28.2",
44
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
55
"main": "src/main.js",
66
"packageManager": "pnpm@11.0.9",

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

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -412,28 +412,7 @@ function ScriptOperator(
412412
if (typeof patch !== 'object')
413413
throw new Error('patch is not an object');
414414
output.$content = ProxyUtils.yaml.safeDump(
415-
deepMerge(
416-
config ||
417-
(output?.$file?.sourceType === 'none'
418-
? {}
419-
: {
420-
proxies: await produceArtifact({
421-
type:
422-
output?.$file?.sourceType ||
423-
'collection',
424-
name: output?.$file?.sourceName,
425-
platform: 'mihomo',
426-
produceType: 'internal',
427-
produceOpts: {
428-
'delete-underscore-fields': true,
429-
'include-unsupported-proxy':
430-
output?.$file
431-
?.includeUnsupportedProxy,
432-
},
433-
}),
434-
}),
435-
patch,
436-
),
415+
deepMerge(config || {}, patch),
437416
);
438417
return output;
439418
} catch (e) {
@@ -470,19 +449,7 @@ function ScriptOperator(
470449
console.log(e.message ?? e);
471450
}
472451
}
473-
$content = ProxyUtils.yaml.safeDump(await main(config || ($file.sourceType === 'none' ? {} : {
474-
proxies: await produceArtifact({
475-
type: $file.sourceType || 'collection',
476-
name: $file.sourceName,
477-
platform: 'mihomo',
478-
produceType: 'internal',
479-
produceOpts: {
480-
'delete-underscore-fields': true,
481-
'include-unsupported-proxy':
482-
$file.includeUnsupportedProxy,
483-
}
484-
}),
485-
})))
452+
$content = ProxyUtils.yaml.safeDump(await main(config || {}))
486453
}
487454
} else {
488455
${script}

backend/src/restful/file.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ async function getFile(req, res, next) {
6969
proxy,
7070
noCache,
7171
produceType,
72+
download,
7273
fakeFile: _fakeFile,
7374
} = req.query;
7475
let $options = {
@@ -204,6 +205,9 @@ async function getFile(req, res, next) {
204205
if (produceType) {
205206
$.info(`指定生产类型: ${produceType}`);
206207
}
208+
if (download) {
209+
$.info('启用下载(文件名为显示名称)');
210+
}
207211
if (includeUnsupportedProxy) {
208212
$.info(`包含官方/商店版不支持的协议: ${includeUnsupportedProxy}`);
209213
}
@@ -277,7 +281,7 @@ async function getFile(req, res, next) {
277281
)}`,
278282
);
279283
}
280-
if (file.download) {
284+
if (file.download || download) {
281285
res.set(
282286
'Content-Disposition',
283287
`attachment; filename*=UTF-8''${encodeURIComponent(

backend/src/restful/preview.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
resolveIgnoreFailedRemoteSubMode,
1212
shouldFallbackIgnoreFailedRemoteSub,
1313
} from '@/restful/ignore-failed-remote-sub';
14+
import { prepareMihomoProfileContent } from '@/restful/sync';
1415
import { normalizeClashYaml } from '@/core/proxy-utils/preprocessors';
1516
import { maskAgeSecretInUrl } from '@/utils/age';
1617

@@ -28,7 +29,9 @@ async function previewFile(req, res) {
2829
try {
2930
const file = req.body;
3031
let content = '';
31-
if (file.type !== 'mihomoProfile') {
32+
if (file.type === 'mihomoProfile') {
33+
content = await prepareMihomoProfileContent(file);
34+
} else {
3235
if (
3336
file.source === 'local' &&
3437
!['localFirst', 'remoteFirst'].includes(file.mergeSources)

backend/src/restful/sync.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,23 @@ function formatAgeSafeUrls(errors) {
4545
return Object.keys(errors).map(maskAgeSecretInUrl).join(', ');
4646
}
4747

48+
async function prepareMihomoProfileContent(file) {
49+
const config = {};
50+
if (file?.sourceType !== 'none') {
51+
config.proxies = await produceArtifact({
52+
type: file?.sourceType || 'collection',
53+
name: file?.sourceName,
54+
platform: 'mihomo',
55+
produceType: 'internal',
56+
produceOpts: {
57+
'delete-underscore-fields': true,
58+
'include-unsupported-proxy': file?.includeUnsupportedProxy,
59+
},
60+
});
61+
}
62+
return ProxyUtils.yaml.safeDump(config);
63+
}
64+
4865
async function produceArtifact({
4966
type,
5067
name,
@@ -583,7 +600,9 @@ async function produceArtifact({
583600
const file = sourceFile || findByName(allFiles, name);
584601
if (!file) throw new Error(`找不到文件 ${name}`);
585602
let raw = '';
586-
if (file.type !== 'mihomoProfile') {
603+
if (file.type === 'mihomoProfile') {
604+
raw = await prepareMihomoProfileContent(file);
605+
} else {
587606
if (
588607
content &&
589608
!['localFirst', 'remoteFirst'].includes(mergeSources)
@@ -1220,6 +1239,7 @@ async function syncArtifact(req, res) {
12201239

12211240
export {
12221241
markArtifactProducedWithoutUpload,
1242+
prepareMihomoProfileContent,
12231243
produceArtifact,
12241244
produceSyncArtifactOutput,
12251245
shouldUploadArtifact,

backend/src/test/restful/file.spec.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,20 @@ describe('file routes', function () {
225225
expect(fakeFileRes.sent).to.equal('from-fake-file');
226226
});
227227

228+
it('forces attachment download with the display name from query', async function () {
229+
state[FILES_KEY][0].displayName = '显示名称.yaml';
230+
231+
const res = await requestFile({ download: '1' });
232+
233+
expect(res.statusCode).to.equal(200);
234+
expect(res.headers['Content-Disposition']).to.equal(
235+
`attachment; filename*=UTF-8''${encodeURIComponent(
236+
'显示名称.yaml',
237+
)}`,
238+
);
239+
expect(res.sent).to.equal('base');
240+
});
241+
228242
it('rejects fakeFile without content or url', async function () {
229243
state[FILES_KEY] = [];
230244

backend/src/test/restful/mihomo-profile-file.spec.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { FILES_KEY, SUBS_KEY } from '@/constants';
55

66
let $;
77
let registerFileRoutes;
8+
let registerPreviewRoutes;
89
let originalError;
910
let originalInfo;
1011
let originalNotify;
@@ -101,10 +102,33 @@ async function requestFile(name, query = {}) {
101102
return res;
102103
}
103104

105+
async function previewFile(file) {
106+
const app = createRouteApp();
107+
registerPreviewRoutes(app);
108+
const handler = app.handlers.get('POST /api/preview/file');
109+
const res = createResponse('/api/preview/file');
110+
111+
await handler(
112+
{
113+
body: file,
114+
headers: {},
115+
method: 'POST',
116+
path: '/api/preview/file',
117+
query: {},
118+
socket: {},
119+
url: '/api/preview/file',
120+
},
121+
res,
122+
);
123+
124+
return res;
125+
}
126+
104127
describe('mihomo profile file routes', function () {
105128
before(async function () {
106129
({ default: $ } = require('@/core/app'));
107130
({ default: registerFileRoutes } = require('@/restful/file'));
131+
({ default: registerPreviewRoutes } = require('@/restful/preview'));
108132

109133
originalRead = $.read.bind($);
110134
originalWrite = $.write.bind($);
@@ -152,6 +176,44 @@ describe('mihomo profile file routes', function () {
152176
$.notify = () => {};
153177
});
154178

179+
it('builds mihomoProfile proxies before file processors run', async function () {
180+
state[FILES_KEY] = [
181+
{
182+
name: 'base-file',
183+
type: 'mihomoProfile',
184+
sourceType: 'subscription',
185+
sourceName: 'demo-sub',
186+
includeUnsupportedProxy: false,
187+
process: [],
188+
},
189+
];
190+
191+
const res = await requestFile('base-file');
192+
193+
expect(res.statusCode).to.equal(200);
194+
expect(res.sent).to.include('proxies:');
195+
expect(res.sent).to.include('name: Supported');
196+
expect(res.sent).to.not.include('name: Unsupported');
197+
});
198+
199+
it('previews mihomoProfile proxies before file processors run', async function () {
200+
const res = await previewFile({
201+
name: 'preview-file',
202+
type: 'mihomoProfile',
203+
sourceType: 'subscription',
204+
sourceName: 'demo-sub',
205+
includeUnsupportedProxy: false,
206+
process: [],
207+
});
208+
209+
expect(res.statusCode).to.equal(200);
210+
expect(res.sent.status).to.equal('success');
211+
expect(res.sent.data.original).to.include('proxies:');
212+
expect(res.sent.data.original).to.include('name: Supported');
213+
expect(res.sent.data.original).to.not.include('name: Unsupported');
214+
expect(res.sent.data.processed).to.include('name: Supported');
215+
});
216+
155217
it('keeps unsupported proxies for YAML mihomoProfile overrides when enabled', async function () {
156218
state[FILES_KEY] = [
157219
{

0 commit comments

Comments
 (0)