diff --git a/backend/package.json b/backend/package.json index 8b8c305e0..1e4d899f6 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "sub-store", - "version": "2.14.374", + "version": "2.14.375", "description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.", "main": "src/main.js", "scripts": { diff --git a/backend/src/restful/index.js b/backend/src/restful/index.js index 502a886e4..d500d3da9 100644 --- a/backend/src/restful/index.js +++ b/backend/src/restful/index.js @@ -3,6 +3,7 @@ import $ from '@/core/app'; import migrate from '@/utils/migration'; import download from '@/utils/download'; import { syncArtifacts } from '@/restful/sync'; +import { gistBackupAction } from '@/restful/miscs'; import registerSubscriptionRoutes from './subscriptions'; import registerCollectionRoutes from './collections'; @@ -46,18 +47,76 @@ export default function serve() { if ($.env.isNode) { const backend_cron = eval('process.env.SUB_STORE_BACKEND_CRON'); if (backend_cron) { - $.info(`[CRON] ${backend_cron} enabled`); + $.info(`[SYNC CRON] ${backend_cron} enabled`); const { CronJob } = eval(`require("cron")`); new CronJob( backend_cron, async function () { try { - $.info(`[CRON] ${backend_cron} started`); + $.info(`[SYNC CRON] ${backend_cron} started`); await syncArtifacts(); - $.info(`[CRON] ${backend_cron} finished`); + $.info(`[SYNC CRON] ${backend_cron} finished`); } catch (e) { $.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 diff --git a/backend/src/restful/miscs.js b/backend/src/restful/miscs.js index a00defcf2..9699f12cb 100644 --- a/backend/src/restful/miscs.js +++ b/backend/src/restful/miscs.js @@ -80,10 +80,72 @@ async function refresh(_, 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) { const { action } = req.query; // read token - const { gistToken, syncPlatform } = $.read(SETTINGS_KEY); + const { gistToken } = $.read(SETTINGS_KEY); if (!gistToken) { failed( res, @@ -93,68 +155,8 @@ async function gistBackup(req, res) { ), ); } else { - const gist = new Gist({ - token: gistToken, - key: GIST_BACKUP_KEY, - syncPlatform, - }); try { - 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 }, - }); - } 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; - } + await gistBackupAction(action); success(res); } catch (err) { $.error( @@ -171,3 +173,5 @@ async function gistBackup(req, res) { } } } + +export { gistBackupAction };