Compare commits

..

1 Commits

Author SHA1 Message Date
QuentinHsu
bac04587b8 🐞 fix(subscriptions): negative usage flow 2022-10-23 13:36:01 +08:00
17 changed files with 4304 additions and 4490 deletions

View File

@@ -21,7 +21,7 @@ jobs:
- name: Set up Node.js - name: Set up Node.js
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: "16" node-version: "14"
- name: Install dependencies - name: Install dependencies
run: | run: |
npm install -g pnpm npm install -g pnpm

View File

@@ -1,6 +1,6 @@
{ {
"name": "sub-store", "name": "sub-store",
"version": "2.14.3", "version": "2.13.5",
"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": {
@@ -30,7 +30,7 @@
"@babel/preset-env": "^7.18.0", "@babel/preset-env": "^7.18.0",
"@babel/register": "^7.17.7", "@babel/register": "^7.17.7",
"@types/gulp": "^4.0.9", "@types/gulp": "^4.0.9",
"axios": "^0.21.2", "axios": "^0.20.0",
"babel-plugin-relative-path-import": "^2.0.1", "babel-plugin-relative-path-import": "^2.0.1",
"babelify": "^10.0.0", "babelify": "^10.0.0",
"browser-pack-flat": "^3.4.2", "browser-pack-flat": "^3.4.2",
@@ -51,4 +51,4 @@
"prettier-plugin-sort-imports": "^1.6.1", "prettier-plugin-sort-imports": "^1.6.1",
"tinyify": "^3.0.0" "tinyify": "^3.0.0"
} }
} }

8544
backend/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,5 +9,3 @@ export const GIST_BACKUP_FILE_NAME = 'Sub-Store';
export const ARTIFACT_REPOSITORY_KEY = 'Sub-Store Artifacts Repository'; export const ARTIFACT_REPOSITORY_KEY = 'Sub-Store Artifacts Repository';
export const RESOURCE_CACHE_KEY = '#sub-store-cached-resource'; export const RESOURCE_CACHE_KEY = '#sub-store-cached-resource';
export const CACHE_EXPIRATION_TIME_MS = 60 * 60 * 1000; // 1 hour export const CACHE_EXPIRATION_TIME_MS = 60 * 60 * 1000; // 1 hour
export const SCRIPT_RESOURCE_CACHE_KEY = '#sub-store-cached-script-resource'; // cached-script-resource CSR
export const CSR_EXPIRATION_TIME_KEY = '#sub-store-csr-expiration-time'; // Custom expiration time key; (Loon|Surge) Default write 48 hour

View File

@@ -95,10 +95,22 @@ function FullConfig() {
return /^(\[server_local\]|\[Proxy\])/gm.test(raw); return /^(\[server_local\]|\[Proxy\])/gm.test(raw);
}; };
const parse = function (raw) { const parse = function (raw) {
const match = raw.match( const regex = /^\[server_local]|\[Proxy]/gm;
/^\[server_local|Proxy\]([\s\S]+?)^\[.+?\](\r?\n|$)/im, const match = regex.exec(raw);
)?.[1]; const results = [];
return match || raw;
let first = true;
if (match) {
raw = raw.substring(match.index);
for (const line of raw.split('\n')) {
if (!first && !line.test(/^\s*\[/)) {
results.push(line);
}
// skip the first line
first = false;
}
return results.join('\n');
}
}; };
return { name, test, parse }; return { name, test, parse };
} }

View File

@@ -1,5 +1,4 @@
import resourceCache from '@/utils/resource-cache'; import resourceCache from '@/utils/resource-cache';
import scriptResourceCache from '@/utils/script-resource-cache';
import { isIPv4, isIPv6 } from '@/utils'; import { isIPv4, isIPv6 } from '@/utils';
import { FULL } from '@/utils/logical'; import { FULL } from '@/utils/logical';
import { getFlag } from '@/utils/geo'; import { getFlag } from '@/utils/geo';
@@ -634,7 +633,6 @@ function createDynamicFunction(name, script, $arguments) {
'$httpClient', '$httpClient',
'$notification', '$notification',
'ProxyUtils', 'ProxyUtils',
'scriptResourceCache',
`${script}\n return ${name}`, `${script}\n return ${name}`,
)( )(
$arguments, $arguments,
@@ -647,7 +645,6 @@ function createDynamicFunction(name, script, $arguments) {
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
$notification, $notification,
ProxyUtils, ProxyUtils,
scriptResourceCache,
); );
} else { } else {
return new Function( return new Function(
@@ -655,8 +652,7 @@ function createDynamicFunction(name, script, $arguments) {
'$substore', '$substore',
'lodash', 'lodash',
'ProxyUtils', 'ProxyUtils',
'scriptResourceCache',
`${script}\n return ${name}`, `${script}\n return ${name}`,
)($arguments, $, lodash, ProxyUtils, scriptResourceCache); )($arguments, $, lodash, ProxyUtils);
} }
} }

View File

@@ -3,7 +3,6 @@ import Clash_Producer from './clash';
import Stash_Producer from './stash'; import Stash_Producer from './stash';
import Loon_Producer from './loon'; import Loon_Producer from './loon';
import URI_Producer from './uri'; import URI_Producer from './uri';
import V2Ray_Producer from './v2ray';
import QX_Producer from './qx'; import QX_Producer from './qx';
function JSON_Producer() { function JSON_Producer() {
@@ -18,7 +17,6 @@ export default {
Loon: Loon_Producer(), Loon: Loon_Producer(),
Clash: Clash_Producer(), Clash: Clash_Producer(),
URI: URI_Producer(), URI: URI_Producer(),
V2Ray: V2Ray_Producer(),
JSON: JSON_Producer(), JSON: JSON_Producer(),
Stash: Stash_Producer(), Stash: Stash_Producer(),
}; };

View File

@@ -55,10 +55,6 @@ function shadowsocks(proxy) {
// udp // udp
result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp'); result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
// test-url
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
return result.toString(); return result.toString();
} }
@@ -91,10 +87,6 @@ function trojan(proxy) {
// udp // udp
result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp'); result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
// test-url
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
return result.toString(); return result.toString();
} }
@@ -135,9 +127,6 @@ function vmess(proxy) {
// udp // udp
result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp'); result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
// test-url
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
return result.toString(); return result.toString();
} }
@@ -166,10 +155,6 @@ function http(proxy) {
// udp // udp
result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp'); result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
// test-url
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
return result.toString(); return result.toString();
} }
@@ -200,10 +185,6 @@ function socks5(proxy) {
// udp // udp
result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp'); result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
// test-url
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
return result.toString(); return result.toString();
} }
@@ -229,10 +210,6 @@ function snell(proxy) {
// udp // udp
result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp'); result.appendIfPresent(`,udp-relay=${proxy.udp}`, 'udp');
// test-url
result.appendIfPresent(`,test-url=${proxy['test-url']}`, 'test-url');
return result.toString(); return result.toString();
} }

View File

@@ -1,12 +0,0 @@
/* eslint-disable no-case-declarations */
import { Base64 } from 'js-base64';
import URI_Producer from './uri';
const URI = URI_Producer();
export default function V2Ray_Producer() {
const type = 'ALL';
const produce = (proxies) =>
Base64.encode(proxies.map((proxy) => URI.produce(proxy)).join('\n'));
return { type, produce };
}

View File

@@ -131,12 +131,7 @@ async function deleteArtifact(req, res) {
files[encodeURIComponent(artifact.name)] = { files[encodeURIComponent(artifact.name)] = {
content: '', content: '',
}; };
// 当别的Sub 删了同步订阅 或 gist里面删了 当前设备没有删除 时 无法删除的bug await syncToGist(files);
try {
await syncToGist(files);
} catch (i) {
$.error(`Function syncToGist: ${name} : ${i}`);
}
} }
// delete local cache // delete local cache
deleteByName(allArtifacts, name); deleteByName(allArtifacts, name);

View File

@@ -1,14 +1,13 @@
import { HTTP, ENV } from '@/vendor/open-api'; import { HTTP } from '@/vendor/open-api';
import { hex_md5 } from '@/vendor/md5'; import { hex_md5 } from '@/vendor/md5';
import resourceCache from '@/utils/resource-cache'; import resourceCache from '@/utils/resource-cache';
const tasks = new Map(); const tasks = new Map();
export default async function download(url, ua) { export default async function download(url, ua) {
const { isNode } = ENV();
ua = ua || 'Quantumult%20X/1.0.29 (iPhone14,5; iOS 15.4.1)'; ua = ua || 'Quantumult%20X/1.0.29 (iPhone14,5; iOS 15.4.1)';
const id = hex_md5(ua + url); const id = hex_md5(ua + url);
if (!isNode && tasks.has(id)) { if (tasks.has(id)) {
return tasks.get(id); return tasks.get(id);
} }
@@ -40,8 +39,6 @@ export default async function download(url, ua) {
} }
}); });
if (!isNode) { tasks.set(id, result);
tasks.set(id, result);
}
return result; return result;
} }

View File

@@ -40,10 +40,6 @@ export default class Gist {
} }
async upload(files) { async upload(files) {
if (Object.keys(files).length === 0) {
return Promise.reject('未提供需上传的文件');
}
const id = await this.locate(); const id = await this.locate();
if (id === -1) { if (id === -1) {

View File

@@ -16,13 +16,8 @@ class ResourceCache {
let clear = false; let clear = false;
Object.entries(this.resourceCache).forEach((entry) => { Object.entries(this.resourceCache).forEach((entry) => {
const [id, updated] = entry; const [id, updated] = entry;
if (!updated.time) { if (new Date().getTime() - updated > this.expires) {
// clear old version cache
delete this.resourceCache[id];
$.delete(`#${id}`); $.delete(`#${id}`);
clear = true;
}
if (new Date().getTime() - updated.time > this.expires) {
delete this.resourceCache[id]; delete this.resourceCache[id];
clear = true; clear = true;
} }
@@ -31,6 +26,9 @@ class ResourceCache {
} }
revokeAll() { revokeAll() {
Object.keys(this.resourceCache).forEach((id) => {
$.delete(`#${id}`);
});
this.resourceCache = {}; this.resourceCache = {};
this._persist(); this._persist();
} }
@@ -40,16 +38,17 @@ class ResourceCache {
} }
get(id) { get(id) {
const updated = this.resourceCache[id] && this.resourceCache[id].time; const updated = this.resourceCache[id];
if (updated && new Date().getTime() - updated <= this.expires) { if (updated && new Date().getTime() - updated <= this.expires) {
return this.resourceCache[id].data; return $.read(`#${id}`);
} }
return null; return null;
} }
set(id, value) { set(id, value) {
this.resourceCache[id] = { time: new Date().getTime(), data: value } this.resourceCache[id] = new Date().getTime();
this._persist(); this._persist();
$.write(value, `#${id}`);
} }
} }

View File

@@ -1,105 +0,0 @@
import $ from '@/core/app';
import {
SCRIPT_RESOURCE_CACHE_KEY,
CSR_EXPIRATION_TIME_KEY,
} from '@/constants';
class ResourceCache {
constructor() {
this.expires = getExpiredTime();
if (!$.read(SCRIPT_RESOURCE_CACHE_KEY)) {
$.write('{}', SCRIPT_RESOURCE_CACHE_KEY);
}
this.resourceCache = JSON.parse($.read(SCRIPT_RESOURCE_CACHE_KEY));
this._cleanup();
}
_cleanup() {
// clear obsolete cached resource
let clear = false;
Object.entries(this.resourceCache).forEach((entry) => {
const [id, updated] = entry;
if (!updated.time) {
// clear old version cache
delete this.resourceCache[id];
$.delete(`#${id}`);
clear = true;
}
if (new Date().getTime() - updated.time > this.expires) {
delete this.resourceCache[id];
clear = true;
}
});
if (clear) this._persist();
}
revokeAll() {
this.resourceCache = {};
this._persist();
}
_persist() {
$.write(JSON.stringify(this.resourceCache), SCRIPT_RESOURCE_CACHE_KEY);
}
get(id) {
const updated = this.resourceCache[id] && this.resourceCache[id].time;
if (updated && new Date().getTime() - updated <= this.expires) {
return this.resourceCache[id].data;
}
return null;
}
gettime(id) {
const updated = this.resourceCache[id] && this.resourceCache[id].time;
if (updated && new Date().getTime() - updated <= this.expires) {
return this.resourceCache[id].time;
}
return null;
}
set(id, value) {
this.resourceCache[id] = { time: new Date().getTime(), data: value };
this._persist();
}
}
function getExpiredTime() {
// console.log($.read(CSR_EXPIRATION_TIME_KEY));
if (!$.read(CSR_EXPIRATION_TIME_KEY)) {
$.write('1728e5', CSR_EXPIRATION_TIME_KEY); // 48 * 3600 * 1000
}
let expiration = 1728e5;
if ($.env.isLoon) {
const loont = {
// Loon 插件自义定
'1\u5206\u949f': 6e4,
'5\u5206\u949f': 3e5,
'10\u5206\u949f': 6e5,
'30\u5206\u949f': 18e5, // "30分钟"
'1\u5c0f\u65f6': 36e5,
'2\u5c0f\u65f6': 72e5,
'3\u5c0f\u65f6': 108e5,
'6\u5c0f\u65f6': 216e5,
'12\u5c0f\u65f6': 432e5,
'24\u5c0f\u65f6': 864e5,
'48\u5c0f\u65f6': 1728e5,
'72\u5c0f\u65f6': 2592e5, // "72小时"
'\u53c2\u6570\u4f20\u5165': 'readcachets', // "参数输入"
};
let intimed = $.read('#\u8282\u70b9\u7f13\u5b58\u6709\u6548\u671f'); // Loon #节点缓存有效期
// console.log(intimed);
if (intimed in loont) {
expiration = loont[intimed];
if (expiration === 'readcachets') {
expiration = intimed;
}
}
return expiration;
} else {
expiration = $.read(CSR_EXPIRATION_TIME_KEY);
return expiration;
}
}
export default new ResourceCache();

View File

@@ -4,16 +4,12 @@
#!author=Peng-YM #!author=Peng-YM
#!homepage=https://github.com/Peng-YM/Sub-Store #!homepage=https://github.com/Peng-YM/Sub-Store
#!icon=https://raw.githubusercontent.com/58xinian/icon/master/Sub-Store1.png #!icon=https://raw.githubusercontent.com/58xinian/icon/master/Sub-Store1.png
#!select = 节点缓存有效期,1分钟,5分钟,10分钟,30分钟,1小时,2小时,3小时,6小时,12小时,24小时,48小时,72小时,参数传入
[Rule]
DOMAIN,sub-store.vercel.app,PROXY
[MITM] [MITM]
hostname=sub.store hostname=sub.store
[Script] [Script]
http-request ^https?:\/\/sub\.store\/((download)|api\/(preview|sync|(utils\/node-info))) script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-1.min.js, requires-body=true, timeout=120, tag=Sub-Store Core http-request ^https?:\/\/sub\.store\/((download)|api\/(preview|sync|(utils\/node-info))) script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-1.min.js, requires-body=true, timeout=120, tag=Sub-Store Core
http-request ^https?:\/\/sub\.store script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js, requires-body=true, timeout=120, tag=Sub-Store Simple http-request https?:\/\/sub\.store script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js, requires-body=true, timeout=120, tag=Sub-Store Simple
cron "0 0 * * *" script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js, tag=Sub-Store Sync cron "0 0 * * *" script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js, tag=Sub-Store Sync

View File

@@ -1,12 +0,0 @@
#!name=Sub-Store
#!desc=高级订阅管理工具 @Peng-YM 无 ability 参数版本,不会爆内存, 如果需要使用指定节点功能 例如[加国旗脚本或者cname脚本] 可以用原版
[MITM]
hostname = %APPEND% sub.store
[Script]
# 主程序 已经去掉 Sub-Store Core 的参数 [,ability=http-client-policy] 不会爆内存,这个参数在 Surge 非常占用内存; 如果不需要使用指定节点功能 例如[加国旗脚本或者cname脚本] 则可以使用此脚本
Sub-Store Core=type=http-request,pattern=^https?:\/\/sub\.store\/((download)|api\/(preview|sync|(utils\/node-info))),script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-1.min.js,requires-body=true,timeout=120
Sub-Store Simple=type=http-request,pattern=^https?:\/\/sub\.store,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/sub-store-0.min.js,requires-body=true
Sub-Store Sync=type=cron,cronexp=0 0 * * *,wake-system=1,timeout=120,script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js

View File

@@ -17,13 +17,8 @@ class ResourceCache {
let clear = false; let clear = false;
Object.entries(this.resourceCache).forEach((entry) => { Object.entries(this.resourceCache).forEach((entry) => {
const [id, updated] = entry; const [id, updated] = entry;
if (!updated.time) { if (new Date().getTime() - updated > this.expires) {
// clear old version cache
delete this.resourceCache[id];
$.delete(`#${id}`); $.delete(`#${id}`);
clear = true;
}
if (new Date().getTime() - updated.time > this.expires) {
delete this.resourceCache[id]; delete this.resourceCache[id];
clear = true; clear = true;
} }
@@ -32,6 +27,9 @@ class ResourceCache {
} }
revokeAll() { revokeAll() {
Object.keys(this.resourceCache).forEach((id) => {
$.delete(`#${id}`);
});
this.resourceCache = {}; this.resourceCache = {};
this._persist(); this._persist();
} }
@@ -41,16 +39,17 @@ class ResourceCache {
} }
get(id) { get(id) {
const updated = this.resourceCache[id] && this.resourceCache[id].time; const updated = this.resourceCache[id];
if (updated && new Date().getTime() - updated <= this.expires) { if (updated && new Date().getTime() - updated <= this.expires) {
return this.resourceCache[id].data; return $.read(`#${id}`);
} }
return null; return null;
} }
set(id, value) { set(id, value) {
this.resourceCache[id] = { time: new Date().getTime(), data: value } this.resourceCache[id] = new Date().getTime();
this._persist(); this._persist();
$.write(value, `#${id}`);
} }
} }