Skip to content

Commit 58ab0a7

Browse files
committed
feat: 优化 Gist 同步诊断信息并更新 Node/Mocha 依赖策略
- 新增 `.node-version`(26.1.0)并在 GitHub Action 中改为按文件读取 Node 版本 - backend 版本号提升到 2.22.27,Mocha 升级到 ^11.7.5 并同步更新 lockfile - 重构 Gist 错误信息提取逻辑,统一返回 `ERROR: HTTP 状态码: 消息`,便于定位 API 问题 - Gist 同步前补充上传摘要日志(文件数、总大小、最大文件),同步失败时输出失败上下文 - 同步异常日志改为输出完整错误栈,便于排查 - 同步到 Gist 的失败场景与新增日志行为补充单测覆盖
1 parent 59f985a commit 58ab0a7

9 files changed

Lines changed: 343 additions & 108 deletions

File tree

.github/workflows/main.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@ jobs:
1616
runs-on: ubuntu-latest
1717
steps:
1818
- name: Checkout
19-
uses: actions/checkout@v3
19+
uses: actions/checkout@v6
2020
with:
2121
ref: "master"
2222
- name: Set up Node.js
23-
uses: actions/setup-node@v3
23+
uses: actions/setup-node@v6
2424
with:
25-
node-version: "20"
25+
node-version-file: ".node-version"
2626
- name: Install dependencies
2727
run: |
28-
npm install -g pnpm
28+
corepack enable
2929
cd backend && pnpm i --no-frozen-lockfile
3030
- name: Sync latest mihomo version
3131
run: |

.node-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
24.15.0

backend/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{
22
"name": "sub-store",
3-
"version": "2.22.26",
3+
"version": "2.22.28",
44
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
55
"main": "src/main.js",
6+
"packageManager": "pnpm@11.0.9",
67
"scripts": {
78
"preinstall": "npx only-allow pnpm",
89
"test": "mocha src/test/**/*.spec.js --require @babel/register --recursive",
@@ -54,7 +55,7 @@
5455
"chai": "^4.3.6",
5556
"esbuild": "^0.19.8",
5657
"eslint": "^8.16.0",
57-
"mocha": "^10.0.0",
58+
"mocha": "^11.7.5",
5859
"nodemon": "^2.0.16",
5960
"peggy": "^2.0.1",
6061
"prettier": "2.6.2"

backend/pnpm-lock.yaml

Lines changed: 221 additions & 71 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/pnpm-workspace.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
allowBuilds:
2+
core-js: true
3+
esbuild: true

backend/src/products/cron-sync-artifacts.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,6 @@ async function doSync(arg = {}) {
269269
}
270270
} catch (e) {
271271
$.notify('🌍 Sub-Store', '同步配置失败', `原因:${e.message ?? e}`);
272-
$.error(`无法同步配置到 Gist,原因:${e}`);
272+
$.error(`无法同步配置到 Gist,原因:${e.stack ?? e.message ?? e}`);
273273
}
274274
}

backend/src/restful/artifacts.js

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -323,16 +323,38 @@ async function syncToGist(files, options = {}) {
323323
if (!gistToken) {
324324
return Promise.reject('未设置 GitHub Token!');
325325
}
326+
const uploadSummary = summarizeGistUploadFiles(files);
327+
$.info(
328+
`准备同步 Gist: 文件数 ${uploadSummary.count}, 总大小 ${formatBytes(
329+
uploadSummary.totalBytes,
330+
)}, 最大文件 ${
331+
uploadSummary.largestFilename || '-'
332+
} (${formatBytes(uploadSummary.largestBytes)})`,
333+
);
326334
const manager = new Gist({
327335
token: gistToken,
328336
key: ARTIFACT_REPOSITORY_KEY,
329337
syncPlatform,
330338
});
331-
const res = await manager.upload(files, {
332-
...options,
333-
emptyFileFallback:
334-
options.emptyFileFallback ?? getArtifactGistEmptyFileFallback(),
335-
});
339+
let res;
340+
try {
341+
res = await manager.upload(files, {
342+
...options,
343+
emptyFileFallback:
344+
options.emptyFileFallback ?? getArtifactGistEmptyFileFallback(),
345+
});
346+
} catch (error) {
347+
$.error(
348+
`同步 Gist 请求失败: 文件数 ${uploadSummary.count}, 总大小 ${formatBytes(
349+
uploadSummary.totalBytes,
350+
)}, 最大文件 ${
351+
uploadSummary.largestFilename || '-'
352+
} (${formatBytes(uploadSummary.largestBytes)}), 原因: ${
353+
error.message ?? error
354+
}`,
355+
);
356+
throw error;
357+
}
336358
let body = {};
337359
try {
338360
body = JSON.parse(res.body);
@@ -353,5 +375,41 @@ async function syncToGist(files, options = {}) {
353375
return res;
354376
}
355377

378+
function summarizeGistUploadFiles(files) {
379+
return Object.entries(files || {}).reduce(
380+
(summary, [filename, file]) => {
381+
const content = file?.content;
382+
if (typeof content !== 'string') return summary;
383+
const bytes = stringByteLength(content);
384+
summary.count++;
385+
summary.totalBytes += bytes;
386+
if (bytes > summary.largestBytes) {
387+
summary.largestBytes = bytes;
388+
summary.largestFilename = filename;
389+
}
390+
return summary;
391+
},
392+
{
393+
count: 0,
394+
totalBytes: 0,
395+
largestBytes: 0,
396+
largestFilename: '',
397+
},
398+
);
399+
}
400+
401+
function stringByteLength(value) {
402+
if (typeof TextEncoder !== 'undefined') {
403+
return new TextEncoder().encode(value).length;
404+
}
405+
return unescape(encodeURIComponent(value)).length;
406+
}
407+
408+
function formatBytes(size) {
409+
if (size < 1024) return `${size} B`;
410+
if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`;
411+
return `${(size / 1024 / 1024).toFixed(1)} MB`;
412+
}
413+
356414
export { syncToGist };
357415
export { createArtifactItem, deleteArtifactItem };

backend/src/test/utils/gist.spec.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import { describe, it } from 'mocha';
33

44
import $ from '@/core/app';
55
import { SETTINGS_KEY } from '@/constants';
6-
import Gist, { getGithubGistBaseURL } from '@/utils/gist';
6+
import Gist, {
7+
describeGistApiErrorResponse,
8+
getGithubGistBaseURL,
9+
} from '@/utils/gist';
710
import { syncToGist } from '@/restful/artifacts';
811

912
describe('Gist GitHub API URL', function () {
@@ -36,6 +39,20 @@ describe('Gist GitHub API URL', function () {
3639
).to.equal('https://litegist.example.com/api');
3740
});
3841

42+
it('includes HTTP status when describing Gist API errors', function () {
43+
expect(
44+
describeGistApiErrorResponse({
45+
statusCode: 500,
46+
body: JSON.stringify({
47+
message:
48+
'Internal Server Error: Error: D1 query budget exceeded',
49+
}),
50+
}),
51+
).to.equal(
52+
'ERROR: HTTP 500: Internal Server Error: Error: D1 query budget exceeded',
53+
);
54+
});
55+
3956
it('keeps an existing Gist alive with a fallback file when a delete would empty it', async function () {
4057
const manager = Object.create(Gist.prototype);
4158
let patchBody;
@@ -143,9 +160,11 @@ describe('Gist GitHub API URL', function () {
143160
it('passes the artifact placeholder fallback to syncToGist by default', async function () {
144161
const originalRead = $.read.bind($);
145162
const originalWrite = $.write.bind($);
163+
const originalInfo = $.info.bind($);
146164
const originalUpload = Gist.prototype.upload;
147165
let capturedOptions;
148166
let writtenSettings;
167+
const infoMessages = [];
149168

150169
$.read = (key) => {
151170
if (key === SETTINGS_KEY) {
@@ -162,6 +181,9 @@ describe('Gist GitHub API URL', function () {
162181
}
163182
return originalWrite(data, key);
164183
};
184+
$.info = (message) => {
185+
infoMessages.push(message);
186+
};
165187
Gist.prototype.upload = async function (_, options) {
166188
capturedOptions = options;
167189
return {
@@ -181,6 +203,7 @@ describe('Gist GitHub API URL', function () {
181203
} finally {
182204
$.read = originalRead;
183205
$.write = originalWrite;
206+
$.info = originalInfo;
184207
Gist.prototype.upload = originalUpload;
185208
}
186209

@@ -193,5 +216,8 @@ describe('Gist GitHub API URL', function () {
193216
'https://gist.example.com/sub-store',
194217
);
195218
expect(writtenSettings.artifactStoreStatus).to.equal('VALID');
219+
expect(infoMessages).to.include(
220+
'准备同步 Gist: 文件数 1, 总大小 12 B, 最大文件 artifact (12 B)',
221+
);
196222
});
197223
});

backend/src/utils/gist.js

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,22 @@ import { SETTINGS_KEY } from '@/constants';
55

66
const DEFAULT_GITHUB_API_URL = 'https://api.github.com';
77

8+
function describeGistApiErrorResponse(resp) {
9+
let body;
10+
try {
11+
body = JSON.parse(resp.body);
12+
} catch (e) {
13+
//
14+
}
15+
const message =
16+
body?.message?.error ??
17+
body?.error ??
18+
body?.message ??
19+
resp.body ??
20+
'Unknown error';
21+
return `ERROR: HTTP ${resp.statusCode}: ${message}`;
22+
}
23+
824
function normalizeApiUrl(url, fallback = DEFAULT_GITHUB_API_URL) {
925
const normalizedUrl = String(url ?? '').trim() || fallback;
1026

@@ -28,6 +44,8 @@ export function getGithubGistBaseURL({ githubApiUrl, githubProxy } = {}) {
2844
}${DEFAULT_GITHUB_API_URL}`;
2945
}
3046

47+
export { describeGistApiErrorResponse };
48+
3149
/**
3250
* Gist backup
3351
*/
@@ -81,19 +99,8 @@ export default class Gist {
8199
events: {
82100
onResponse: (resp) => {
83101
if (/^[45]/.test(String(resp.statusCode))) {
84-
let body;
85-
try {
86-
body = JSON.parse(resp.body);
87-
} catch (e) {
88-
//
89-
}
90102
return Promise.reject(
91-
`ERROR: ${
92-
body?.message?.error ??
93-
body?.error ??
94-
body?.message ??
95-
resp.body
96-
}`,
103+
describeGistApiErrorResponse(resp),
97104
);
98105
} else {
99106
return resp;
@@ -131,19 +138,8 @@ export default class Gist {
131138
events: {
132139
onResponse: (resp) => {
133140
if (/^[45]/.test(String(resp.statusCode))) {
134-
let body;
135-
try {
136-
body = JSON.parse(resp.body);
137-
} catch (e) {
138-
//
139-
}
140141
return Promise.reject(
141-
`ERROR: ${
142-
body?.message?.error ??
143-
body?.error ??
144-
body?.message ??
145-
resp.body
146-
}`,
142+
describeGistApiErrorResponse(resp),
147143
);
148144
} else {
149145
return resp;

0 commit comments

Comments
 (0)