mirror of
https://git.mirrors.martin98.com/https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-22 10:29:05 +08:00
feat: Node.js 版支持环境变量 SUB_STORE_BACKEND_DOWNLOAD_CRON
设置定时恢复配置, SUB_STORE_BACKEND_UPLOAD_CRON
设置定时备份配置
This commit is contained in:
parent
75d88c02c7
commit
4c558cfdcd
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sub-store",
|
"name": "sub-store",
|
||||||
"version": "2.14.374",
|
"version": "2.14.375",
|
||||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||||
"main": "src/main.js",
|
"main": "src/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -3,6 +3,7 @@ import $ from '@/core/app';
|
|||||||
import migrate from '@/utils/migration';
|
import migrate from '@/utils/migration';
|
||||||
import download from '@/utils/download';
|
import download from '@/utils/download';
|
||||||
import { syncArtifacts } from '@/restful/sync';
|
import { syncArtifacts } from '@/restful/sync';
|
||||||
|
import { gistBackupAction } from '@/restful/miscs';
|
||||||
|
|
||||||
import registerSubscriptionRoutes from './subscriptions';
|
import registerSubscriptionRoutes from './subscriptions';
|
||||||
import registerCollectionRoutes from './collections';
|
import registerCollectionRoutes from './collections';
|
||||||
@ -46,18 +47,76 @@ export default function serve() {
|
|||||||
if ($.env.isNode) {
|
if ($.env.isNode) {
|
||||||
const backend_cron = eval('process.env.SUB_STORE_BACKEND_CRON');
|
const backend_cron = eval('process.env.SUB_STORE_BACKEND_CRON');
|
||||||
if (backend_cron) {
|
if (backend_cron) {
|
||||||
$.info(`[CRON] ${backend_cron} enabled`);
|
$.info(`[SYNC CRON] ${backend_cron} enabled`);
|
||||||
const { CronJob } = eval(`require("cron")`);
|
const { CronJob } = eval(`require("cron")`);
|
||||||
new CronJob(
|
new CronJob(
|
||||||
backend_cron,
|
backend_cron,
|
||||||
async function () {
|
async function () {
|
||||||
try {
|
try {
|
||||||
$.info(`[CRON] ${backend_cron} started`);
|
$.info(`[SYNC CRON] ${backend_cron} started`);
|
||||||
await syncArtifacts();
|
await syncArtifacts();
|
||||||
$.info(`[CRON] ${backend_cron} finished`);
|
$.info(`[SYNC CRON] ${backend_cron} finished`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
$.error(
|
$.error(
|
||||||
`[CRON] ${backend_cron} error: ${e.message ?? e}`,
|
`[SYNC CRON] ${backend_cron} error: ${
|
||||||
|
e.message ?? e
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, // onTick
|
||||||
|
null, // onComplete
|
||||||
|
true, // start
|
||||||
|
// 'Asia/Shanghai' // timeZone
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const backend_download_cron = eval(
|
||||||
|
'process.env.SUB_STORE_BACKEND_DOWNLOAD_CRON',
|
||||||
|
);
|
||||||
|
if (backend_download_cron) {
|
||||||
|
$.info(`[DOWNLOAD CRON] ${backend_download_cron} enabled`);
|
||||||
|
const { CronJob } = eval(`require("cron")`);
|
||||||
|
new CronJob(
|
||||||
|
backend_download_cron,
|
||||||
|
async function () {
|
||||||
|
try {
|
||||||
|
$.info(
|
||||||
|
`[DOWNLOAD CRON] ${backend_download_cron} started`,
|
||||||
|
);
|
||||||
|
await gistBackupAction('download');
|
||||||
|
$.info(
|
||||||
|
`[DOWNLOAD CRON] ${backend_download_cron} finished`,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
$.error(
|
||||||
|
`[DOWNLOAD CRON] ${backend_download_cron} error: ${
|
||||||
|
e.message ?? e
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, // onTick
|
||||||
|
null, // onComplete
|
||||||
|
true, // start
|
||||||
|
// 'Asia/Shanghai' // timeZone
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const backend_upload_cron = eval(
|
||||||
|
'process.env.SUB_STORE_BACKEND_UPLOAD_CRON',
|
||||||
|
);
|
||||||
|
if (backend_upload_cron) {
|
||||||
|
$.info(`[UPLOAD CRON] ${backend_upload_cron} enabled`);
|
||||||
|
const { CronJob } = eval(`require("cron")`);
|
||||||
|
new CronJob(
|
||||||
|
backend_upload_cron,
|
||||||
|
async function () {
|
||||||
|
try {
|
||||||
|
$.info(`[UPLOAD CRON] ${backend_upload_cron} started`);
|
||||||
|
await gistBackupAction('upload');
|
||||||
|
$.info(`[UPLOAD CRON] ${backend_upload_cron} finished`);
|
||||||
|
} catch (e) {
|
||||||
|
$.error(
|
||||||
|
`[UPLOAD CRON] ${backend_upload_cron} error: ${
|
||||||
|
e.message ?? e
|
||||||
|
}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, // onTick
|
}, // onTick
|
||||||
|
@ -80,10 +80,72 @@ async function refresh(_, res) {
|
|||||||
success(res);
|
success(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function gistBackupAction(action) {
|
||||||
|
// read token
|
||||||
|
const { gistToken, syncPlatform } = $.read(SETTINGS_KEY);
|
||||||
|
if (!gistToken) throw new Error('GitHub Token is required for backup!');
|
||||||
|
|
||||||
|
const gist = new Gist({
|
||||||
|
token: gistToken,
|
||||||
|
key: GIST_BACKUP_KEY,
|
||||||
|
syncPlatform,
|
||||||
|
});
|
||||||
|
let content;
|
||||||
|
const settings = $.read(SETTINGS_KEY);
|
||||||
|
const updated = settings.syncTime;
|
||||||
|
switch (action) {
|
||||||
|
case 'upload':
|
||||||
|
// update syncTime
|
||||||
|
settings.syncTime = new Date().getTime();
|
||||||
|
$.write(settings, SETTINGS_KEY);
|
||||||
|
content = $.read('#sub-store');
|
||||||
|
if ($.env.isNode) content = JSON.stringify($.cache, null, ` `);
|
||||||
|
$.info(`上传备份中...`);
|
||||||
|
try {
|
||||||
|
await gist.upload({
|
||||||
|
[GIST_BACKUP_FILE_NAME]: { content },
|
||||||
|
});
|
||||||
|
$.info(`上传备份完成`);
|
||||||
|
} catch (err) {
|
||||||
|
// restore syncTime if upload failed
|
||||||
|
settings.syncTime = updated;
|
||||||
|
$.write(settings, SETTINGS_KEY);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'download':
|
||||||
|
$.info(`还原备份中...`);
|
||||||
|
content = await gist.download(GIST_BACKUP_FILE_NAME);
|
||||||
|
try {
|
||||||
|
if (Object.keys(JSON.parse(content).settings).length === 0) {
|
||||||
|
throw new Error('备份文件应该至少包含 settings 字段');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
$.error(
|
||||||
|
`Gist 备份文件校验失败, 无法还原\nReason: ${
|
||||||
|
err.message ?? err
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
throw new Error('Gist 备份文件校验失败, 无法还原');
|
||||||
|
}
|
||||||
|
// restore settings
|
||||||
|
$.write(content, '#sub-store');
|
||||||
|
if ($.env.isNode) {
|
||||||
|
content = JSON.parse(content);
|
||||||
|
$.cache = content;
|
||||||
|
$.persistCache();
|
||||||
|
}
|
||||||
|
$.info(`perform migration after restoring from gist...`);
|
||||||
|
migrate();
|
||||||
|
$.info(`migration completed`);
|
||||||
|
$.info(`还原备份完成`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
async function gistBackup(req, res) {
|
async function gistBackup(req, res) {
|
||||||
const { action } = req.query;
|
const { action } = req.query;
|
||||||
// read token
|
// read token
|
||||||
const { gistToken, syncPlatform } = $.read(SETTINGS_KEY);
|
const { gistToken } = $.read(SETTINGS_KEY);
|
||||||
if (!gistToken) {
|
if (!gistToken) {
|
||||||
failed(
|
failed(
|
||||||
res,
|
res,
|
||||||
@ -93,68 +155,8 @@ async function gistBackup(req, res) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const gist = new Gist({
|
|
||||||
token: gistToken,
|
|
||||||
key: GIST_BACKUP_KEY,
|
|
||||||
syncPlatform,
|
|
||||||
});
|
|
||||||
try {
|
try {
|
||||||
let content;
|
await gistBackupAction(action);
|
||||||
const settings = $.read(SETTINGS_KEY);
|
|
||||||
const updated = settings.syncTime;
|
|
||||||
switch (action) {
|
|
||||||
case 'upload':
|
|
||||||
// update syncTime
|
|
||||||
settings.syncTime = new Date().getTime();
|
|
||||||
$.write(settings, SETTINGS_KEY);
|
|
||||||
content = $.read('#sub-store');
|
|
||||||
if ($.env.isNode)
|
|
||||||
content = JSON.stringify($.cache, null, ` `);
|
|
||||||
$.info(`上传备份中...`);
|
|
||||||
try {
|
|
||||||
await gist.upload({
|
|
||||||
[GIST_BACKUP_FILE_NAME]: { content },
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
// restore syncTime if upload failed
|
|
||||||
settings.syncTime = updated;
|
|
||||||
$.write(settings, SETTINGS_KEY);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'download':
|
|
||||||
$.info(`还原备份中...`);
|
|
||||||
content = await gist.download(GIST_BACKUP_FILE_NAME);
|
|
||||||
try {
|
|
||||||
if (
|
|
||||||
Object.keys(JSON.parse(content).settings).length ===
|
|
||||||
0
|
|
||||||
) {
|
|
||||||
throw new Error(
|
|
||||||
'备份文件应该至少包含 settings 字段',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
$.error(
|
|
||||||
`Gist 备份文件校验失败, 无法还原\nReason: ${
|
|
||||||
err.message ?? err
|
|
||||||
}`,
|
|
||||||
);
|
|
||||||
throw new Error('Gist 备份文件校验失败, 无法还原');
|
|
||||||
}
|
|
||||||
// restore settings
|
|
||||||
$.write(content, '#sub-store');
|
|
||||||
if ($.env.isNode) {
|
|
||||||
content = JSON.parse(content);
|
|
||||||
$.cache = content;
|
|
||||||
$.persistCache();
|
|
||||||
}
|
|
||||||
$.info(`perform migration after restoring from gist...`);
|
|
||||||
migrate();
|
|
||||||
$.info(`migration completed`);
|
|
||||||
$.info(`还原备份完成`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
success(res);
|
success(res);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
$.error(
|
$.error(
|
||||||
@ -171,3 +173,5 @@ async function gistBackup(req, res) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { gistBackupAction };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user