mirror of
https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 00:52:40 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a417d9852 | ||
|
|
ebff520499 | ||
|
|
876d2e92ca | ||
|
|
63064bc596 | ||
|
|
e81245a5bb | ||
|
|
217fdae7f1 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sub-store",
|
||||
"version": "2.19.83",
|
||||
"version": "2.19.89",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -134,6 +134,18 @@ export default function Clash_Producer() {
|
||||
proxy['h2-opts'].headers.host = [host];
|
||||
}
|
||||
}
|
||||
if (proxy.network === 'ws') {
|
||||
const wsPath = proxy['ws-opts']?.path;
|
||||
const reg = /^(.*?)(?:\?ed=(\d+))?$/;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [_, path = '', ed = ''] = reg.exec(wsPath);
|
||||
proxy['ws-opts'].path = path;
|
||||
if (ed !== '') {
|
||||
proxy['ws-opts']['early-data-header-name'] =
|
||||
'Sec-WebSocket-Protocol';
|
||||
proxy['ws-opts']['max-early-data'] = parseInt(ed, 10);
|
||||
}
|
||||
}
|
||||
if (proxy['plugin-opts']?.tls) {
|
||||
if (isPresent(proxy, 'skip-cert-verify')) {
|
||||
proxy['plugin-opts']['skip-cert-verify'] =
|
||||
|
||||
@@ -198,6 +198,18 @@ export default function ClashMeta_Producer() {
|
||||
proxy['h2-opts'].headers.host = [host];
|
||||
}
|
||||
}
|
||||
if (proxy.network === 'ws') {
|
||||
const wsPath = proxy['ws-opts']?.path;
|
||||
const reg = /^(.*?)(?:\?ed=(\d+))?$/;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [_, path = '', ed = ''] = reg.exec(wsPath);
|
||||
proxy['ws-opts'].path = path;
|
||||
if (ed !== '') {
|
||||
proxy['ws-opts']['early-data-header-name'] =
|
||||
'Sec-WebSocket-Protocol';
|
||||
proxy['ws-opts']['max-early-data'] = parseInt(ed, 10);
|
||||
}
|
||||
}
|
||||
|
||||
if (proxy['plugin-opts']?.tls) {
|
||||
if (isPresent(proxy, 'skip-cert-verify')) {
|
||||
|
||||
@@ -166,6 +166,18 @@ export default function Shadowrocket_Producer() {
|
||||
proxy['h2-opts'].headers.host = [host];
|
||||
}
|
||||
}
|
||||
if (proxy.network === 'ws') {
|
||||
const wsPath = proxy['ws-opts']?.path;
|
||||
const reg = /^(.*?)(?:\?ed=(\d+))?$/;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [_, path = '', ed = ''] = reg.exec(wsPath);
|
||||
proxy['ws-opts'].path = path;
|
||||
if (ed !== '') {
|
||||
proxy['ws-opts']['early-data-header-name'] =
|
||||
'Sec-WebSocket-Protocol';
|
||||
proxy['ws-opts']['max-early-data'] = parseInt(ed, 10);
|
||||
}
|
||||
}
|
||||
if (proxy['plugin-opts']?.tls) {
|
||||
if (isPresent(proxy, 'skip-cert-verify')) {
|
||||
proxy['plugin-opts']['skip-cert-verify'] =
|
||||
|
||||
@@ -71,7 +71,14 @@ const smuxParser = (smux, proxy) => {
|
||||
const wsParser = (proxy, parsedProxy) => {
|
||||
const transport = { type: 'ws', headers: {} };
|
||||
if (proxy['ws-opts']) {
|
||||
const { path: wsPath = '', headers: wsHeaders = {} } = proxy['ws-opts'];
|
||||
const {
|
||||
path: wsPath = '',
|
||||
headers: wsHeaders = {},
|
||||
'max-early-data': max_early_data,
|
||||
'early-data-header-name': early_data_header_name,
|
||||
} = proxy['ws-opts'];
|
||||
transport.early_data_header_name = early_data_header_name;
|
||||
transport.max_early_data = parseInt(max_early_data, 10);
|
||||
if (wsPath !== '') transport.path = `${wsPath}`;
|
||||
if (Object.keys(wsHeaders).length > 0) {
|
||||
const headers = {};
|
||||
|
||||
@@ -238,6 +238,18 @@ export default function Stash_Producer() {
|
||||
proxy['h2-opts'].headers.host = [host];
|
||||
}
|
||||
}
|
||||
if (proxy.network === 'ws') {
|
||||
const wsPath = proxy['ws-opts']?.path;
|
||||
const reg = /^(.*?)(?:\?ed=(\d+))?$/;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [_, path = '', ed = ''] = reg.exec(wsPath);
|
||||
proxy['ws-opts'].path = path;
|
||||
if (ed !== '') {
|
||||
proxy['ws-opts']['early-data-header-name'] =
|
||||
'Sec-WebSocket-Protocol';
|
||||
proxy['ws-opts']['max-early-data'] = parseInt(ed, 10);
|
||||
}
|
||||
}
|
||||
if (proxy['plugin-opts']?.tls) {
|
||||
if (isPresent(proxy, 'skip-cert-verify')) {
|
||||
proxy['plugin-opts']['skip-cert-verify'] =
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Base64 } from 'js-base64';
|
||||
import _ from 'lodash';
|
||||
import express from '@/vendor/express';
|
||||
import $ from '@/core/app';
|
||||
import migrate from '@/utils/migration';
|
||||
@@ -304,6 +306,7 @@ export default function serve() {
|
||||
const path = eval(`require("path")`);
|
||||
const fs = eval(`require("fs")`);
|
||||
const data_url = eval('process.env.SUB_STORE_DATA_URL');
|
||||
const data_url_post = eval('process.env.SUB_STORE_DATA_URL_POST');
|
||||
const fe_be_path = eval('process.env.SUB_STORE_FRONTEND_BACKEND_PATH');
|
||||
const fe_port = eval('process.env.SUB_STORE_FRONTEND_PORT') || 3001;
|
||||
const fe_host =
|
||||
@@ -424,10 +427,39 @@ export default function serve() {
|
||||
if (data_url) {
|
||||
$.info(`[BACKEND] downloading data from ${data_url}`);
|
||||
download(data_url)
|
||||
.then((content) => {
|
||||
$.write(content, '#sub-store');
|
||||
.then(async (content) => {
|
||||
try {
|
||||
content = JSON.parse(Base64.decode(content));
|
||||
if (Object.keys(content.settings).length === 0) {
|
||||
throw new Error(
|
||||
'备份文件应该至少包含 settings 字段',
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
try {
|
||||
content = JSON.parse(content);
|
||||
if (Object.keys(content.settings).length === 0) {
|
||||
throw new Error(
|
||||
'备份文件应该至少包含 settings 字段',
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
$.error(
|
||||
`Gist 备份文件校验失败, 无法还原\nReason: ${
|
||||
err.message ?? err
|
||||
}`,
|
||||
);
|
||||
throw new Error('Gist 备份文件校验失败, 无法还原');
|
||||
}
|
||||
}
|
||||
if (data_url_post) {
|
||||
$.info('[BACKEND] executing post-processing script');
|
||||
eval(data_url_post);
|
||||
}
|
||||
|
||||
$.cache = JSON.parse(content);
|
||||
$.write(JSON.stringify(content, null, ` `), '#sub-store');
|
||||
|
||||
$.cache = content;
|
||||
$.persistCache();
|
||||
|
||||
migrate();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Base64 } from 'js-base64';
|
||||
import _ from 'lodash';
|
||||
import $ from '@/core/app';
|
||||
import { ENV } from '@/vendor/open-api';
|
||||
@@ -40,10 +41,30 @@ export default function register($app) {
|
||||
);
|
||||
})
|
||||
.post((req, res) => {
|
||||
const { content } = req.body;
|
||||
$.write(content, '#sub-store');
|
||||
let { content } = req.body;
|
||||
try {
|
||||
content = JSON.parse(Base64.decode(content));
|
||||
if (Object.keys(content.settings).length === 0) {
|
||||
throw new Error('备份文件应该至少包含 settings 字段');
|
||||
}
|
||||
} catch (err) {
|
||||
try {
|
||||
content = JSON.parse(content);
|
||||
if (Object.keys(content.settings).length === 0) {
|
||||
throw new Error('备份文件应该至少包含 settings 字段');
|
||||
}
|
||||
} catch (err) {
|
||||
$.error(
|
||||
`备份文件校验失败, 无法还原\nReason: ${
|
||||
err.message ?? err
|
||||
}`,
|
||||
);
|
||||
throw new Error('备份文件校验失败, 无法还原');
|
||||
}
|
||||
}
|
||||
$.write(JSON.stringify(content, null, ` `), '#sub-store');
|
||||
if ($.env.isNode) {
|
||||
$.cache = JSON.parse(content);
|
||||
$.cache = content;
|
||||
$.persistCache();
|
||||
}
|
||||
migrate();
|
||||
@@ -105,7 +126,7 @@ async function refresh(_, res) {
|
||||
success(res);
|
||||
}
|
||||
|
||||
async function gistBackupAction(action, keep) {
|
||||
async function gistBackupAction(action, keep, encode) {
|
||||
// read token
|
||||
const { gistToken, syncPlatform } = $.read(SETTINGS_KEY);
|
||||
if (!gistToken) throw new Error('GitHub Token is required for backup!');
|
||||
@@ -127,8 +148,16 @@ async function gistBackupAction(action, keep) {
|
||||
content = $.read('#sub-store');
|
||||
content = content ? JSON.parse(content) : {};
|
||||
if ($.env.isNode) content = JSON.parse(JSON.stringify($.cache));
|
||||
content.settings.gistToken = '恢复后请重新设置 GitHub Token';
|
||||
content = JSON.stringify(content, null, ` `);
|
||||
if (encode === 'plaintext') {
|
||||
content.settings.gistToken =
|
||||
'恢复后请重新设置 GitHub Token';
|
||||
content = JSON.stringify(content, null, ` `);
|
||||
} else {
|
||||
content = Base64.encode(
|
||||
JSON.stringify(content, null, ` `),
|
||||
);
|
||||
}
|
||||
|
||||
$.info(`下载备份, 与本地内容对比...`);
|
||||
const onlineContent = await gist.download(
|
||||
GIST_BACKUP_FILE_NAME,
|
||||
@@ -147,8 +176,12 @@ async function gistBackupAction(action, keep) {
|
||||
content = $.read('#sub-store');
|
||||
content = content ? JSON.parse(content) : {};
|
||||
if ($.env.isNode) content = JSON.parse(JSON.stringify($.cache));
|
||||
content.settings.gistToken = '恢复后请重新设置 GitHub Token';
|
||||
content = JSON.stringify(content, null, ` `);
|
||||
if (encode === 'plaintext') {
|
||||
content.settings.gistToken = '恢复后请重新设置 GitHub Token';
|
||||
content = JSON.stringify(content, null, ` `);
|
||||
} else {
|
||||
content = Base64.encode(JSON.stringify(content, null, ` `));
|
||||
}
|
||||
$.info(`上传备份中...`);
|
||||
try {
|
||||
await gist.upload({
|
||||
@@ -166,17 +199,24 @@ async function gistBackupAction(action, keep) {
|
||||
$.info(`还原备份中...`);
|
||||
content = await gist.download(GIST_BACKUP_FILE_NAME);
|
||||
try {
|
||||
content = JSON.parse(content);
|
||||
content = JSON.parse(Base64.decode(content));
|
||||
if (Object.keys(content.settings).length === 0) {
|
||||
throw new Error('备份文件应该至少包含 settings 字段');
|
||||
}
|
||||
} catch (err) {
|
||||
$.error(
|
||||
`Gist 备份文件校验失败, 无法还原\nReason: ${
|
||||
err.message ?? err
|
||||
}`,
|
||||
);
|
||||
throw new Error('Gist 备份文件校验失败, 无法还原');
|
||||
try {
|
||||
content = JSON.parse(content);
|
||||
if (Object.keys(content.settings).length === 0) {
|
||||
throw new Error('备份文件应该至少包含 settings 字段');
|
||||
}
|
||||
} catch (err) {
|
||||
$.error(
|
||||
`Gist 备份文件校验失败, 无法还原\nReason: ${
|
||||
err.message ?? err
|
||||
}`,
|
||||
);
|
||||
throw new Error('Gist 备份文件校验失败, 无法还原');
|
||||
}
|
||||
}
|
||||
if (keep) {
|
||||
$.info(`保留原有设置 ${keep}`);
|
||||
@@ -198,7 +238,7 @@ async function gistBackupAction(action, keep) {
|
||||
}
|
||||
}
|
||||
async function gistBackup(req, res) {
|
||||
const { action, keep } = req.query;
|
||||
const { action, keep, encode } = req.query;
|
||||
// read token
|
||||
const { gistToken } = $.read(SETTINGS_KEY);
|
||||
if (!gistToken) {
|
||||
@@ -211,7 +251,7 @@ async function gistBackup(req, res) {
|
||||
);
|
||||
} else {
|
||||
try {
|
||||
await gistBackupAction(action, keep);
|
||||
await gistBackupAction(action, keep, encode);
|
||||
success(res);
|
||||
} catch (err) {
|
||||
$.error(
|
||||
|
||||
21
backend/src/vendor/open-api.js
vendored
21
backend/src/vendor/open-api.js
vendored
@@ -9,6 +9,7 @@ const isShadowRocket = 'undefined' !== typeof $rocket;
|
||||
const isEgern = 'object' == typeof egern;
|
||||
const isLanceX = 'undefined' != typeof $native;
|
||||
const isGUIforCores = typeof $Plugins !== 'undefined';
|
||||
import { Base64 } from 'js-base64';
|
||||
|
||||
function isPlainObject(obj) {
|
||||
return (
|
||||
@@ -120,13 +121,23 @@ export class OpenAPI {
|
||||
if (this.node.fs.existsSync(fpath)) {
|
||||
try {
|
||||
this.cache = JSON.parse(
|
||||
this.node.fs.readFileSync(`${fpath}`),
|
||||
this.node.fs.readFileSync(`${fpath}`, 'utf-8'),
|
||||
);
|
||||
} catch (e) {
|
||||
this.node.fs.copyFileSync(fpath, backupPath);
|
||||
this.error(
|
||||
`Failed to parse ${fpath}: ${e.message}. Backup created at ${backupPath}`,
|
||||
);
|
||||
try {
|
||||
const str = Base64.decode(
|
||||
this.node.fs.readFileSync(`${fpath}`, 'utf-8'),
|
||||
);
|
||||
this.cache = JSON.parse(str);
|
||||
this.node.fs.writeFileSync(fpath, str, {
|
||||
flag: 'w',
|
||||
});
|
||||
} catch (e) {
|
||||
this.node.fs.copyFileSync(fpath, backupPath);
|
||||
this.error(
|
||||
`Failed to parse ${fpath}: ${e.message}. Backup created at ${backupPath}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!isPlainObject(this.cache)) {
|
||||
|
||||
1
backend/sub-store_1752902525508.json
Normal file
1
backend/sub-store_1752902525508.json
Normal file
File diff suppressed because one or more lines are too long
1
backend/sub-store_1752902571053.json
Normal file
1
backend/sub-store_1752902571053.json
Normal file
File diff suppressed because one or more lines are too long
1
backend/sub-store_1752902602701.json
Normal file
1
backend/sub-store_1752902602701.json
Normal file
File diff suppressed because one or more lines are too long
1
backend/sub-store_1752902651631.json
Normal file
1
backend/sub-store_1752902651631.json
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user