mirror of
https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 00:52:40 +00:00
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
47849dc6d0 | ||
|
|
af06086c1b | ||
|
|
4a6a147667 | ||
|
|
c6540d14cd | ||
|
|
3db71ec531 | ||
|
|
cf156c2f17 | ||
|
|
e28e2a78fb | ||
|
|
b0a2c709e8 | ||
|
|
5dc2c8ced7 | ||
|
|
d2a65ee0fe | ||
|
|
4dd4ae98ca | ||
|
|
0d41eb467f | ||
|
|
ba1c91a7a5 | ||
|
|
30fa87c172 | ||
|
|
1eaa33948b | ||
|
|
619e256ed8 | ||
|
|
b46209e164 | ||
|
|
a1ba4e273e | ||
|
|
bfc95ed92a | ||
|
|
32f591ec56 | ||
|
|
cea16d8c44 | ||
|
|
93a1ba7b50 | ||
|
|
e6d1aa1150 | ||
|
|
26e83798da | ||
|
|
b083d2d840 | ||
|
|
cf35afcab2 | ||
|
|
d073dfeef8 | ||
|
|
f970ea3361 |
@@ -11,7 +11,7 @@ Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.
|
||||
</p>
|
||||
|
||||
[](https://github.com/sub-store-org/Sub-Store/actions/workflows/main.yml)     
|
||||
|
||||
<a href="https://trendshift.io/repositories/4572" target="_blank"><img src="https://trendshift.io/api/badge/repositories/4572" alt="sub-store-org%2FSub-Store | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
[](https://www.buymeacoffee.com/PengYM)
|
||||
|
||||
Core functionalities:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sub-store",
|
||||
"version": "2.14.307",
|
||||
"version": "2.14.321",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
@@ -17,6 +17,7 @@
|
||||
"author": "Peng-YM",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@maxmind/geoip2-node": "^5.0.0",
|
||||
"automerge": "1.0.1-preview.7",
|
||||
"body-parser": "^1.19.0",
|
||||
"connect-history-api-fallback": "^2.0.0",
|
||||
|
||||
63
backend/pnpm-lock.yaml
generated
63
backend/pnpm-lock.yaml
generated
@@ -5,6 +5,9 @@ settings:
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@maxmind/geoip2-node':
|
||||
specifier: ^5.0.0
|
||||
version: registry.npmmirror.com/@maxmind/geoip2-node@5.0.0
|
||||
automerge:
|
||||
specifier: 1.0.1-preview.7
|
||||
version: registry.npmmirror.com/automerge@1.0.1-preview.7
|
||||
@@ -1967,6 +1970,15 @@ packages:
|
||||
'@jridgewell/sourcemap-codec': registry.npmmirror.com/@jridgewell/sourcemap-codec@1.4.13
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/@maxmind/geoip2-node@5.0.0:
|
||||
resolution: {integrity: sha512-ki+q5//oU4tZ3BAhegZJcB5czoZyic5JSTEKbrUAQB/BzAoAiGyLW0immEmQvVVyy2SMlvBTJ3zqyRj8K9BbwQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@maxmind/geoip2-node/-/geoip2-node-5.0.0.tgz}
|
||||
name: '@maxmind/geoip2-node'
|
||||
version: 5.0.0
|
||||
dependencies:
|
||||
ip6addr: registry.npmmirror.com/ip6addr@0.2.5
|
||||
maxmind: registry.npmmirror.com/maxmind@4.3.19
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/@sindresorhus/is@0.14.0:
|
||||
resolution: {integrity: sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@sindresorhus/is/-/is-0.14.0.tgz}
|
||||
name: '@sindresorhus/is'
|
||||
@@ -5988,6 +6000,15 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/ip6addr@0.2.5:
|
||||
resolution: {integrity: sha512-9RGGSB6Zc9Ox5DpDGFnJdIeF0AsqXzdH+FspCfPPaU/L/4tI6P+5lIoFUFm9JXs9IrJv1boqAaNCQmoDADTSKQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/ip6addr/-/ip6addr-0.2.5.tgz}
|
||||
name: ip6addr
|
||||
version: 0.2.5
|
||||
dependencies:
|
||||
assert-plus: registry.npmmirror.com/assert-plus@1.0.0
|
||||
jsprim: registry.npmmirror.com/jsprim@2.0.2
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/ipaddr.js@1.9.1:
|
||||
resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz}
|
||||
name: ipaddr.js
|
||||
@@ -6556,6 +6577,12 @@ packages:
|
||||
version: 0.2.3
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/json-schema@0.4.0:
|
||||
resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/json-schema/-/json-schema-0.4.0.tgz}
|
||||
name: json-schema
|
||||
version: 0.4.0
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/json-stable-stringify-without-jsonify@1.0.1:
|
||||
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz}
|
||||
name: json-stable-stringify-without-jsonify
|
||||
@@ -6595,6 +6622,18 @@ packages:
|
||||
verror: registry.npmmirror.com/verror@1.10.0
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/jsprim@2.0.2:
|
||||
resolution: {integrity: sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/jsprim/-/jsprim-2.0.2.tgz}
|
||||
name: jsprim
|
||||
version: 2.0.2
|
||||
engines: {'0': node >=0.6.0}
|
||||
dependencies:
|
||||
assert-plus: registry.npmmirror.com/assert-plus@1.0.0
|
||||
extsprintf: registry.npmmirror.com/extsprintf@1.3.0
|
||||
json-schema: registry.npmmirror.com/json-schema@0.4.0
|
||||
verror: registry.npmmirror.com/verror@1.10.0
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/just-debounce@1.1.0:
|
||||
resolution: {integrity: sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/just-debounce/-/just-debounce-1.1.0.tgz}
|
||||
name: just-debounce
|
||||
@@ -6943,6 +6982,16 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/maxmind@4.3.19:
|
||||
resolution: {integrity: sha512-Bu/VEN7ZWAOCjifdZaXJQuN6/yO7+OK35pnJsqmz8sOndK3KQFvJoY+6HX09/MmLLqtCfa+sMK0iaQOaTejGNA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/maxmind/-/maxmind-4.3.19.tgz}
|
||||
name: maxmind
|
||||
version: 4.3.19
|
||||
engines: {node: '>=12', npm: '>=6'}
|
||||
dependencies:
|
||||
mmdb-lib: registry.npmmirror.com/mmdb-lib@2.1.0
|
||||
tiny-lru: registry.npmmirror.com/tiny-lru@11.2.6
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/md5.js@1.3.5:
|
||||
resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/md5.js/-/md5.js-1.3.5.tgz}
|
||||
name: md5.js
|
||||
@@ -7132,6 +7181,13 @@ packages:
|
||||
version: 0.5.3
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/mmdb-lib@2.1.0:
|
||||
resolution: {integrity: sha512-tdDTZmnI5G4UoSctv2KxM/3VQt2XRj4CmR5R4VsAWsOUcS3LysHR34wtixWm/pXxXdkBDuN92auxkC0T2+qd1Q==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/mmdb-lib/-/mmdb-lib-2.1.0.tgz}
|
||||
name: mmdb-lib
|
||||
version: 2.1.0
|
||||
engines: {node: '>=10', npm: '>=6'}
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/mocha@10.0.0:
|
||||
resolution: {integrity: sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/mocha/-/mocha-10.0.0.tgz}
|
||||
name: mocha
|
||||
@@ -9424,6 +9480,13 @@ packages:
|
||||
process: registry.npmmirror.com/process@0.11.10
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/tiny-lru@11.2.6:
|
||||
resolution: {integrity: sha512-0PU3c9PjMnltZaFo2sGYv/nnJsMjG0Cxx8X6FXHPPGjFyoo1SJDxvUXW1207rdiSxYizf31roo+GrkIByQeZoA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tiny-lru/-/tiny-lru-11.2.6.tgz}
|
||||
name: tiny-lru
|
||||
version: 11.2.6
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/tinyify@3.0.0:
|
||||
resolution: {integrity: sha512-RtjVjC1xwwxt8AMVfxEmo+FzRJB6p5sAOtFaJj8vMrkWShtArsM4dLVRWhx2Vc07Me3NWgmP7pi9UPm/a2XNNA==, registry: http://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tinyify/-/tinyify-3.0.0.tgz}
|
||||
name: tinyify
|
||||
|
||||
@@ -15,7 +15,7 @@ import $ from '@/core/app';
|
||||
import { FILES_KEY, MODULES_KEY } from '@/constants';
|
||||
import { findByName } from '@/utils/database';
|
||||
import { produceArtifact } from '@/restful/sync';
|
||||
import { getFlag, getISO } from '@/utils/geo';
|
||||
import { getFlag, getISO, MMDB } from '@/utils/geo';
|
||||
import Gist from '@/utils/gist';
|
||||
|
||||
function preprocess(raw) {
|
||||
@@ -199,6 +199,10 @@ function produce(proxies, targetPlatform, type, opts = {}) {
|
||||
);
|
||||
|
||||
proxies = proxies.map((proxy) => {
|
||||
proxy._subName = proxy.subName;
|
||||
proxy._collectionName = proxy.collectionName;
|
||||
proxy._resolved = proxy.resolved;
|
||||
|
||||
if (!isNotBlank(proxy.name)) {
|
||||
proxy.name = `${proxy.type} ${proxy.server}:${proxy.port}`;
|
||||
}
|
||||
@@ -273,6 +277,7 @@ export const ProxyUtils = {
|
||||
yaml: YAML,
|
||||
getFlag,
|
||||
getISO,
|
||||
MMDB,
|
||||
Gist,
|
||||
};
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import { Base64 } from 'js-base64';
|
||||
// Parse SS URI format (only supports new SIP002, legacy format is depreciated).
|
||||
// reference: https://github.com/shadowsocks/shadowsocks-org/wiki/SIP002-URI-Scheme
|
||||
function URI_SS() {
|
||||
// TODO: 暂不支持 httpupgrade
|
||||
const name = 'URI SS Parser';
|
||||
const test = (line) => {
|
||||
return /^ss:\/\//.test(line);
|
||||
@@ -299,6 +300,7 @@ function URI_VMess() {
|
||||
if (proxy.tls && proxy.sni) {
|
||||
proxy.sni = params.sni;
|
||||
}
|
||||
let httpupgrade = false;
|
||||
// handle obfs
|
||||
if (params.net === 'ws' || params.obfs === 'websocket') {
|
||||
proxy.network = 'ws';
|
||||
@@ -309,6 +311,12 @@ function URI_VMess() {
|
||||
proxy.network = 'http';
|
||||
} else if (['grpc'].includes(params.net)) {
|
||||
proxy.network = 'grpc';
|
||||
} else if (
|
||||
params.net === 'httpupgrade' ||
|
||||
proxy.network === 'httpupgrade'
|
||||
) {
|
||||
proxy.network = 'ws';
|
||||
httpupgrade = true;
|
||||
}
|
||||
if (proxy.network) {
|
||||
let transportHost = params.host ?? params.obfsParam;
|
||||
@@ -341,10 +349,15 @@ function URI_VMess() {
|
||||
'_grpc-type': getIfNotBlank(params.type),
|
||||
};
|
||||
} else {
|
||||
proxy[`${proxy.network}-opts`] = {
|
||||
const opts = {
|
||||
path: getIfNotBlank(transportPath),
|
||||
headers: { Host: getIfNotBlank(transportHost) },
|
||||
};
|
||||
if (httpupgrade) {
|
||||
opts['v2ray-http-upgrade'] = true;
|
||||
opts['v2ray-http-upgrade-fast-open'] = true;
|
||||
}
|
||||
proxy[`${proxy.network}-opts`] = opts;
|
||||
}
|
||||
} else {
|
||||
delete proxy.network;
|
||||
@@ -444,10 +457,13 @@ function URI_VLESS() {
|
||||
proxy[`${params.security}-opts`] = opts;
|
||||
}
|
||||
}
|
||||
|
||||
let httpupgrade = false;
|
||||
proxy.network = params.type;
|
||||
if (proxy.network === 'tcp' && params.headerType === 'http') {
|
||||
proxy.network = 'http';
|
||||
} else if (proxy.network === 'httpupgrade') {
|
||||
proxy.network = 'ws';
|
||||
httpupgrade = true;
|
||||
}
|
||||
if (!proxy.network && isShadowrocket && params.obfs) {
|
||||
proxy.network = params.obfs;
|
||||
@@ -485,6 +501,10 @@ function URI_VLESS() {
|
||||
if (['grpc'].includes(proxy.network)) {
|
||||
opts['_grpc-type'] = params.mode || 'gun';
|
||||
}
|
||||
if (httpupgrade) {
|
||||
opts['v2ray-http-upgrade'] = true;
|
||||
opts['v2ray-http-upgrade-fast-open'] = true;
|
||||
}
|
||||
if (Object.keys(opts).length > 0) {
|
||||
proxy[`${proxy.network}-opts`] = opts;
|
||||
}
|
||||
@@ -831,6 +851,9 @@ function Clash_All() {
|
||||
}
|
||||
}
|
||||
|
||||
if (proxy['server-cert-fingerprint']) {
|
||||
proxy['tls-fingerprint'] = proxy['server-cert-fingerprint'];
|
||||
}
|
||||
if (proxy.fingerprint) {
|
||||
proxy['tls-fingerprint'] = proxy.fingerprint;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,12 @@ params = "?" head:param tail:("&"@param)* {
|
||||
}
|
||||
|
||||
if (params["type"]) {
|
||||
let httpupgrade
|
||||
proxy.network = params["type"]
|
||||
if(proxy.network === 'httpupgrade') {
|
||||
proxy.network = 'ws'
|
||||
httpupgrade = true
|
||||
}
|
||||
if (['grpc'].includes(proxy.network)) {
|
||||
proxy[proxy.network + '-opts'] = {
|
||||
'grpc-service-name': params["serviceName"],
|
||||
@@ -102,6 +107,10 @@ params = "?" head:param tail:("&"@param)* {
|
||||
if (params["host"]) {
|
||||
$set(proxy, proxy.network+"-opts.headers.Host", decodeURIComponent(params["host"]));
|
||||
}
|
||||
if (httpupgrade) {
|
||||
$set(proxy, proxy.network+"-opts.v2ray-http-upgrade", true);
|
||||
$set(proxy, proxy.network+"-opts.v2ray-http-upgrade-fast-open", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,12 @@ params = "?" head:param tail:("&"@param)* {
|
||||
}
|
||||
|
||||
if (params["type"]) {
|
||||
let httpupgrade
|
||||
proxy.network = params["type"]
|
||||
if(proxy.network === 'httpupgrade') {
|
||||
proxy.network = 'ws'
|
||||
httpupgrade = true
|
||||
}
|
||||
if (['grpc'].includes(proxy.network)) {
|
||||
proxy[proxy.network + '-opts'] = {
|
||||
'grpc-service-name': params["serviceName"],
|
||||
@@ -100,6 +105,10 @@ params = "?" head:param tail:("&"@param)* {
|
||||
if (params["host"]) {
|
||||
$set(proxy, proxy.network+"-opts.headers.Host", decodeURIComponent(params["host"]));
|
||||
}
|
||||
if (httpupgrade) {
|
||||
$set(proxy, proxy.network+"-opts.v2ray-http-upgrade", true);
|
||||
$set(proxy, proxy.network+"-opts.v2ray-http-upgrade-fast-open", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ function Base64Encoded() {
|
||||
'aHR0c', // htt
|
||||
'dmxlc3M=', // vless
|
||||
'aHlzdGVyaWEy', // hysteria2
|
||||
'aHkyOi8v', // hy2://
|
||||
'd2lyZWd1YXJkOi8v', // wireguard://
|
||||
'd2c6Ly8=', // wg://
|
||||
'dHVpYzovLw==', // tuic://
|
||||
|
||||
@@ -462,7 +462,7 @@ const DOMAIN_RESOLVERS = {
|
||||
const cached = resourceCache.get(id);
|
||||
if (!noCache && cached) return cached;
|
||||
const resp = await $.http.get({
|
||||
url: `http://223.6.6.6/resolve?name=${encodeURIComponent(
|
||||
url: `http://223.6.6.6/resolve?edns_client_subnet=223.6.6.6/24&name=${encodeURIComponent(
|
||||
domain,
|
||||
)}&type=${type === 'IPv6' ? 'AAAA' : 'A'}&short=1`,
|
||||
headers: {
|
||||
@@ -482,7 +482,7 @@ const DOMAIN_RESOLVERS = {
|
||||
const cached = resourceCache.get(id);
|
||||
if (!noCache && cached) return cached;
|
||||
const resp = await $.http.get({
|
||||
url: `http://119.28.28.28/d?type=${
|
||||
url: `http://119.28.28.28/d?ip=119.28.28.28&type=${
|
||||
type === 'IPv6' ? 'AAAA' : 'A'
|
||||
}&dn=${encodeURIComponent(domain)}`,
|
||||
headers: {
|
||||
|
||||
@@ -239,7 +239,7 @@ export default function Stash_Producer() {
|
||||
delete proxy.tls;
|
||||
}
|
||||
if (proxy['tls-fingerprint']) {
|
||||
proxy.fingerprint = proxy['tls-fingerprint'];
|
||||
proxy['server-cert-fingerprint'] = proxy['tls-fingerprint'];
|
||||
}
|
||||
delete proxy['tls-fingerprint'];
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ const targetPlatform = 'Surfboard';
|
||||
|
||||
export default function Surfboard_Producer() {
|
||||
const produce = (proxy) => {
|
||||
proxy.name = proxy.name.replace(/=/g, '');
|
||||
proxy.name = proxy.name.replace(/=|,/g, '');
|
||||
switch (proxy.type) {
|
||||
case 'ss':
|
||||
return shadowsocks(proxy);
|
||||
|
||||
@@ -14,6 +14,7 @@ const ipVersions = {
|
||||
|
||||
export default function Surge_Producer() {
|
||||
const produce = (proxy, type, opts = {}) => {
|
||||
proxy.name = proxy.name.replace(/=|,/g, '');
|
||||
switch (proxy.type) {
|
||||
case 'ss':
|
||||
return shadowsocks(proxy);
|
||||
|
||||
@@ -84,6 +84,11 @@ export default function URI_Producer() {
|
||||
if (proxy.network === 'http') {
|
||||
net = 'tcp';
|
||||
type = 'http';
|
||||
} else if (
|
||||
proxy.network === 'ws' &&
|
||||
proxy['ws-opts']?.['v2ray-http-upgrade']
|
||||
) {
|
||||
net = 'httpupgrade';
|
||||
}
|
||||
result = {
|
||||
v: '2',
|
||||
@@ -172,9 +177,15 @@ export default function URI_Producer() {
|
||||
if (proxy.flow) {
|
||||
flow = `&flow=${encodeURIComponent(proxy.flow)}`;
|
||||
}
|
||||
let vlessTransport = `&type=${encodeURIComponent(
|
||||
proxy.network,
|
||||
)}`;
|
||||
let vlessType = proxy.network;
|
||||
if (
|
||||
proxy.network === 'ws' &&
|
||||
proxy['ws-opts']?.['v2ray-http-upgrade']
|
||||
) {
|
||||
vlessType = 'httpupgrade';
|
||||
}
|
||||
|
||||
let vlessTransport = `&type=${encodeURIComponent(vlessType)}`;
|
||||
if (['grpc'].includes(proxy.network)) {
|
||||
// https://github.com/XTLS/Xray-core/issues/91
|
||||
vlessTransport += `&mode=${encodeURIComponent(
|
||||
@@ -220,7 +231,14 @@ export default function URI_Producer() {
|
||||
case 'trojan':
|
||||
let trojanTransport = '';
|
||||
if (proxy.network) {
|
||||
trojanTransport = `&type=${proxy.network}`;
|
||||
let trojanType = proxy.network;
|
||||
if (
|
||||
proxy.network === 'ws' &&
|
||||
proxy['ws-opts']?.['v2ray-http-upgrade']
|
||||
) {
|
||||
trojanType = 'httpupgrade';
|
||||
}
|
||||
trojanTransport = `&type=${encodeURIComponent(trojanType)}`;
|
||||
if (['grpc'].includes(proxy.network)) {
|
||||
let trojanTransportServiceName =
|
||||
proxy[`${proxy.network}-opts`]?.[
|
||||
|
||||
@@ -11,17 +11,65 @@ import { syncToGist } from '@/restful/artifacts';
|
||||
import { findByName } from '@/utils/database';
|
||||
|
||||
!(async function () {
|
||||
const settings = $.read(SETTINGS_KEY);
|
||||
// if GitHub token is not configured
|
||||
if (!settings.githubUser || !settings.gistToken) return;
|
||||
let arg;
|
||||
if (typeof $argument != 'undefined') {
|
||||
arg = Object.fromEntries(
|
||||
// eslint-disable-next-line no-undef
|
||||
$argument.split('&').map((item) => item.split('=')),
|
||||
);
|
||||
} else {
|
||||
arg = {};
|
||||
}
|
||||
let sub_names = (arg?.subscription ?? arg?.sub ?? '')
|
||||
.split(/,|,/g)
|
||||
.map((i) => i.trim())
|
||||
.filter((i) => i.length > 0)
|
||||
.map((i) => decodeURIComponent(i));
|
||||
let col_names = (arg?.collection ?? arg?.col ?? '')
|
||||
.split(/,|,/g)
|
||||
.map((i) => i.trim())
|
||||
.filter((i) => i.length > 0)
|
||||
.map((i) => decodeURIComponent(i));
|
||||
if (sub_names.length > 0 || col_names.length > 0) {
|
||||
if (sub_names.length > 0)
|
||||
await produceArtifacts(sub_names, 'subscription');
|
||||
if (col_names.length > 0)
|
||||
await produceArtifacts(col_names, 'collection');
|
||||
} else {
|
||||
const settings = $.read(SETTINGS_KEY);
|
||||
// if GitHub token is not configured
|
||||
if (!settings.githubUser || !settings.gistToken) return;
|
||||
|
||||
const artifacts = $.read(ARTIFACTS_KEY);
|
||||
if (!artifacts || artifacts.length === 0) return;
|
||||
const artifacts = $.read(ARTIFACTS_KEY);
|
||||
if (!artifacts || artifacts.length === 0) return;
|
||||
|
||||
const shouldSync = artifacts.some((artifact) => artifact.sync);
|
||||
if (shouldSync) await doSync();
|
||||
const shouldSync = artifacts.some((artifact) => artifact.sync);
|
||||
if (shouldSync) await doSync();
|
||||
}
|
||||
})().finally(() => $.done());
|
||||
|
||||
async function produceArtifacts(names, type) {
|
||||
try {
|
||||
if (names.length > 0) {
|
||||
$.info(`produceArtifacts ${type} 开始: ${names.join(', ')}`);
|
||||
await Promise.all(
|
||||
names.map(async (name) => {
|
||||
try {
|
||||
await produceArtifact({
|
||||
type,
|
||||
name,
|
||||
});
|
||||
} catch (e) {
|
||||
$.error(`${type} ${name} error: ${e.message ?? e}`);
|
||||
}
|
||||
}),
|
||||
);
|
||||
$.info(`produceArtifacts ${type} 完成: ${names.join(', ')}`);
|
||||
}
|
||||
} catch (e) {
|
||||
$.error(`produceArtifacts error: ${e.message ?? e}`);
|
||||
}
|
||||
}
|
||||
async function doSync() {
|
||||
console.log(
|
||||
`
|
||||
|
||||
@@ -55,7 +55,11 @@ async function downloadSubscription(req, res) {
|
||||
const platform =
|
||||
req.query.target || getPlatformFromHeaders(req.headers) || 'JSON';
|
||||
|
||||
$.info(`正在下载订阅:${name}`);
|
||||
$.info(
|
||||
`正在下载订阅:${name}\n请求 User-Agent: ${
|
||||
req.headers['user-agent'] || req.headers['User-Agent']
|
||||
}`,
|
||||
);
|
||||
let {
|
||||
url,
|
||||
ua,
|
||||
@@ -228,7 +232,11 @@ async function downloadCollection(req, res) {
|
||||
const allCols = $.read(COLLECTIONS_KEY);
|
||||
const collection = findByName(allCols, name);
|
||||
|
||||
$.info(`正在下载组合订阅:${name}`);
|
||||
$.info(
|
||||
`正在下载组合订阅:${name}\n请求 User-Agent: ${
|
||||
req.headers['user-agent'] || req.headers['User-Agent']
|
||||
}`,
|
||||
);
|
||||
|
||||
let {
|
||||
ignoreFailedRemoteSub,
|
||||
|
||||
@@ -14,7 +14,13 @@ import $ from '@/core/app';
|
||||
|
||||
const tasks = new Map();
|
||||
|
||||
export default async function download(rawUrl, ua, timeout, proxy) {
|
||||
export default async function download(
|
||||
rawUrl,
|
||||
ua,
|
||||
timeout,
|
||||
proxy,
|
||||
skipCustomCache,
|
||||
) {
|
||||
let $arguments = {};
|
||||
let url = rawUrl.replace(/#noFlow$/, '');
|
||||
const rawArgs = url.split('#');
|
||||
@@ -39,6 +45,23 @@ export default async function download(rawUrl, ua, timeout, proxy) {
|
||||
? `#sub-store-cached-custom-${$arguments?.cacheKey}`
|
||||
: undefined;
|
||||
|
||||
if (customCacheKey && !skipCustomCache) {
|
||||
const cached = $.read(customCacheKey);
|
||||
if (cached) {
|
||||
$.info(
|
||||
`乐观缓存: URL ${url}\n本次返回自定义缓存 ${$arguments?.cacheKey}\n并进行请求 尝试更新缓存`,
|
||||
);
|
||||
download(
|
||||
rawUrl.replace(/(\?|&)cacheKey=.*?(&|$)/, ''),
|
||||
ua,
|
||||
timeout,
|
||||
proxy,
|
||||
true,
|
||||
);
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
|
||||
// const downloadUrlMatch = url.match(/^\/api\/(file|module)\/(.+)/);
|
||||
// if (downloadUrlMatch) {
|
||||
// let type = downloadUrlMatch?.[1];
|
||||
@@ -81,7 +104,7 @@ export default async function download(rawUrl, ua, timeout, proxy) {
|
||||
|
||||
// try to find in app cache
|
||||
const cached = resourceCache.get(id);
|
||||
if (!$arguments?.noCache && cached) {
|
||||
if (!$arguments?.noCache && cached && !skipCustomCache) {
|
||||
$.info(`使用缓存: ${url}`);
|
||||
result = cached;
|
||||
} else {
|
||||
@@ -120,6 +143,9 @@ export default async function download(rawUrl, ua, timeout, proxy) {
|
||||
if (shouldCache) {
|
||||
resourceCache.set(id, body);
|
||||
if (customCacheKey) {
|
||||
$.info(
|
||||
`URL ${url}\n写入自定义缓存 ${$arguments?.cacheKey}`,
|
||||
);
|
||||
$.write(body, customCacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ const {
|
||||
isShadowRocket,
|
||||
isLanceX,
|
||||
isEgern,
|
||||
isGUIforCores,
|
||||
} = ENV();
|
||||
let backend = 'Node';
|
||||
if (isNode) backend = 'Node';
|
||||
@@ -20,6 +21,7 @@ if (isStash) backend = 'Stash';
|
||||
if (isShadowRocket) backend = 'ShadowRocket';
|
||||
if (isEgern) backend = 'Egern';
|
||||
if (isLanceX) backend = 'LanceX';
|
||||
if (isGUIforCores) backend = 'GUI.for.Cores';
|
||||
|
||||
let meta = {};
|
||||
|
||||
@@ -36,6 +38,10 @@ try {
|
||||
// eslint-disable-next-line no-undef
|
||||
meta.script = $script;
|
||||
}
|
||||
if (typeof $Plugin !== 'undefined') {
|
||||
// eslint-disable-next-line no-undef
|
||||
meta.plugin = $Plugin;
|
||||
}
|
||||
if (isNode) {
|
||||
meta.node = {
|
||||
version: eval('process.version'),
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import $ from '@/core/app';
|
||||
|
||||
const ISOFlags = {
|
||||
'🏳️🌈': ['EXP', 'BAND'],
|
||||
'🇸🇱': ['TEST', 'SOS'],
|
||||
@@ -427,3 +429,41 @@ export function getFlag(name) {
|
||||
export function getISO(name) {
|
||||
return ISOFlags[getFlag(name)]?.[0];
|
||||
}
|
||||
|
||||
export class MMDB {
|
||||
constructor({ country, asn } = {}) {
|
||||
if ($.env.isNode) {
|
||||
const Reader = eval(`require("@maxmind/geoip2-node")`).Reader;
|
||||
const fs = eval("require('fs')");
|
||||
const countryFile =
|
||||
country || eval('process.env.SUB_STORE_MMDB_COUNTRY_PATH');
|
||||
const asnFile = asn || eval('process.env.SUB_STORE_MMDB_ASN_PATH');
|
||||
// $.info(
|
||||
// `GeoLite2 Country MMDB: ${countryFile}, exists: ${fs.existsSync(
|
||||
// countryFile,
|
||||
// )}`,
|
||||
// );
|
||||
if (countryFile) {
|
||||
this.countryReader = Reader.openBuffer(
|
||||
fs.readFileSync(countryFile),
|
||||
);
|
||||
}
|
||||
// $.info(
|
||||
// `GeoLite2 ASN MMDB: ${asnFile}, exists: ${fs.existsSync(
|
||||
// asnFile,
|
||||
// )}`,
|
||||
// );
|
||||
if (asnFile) {
|
||||
if (!fs.existsSync(asnFile))
|
||||
throw new Error('GeoLite2 ASN MMDB does not exist');
|
||||
this.asnReader = Reader.openBuffer(fs.readFileSync(asnFile));
|
||||
}
|
||||
}
|
||||
}
|
||||
geoip(ip) {
|
||||
return this.countryReader?.country(ip)?.country?.isoCode;
|
||||
}
|
||||
ipaso(ip) {
|
||||
return this.asnReader?.asn(ip)?.autonomousSystemOrganization;
|
||||
}
|
||||
}
|
||||
|
||||
4
backend/src/vendor/express.js
vendored
4
backend/src/vendor/express.js
vendored
@@ -161,7 +161,7 @@ export default function express({ substore: $, port, host }) {
|
||||
|
||||
function Response() {
|
||||
let statusCode = 200;
|
||||
const { isQX, isLoon, isSurge } = ENV();
|
||||
const { isQX, isLoon, isSurge, isGUIforCores } = ENV();
|
||||
const headers = DEFAULT_HEADERS;
|
||||
const STATUS_CODE_MAP = {
|
||||
200: 'HTTP/1.1 200 OK',
|
||||
@@ -184,7 +184,7 @@ export default function express({ substore: $, port, host }) {
|
||||
body,
|
||||
headers,
|
||||
};
|
||||
if (isQX) {
|
||||
if (isQX || isGUIforCores) {
|
||||
$done(response);
|
||||
} else if (isLoon || isSurge) {
|
||||
$done({
|
||||
|
||||
42
backend/src/vendor/open-api.js
vendored
42
backend/src/vendor/open-api.js
vendored
@@ -8,6 +8,7 @@ const isStash =
|
||||
const isShadowRocket = 'undefined' !== typeof $rocket;
|
||||
const isEgern = 'object' == typeof egern;
|
||||
const isLanceX = 'undefined' != typeof $native;
|
||||
const isGUIforCores = typeof $Plugins !== 'undefined';
|
||||
|
||||
export class OpenAPI {
|
||||
constructor(name = 'untitled', debug = false) {
|
||||
@@ -48,7 +49,10 @@ export class OpenAPI {
|
||||
this.cache = JSON.parse($prefs.valueForKey(this.name) || '{}');
|
||||
if (isLoon || isSurge)
|
||||
this.cache = JSON.parse($persistentStore.read(this.name) || '{}');
|
||||
|
||||
if (isGUIforCores)
|
||||
this.cache = JSON.parse(
|
||||
$Plugins.SubStoreCache.get(this.name) || '{}',
|
||||
);
|
||||
if (isNode) {
|
||||
// create a json for root cache
|
||||
const basePath =
|
||||
@@ -86,6 +90,7 @@ export class OpenAPI {
|
||||
const data = JSON.stringify(this.cache, null, 2);
|
||||
if (isQX) $prefs.setValueForKey(data, this.name);
|
||||
if (isLoon || isSurge) $persistentStore.write(data, this.name);
|
||||
if (isGUIforCores) $Plugins.SubStoreCache.set(this.name, data);
|
||||
if (isNode) {
|
||||
const basePath =
|
||||
eval('process.env.SUB_STORE_DATA_BASE_PATH') || '.';
|
||||
@@ -118,6 +123,9 @@ export class OpenAPI {
|
||||
if (isNode) {
|
||||
this.root[key] = data;
|
||||
}
|
||||
if (isGUIforCores) {
|
||||
return $Plugins.SubStoreCache.set(key, data);
|
||||
}
|
||||
} else {
|
||||
this.cache[key] = data;
|
||||
}
|
||||
@@ -137,6 +145,9 @@ export class OpenAPI {
|
||||
if (isNode) {
|
||||
return this.root[key];
|
||||
}
|
||||
if (isGUIforCores) {
|
||||
return $Plugins.SubStoreCache.get(key);
|
||||
}
|
||||
} else {
|
||||
return this.cache[key];
|
||||
}
|
||||
@@ -155,6 +166,9 @@ export class OpenAPI {
|
||||
if (isNode) {
|
||||
delete this.root[key];
|
||||
}
|
||||
if (isGUIforCores) {
|
||||
return $Plugins.SubStoreCache.remove(key);
|
||||
}
|
||||
} else {
|
||||
delete this.cache[key];
|
||||
}
|
||||
@@ -220,6 +234,9 @@ export class OpenAPI {
|
||||
});
|
||||
}
|
||||
}
|
||||
if (isGUIforCores) {
|
||||
$Plugins.Notify(title, subtitle + '\n' + content);
|
||||
}
|
||||
}
|
||||
|
||||
// other helper functions
|
||||
@@ -240,7 +257,7 @@ export class OpenAPI {
|
||||
}
|
||||
|
||||
done(value = {}) {
|
||||
if (isQX || isLoon || isSurge) {
|
||||
if (isQX || isLoon || isSurge || isGUIforCores) {
|
||||
$done(value);
|
||||
} else if (isNode) {
|
||||
if (typeof $context !== 'undefined') {
|
||||
@@ -262,11 +279,12 @@ export function ENV() {
|
||||
isShadowRocket,
|
||||
isEgern,
|
||||
isLanceX,
|
||||
isGUIforCores,
|
||||
};
|
||||
}
|
||||
|
||||
export function HTTP(defaultOptions = { baseURL: '' }) {
|
||||
const { isQX, isLoon, isSurge, isNode } = ENV();
|
||||
const { isQX, isLoon, isSurge, isNode, isGUIforCores } = ENV();
|
||||
const methods = [
|
||||
'GET',
|
||||
'POST',
|
||||
@@ -356,6 +374,24 @@ export function HTTP(defaultOptions = { baseURL: '' }) {
|
||||
});
|
||||
});
|
||||
});
|
||||
} else if (isGUIforCores) {
|
||||
worker = new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const response = await $Plugins.Requests({
|
||||
method,
|
||||
url: options.url,
|
||||
headers: options.headers,
|
||||
body: options.body,
|
||||
});
|
||||
resolve({
|
||||
statusCode: response.status,
|
||||
headers: response.headers,
|
||||
body: response.body,
|
||||
});
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let timeoutid;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!name=Sub-Store
|
||||
#!desc=高级订阅管理工具. 定时任务默认为每天 23 点 55 分
|
||||
#!desc=高级订阅管理工具. 定时任务默认为每天 23 点 55 分. 定时任务指定时将订阅/文件上传到私有 Gist. 在前端, 叫做 '同步' 或 '同步配置'
|
||||
#!openUrl=https://sub.store
|
||||
#!author=Peng-YM
|
||||
#!homepage=https://github.com/sub-store-org/Sub-Store
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Sub-Store",
|
||||
"description": "定时任务默认为每天 23 点 55 分",
|
||||
"description": "定时任务默认为每天 23 点 55 分. 定时任务指定时将订阅/文件上传到私有 Gist. 在前端, 叫做 '同步' 或 '同步配置'",
|
||||
"task": [
|
||||
"55 23 * * * https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js, tag=Sub-Store Sync, img-url=https://raw.githubusercontent.com/58xinian/icon/master/Sub-Store1.png"
|
||||
]
|
||||
|
||||
@@ -17,6 +17,8 @@ Telegram 频道: [`https://t.me/cool_scripts` ](https://t.me/cool_scripts)
|
||||
|
||||
1. 官方默认版模块(支持 App 内使用编辑参数): [`https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge.sgmodule`](https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge.sgmodule)
|
||||
|
||||
> 最新版 Surge 已删除 `ability: http-client-policy` 参数, 模块暂不做修改, 对测落地功能无影响
|
||||
|
||||
2. 经典版, 不支持编辑参数, 固定带 ability 参数版本, 使用 jsc 引擎时, 可能会爆内存, 如果需要使用指定节点功能 例如[加旗帜脚本或者cname脚本] 请使用此带 ability 参数版本: [`https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-ability.sgmodule`](https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-ability.sgmodule)
|
||||
|
||||
3. 经典版, 不支持编辑参数, 固定不带 ability 参数版本: [`https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-Noability.sgmodule`](https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-Noability.sgmodule)
|
||||
@@ -31,7 +33,7 @@ Telegram 频道: [`https://t.me/cool_scripts` ](https://t.me/cool_scripts)
|
||||
安装使用 覆写 [`https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Stash.stoverride`](https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Stash.stoverride) 即可。
|
||||
|
||||
### 5. Shadowrocket
|
||||
安装使用 模块 [`https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge.sgmodule`](https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge.sgmodule) 即可。
|
||||
安装使用 模块 [`https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-Noability.sgmodule`](https://raw.githubusercontent.com/sub-store-org/Sub-Store/master/config/Surge-Noability.sgmodule) 即可。
|
||||
|
||||
## 使用 Sub-Store
|
||||
1. 使用 Safari 打开这个 https://sub.store 如网页正常打开并且未弹出任何错误提示,说明 Sub-Store 已经配置成功。
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: Sub-Store
|
||||
desc: 高级订阅管理工具 @Peng-YM. 定时任务默认为每天 23 点 55 分
|
||||
desc: 高级订阅管理工具 @Peng-YM. 定时任务默认为每天 23 点 55 分. 定时任务指定时将订阅/文件上传到私有 Gist. 在前端, 叫做 '同步' 或 '同步配置'
|
||||
icon: https://raw.githubusercontent.com/cc63/ICON/main/Sub-Store.png
|
||||
|
||||
http:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!name=Sub-Store(β)
|
||||
#!desc=支持 Surge 正式版的参数设置功能. 测落地功能 ability: http-client-policy, 同步配置的定时 cronexp: 55 23 * * *
|
||||
#!category=订阅管理
|
||||
#!arguments=ability:http-client-policy,cronexp:55 23 * * *,sync:"Sub-Store Sync",timeout:120,engine:auto
|
||||
#!arguments-desc=\n1️⃣ ability\n\n默认已开启测落地能力\n需要配合脚本操作\n如 https://raw.githubusercontent.com/Keywos/rule/main/cname.js\n填写任意其他值关闭\n\n2️⃣ cronexp\n\n同步配置定时任务\n默认为每天 23 点 55 分\n\n3️⃣ sync\n\n自定义定时任务名\n便于在脚本编辑器中选择\n若设为 # 可取消定时任务\n\n4️⃣ timeout\n\n超时, 单位为秒\n\n5️⃣ engine\n\n默认为自动使用 webview 引擎, 可设为指定 jsc, 但 jsc 容易爆内存
|
||||
#!arguments=ability:http-client-policy,cronexp:55 23 * * *,sync:"Sub-Store Sync",timeout:120,engine:auto,produce:"# Sub-Store Produce",produce_cronexp:50 */6 * * *,produce_sub:"sub1,sub2",produce_col:"col1,col2"
|
||||
#!arguments-desc=\n1️⃣ ability\n\n默认已开启测落地能力\n需要配合脚本操作\n如 https://raw.githubusercontent.com/Keywos/rule/main/cname.js\n填写任意其他值关闭\n\n2️⃣ cronexp\n\n同步配置定时任务\n默认为每天 23 点 55 分\n\n定时任务指定时将订阅/文件上传到私有 Gist. 在前端, 叫做 '同步' 或 '同步配置'\n\n3️⃣ sync\n\n自定义定时任务名\n便于在脚本编辑器中选择\n若设为 # 可取消定时任务\n\n4️⃣ timeout\n\n脚本超时, 单位为秒\n\n5️⃣ engine\n\n默认为自动使用 webview 引擎, 可设为指定 jsc, 但 jsc 容易爆内存\n\n6️⃣ produce\n\n自定义处理订阅的定时任务名\n一般用于定时处理耗时较长的订阅, 以更新缓存\n这样 Surge 中拉取的时候就能用到缓存, 不至于总是超时\n若设为 # 可取消此定时任务\n默认不开启\n\n7️⃣ produce_cronexp\n\n配置处理订阅的定时任务\n\n默认为每 6 小时\n\n9️⃣ produce_sub\n\n自定义需定时处理的单条订阅名\n多个用 , 连接\n\n🔟 produce_col\n\n自定义需定时处理的组合订阅名\n多个用 , 连接\n\n⚠️ 注意: 是 名称(name) 不是 显示名称(displayName)\n如果名称需要编码, 请编码后再用 , 连接\n顺序: 并发执行单条订阅, 然后并发执行组合订阅
|
||||
|
||||
[MITM]
|
||||
hostname = %APPEND% sub.store
|
||||
@@ -12,4 +12,6 @@ Sub-Store Core=type=http-request,pattern=^https?:\/\/sub\.store\/((download)|api
|
||||
|
||||
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,timeout={{{timeout}}},engine={{{engine}}}
|
||||
|
||||
{{{sync}}}=type=cron,cronexp="{{{cronexp}}}",wake-system=1,timeout={{{timeout}}},script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js,engine={{{engine}}}
|
||||
{{{sync}}}=type=cron,cronexp="{{{cronexp}}}",wake-system=1,timeout={{{timeout}}},script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js,engine={{{engine}}}
|
||||
|
||||
{{{produce}}}=type=cron,cronexp="{{{produce_cronexp}}}",wake-system=1,timeout={{{timeout}}},script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js,engine={{{engine}}},argument="sub={{{produce_sub}}}&col={{{produce_col}}}"
|
||||
@@ -1,5 +1,5 @@
|
||||
#!name=Sub-Store
|
||||
#!desc=高级订阅管理工具 @Peng-YM 无 ability 参数版本,不会爆内存, 如果需要使用指定节点功能 例如[加旗帜脚本或者cname脚本] 可以用带 ability 参数. 定时任务默认为每天 23 点 55 分
|
||||
#!desc=高级订阅管理工具 @Peng-YM 无 ability 参数版本,不会爆内存, 如果需要使用指定节点功能 例如[加旗帜脚本或者cname脚本] 可以用带 ability 参数. 定时任务默认为每天 23 点 55 分. 定时任务指定时将订阅/文件上传到私有 Gist. 在前端, 叫做 '同步' 或 '同步配置'
|
||||
#!category=订阅管理
|
||||
|
||||
[MITM]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!name=Sub-Store
|
||||
#!desc=高级订阅管理工具 @Peng-YM 带 ability 参数版本, 使用 jsc 引擎时, 可能会爆内存, 如果不需要使用指定节点功能 例如[加旗帜脚本或者cname脚本] 可以用不带 ability 参数版本. 定时任务默认为每天 23 点 55 分
|
||||
#!desc=高级订阅管理工具 @Peng-YM 带 ability 参数版本, 使用 jsc 引擎时, 可能会爆内存, 如果不需要使用指定节点功能 例如[加旗帜脚本或者cname脚本] 可以用不带 ability 参数版本. 定时任务默认为每天 23 点 55 分. 定时任务指定时将订阅/文件上传到私有 Gist. 在前端, 叫做 '同步' 或 '同步配置'
|
||||
#!category=订阅管理
|
||||
|
||||
[MITM]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!name=Sub-Store
|
||||
#!desc=支持 Surge 正式版的参数设置功能. 测落地功能 ability: http-client-policy, 同步配置的定时 cronexp: 55 23 * * *
|
||||
#!category=订阅管理
|
||||
#!arguments=ability:http-client-policy,cronexp:55 23 * * *,sync:"Sub-Store Sync",timeout:120,engine:auto
|
||||
#!arguments-desc=\n1️⃣ ability\n\n默认已开启测落地能力\n需要配合脚本操作\n如 https://raw.githubusercontent.com/Keywos/rule/main/cname.js\n填写任意其他值关闭\n\n2️⃣ cronexp\n\n同步配置定时任务\n默认为每天 23 点 55 分\n\n3️⃣ sync\n\n自定义定时任务名\n便于在脚本编辑器中选择\n若设为 # 可取消定时任务\n\n4️⃣ timeout\n\n超时, 单位为秒\n\n5️⃣ engine\n\n默认为自动使用 webview 引擎, 可设为指定 jsc, 但 jsc 容易爆内存
|
||||
#!arguments=ability:http-client-policy,cronexp:55 23 * * *,sync:"Sub-Store Sync",timeout:120,engine:auto,produce:"# Sub-Store Produce",produce_cronexp:50 */6 * * *,produce_sub:"sub1,sub2",produce_col:"col1,col2"
|
||||
#!arguments-desc=\n1️⃣ ability\n\n默认已开启测落地能力\n需要配合脚本操作\n如 https://raw.githubusercontent.com/Keywos/rule/main/cname.js\n填写任意其他值关闭\n\n2️⃣ cronexp\n\n同步配置定时任务\n默认为每天 23 点 55 分\n\n定时任务指定时将订阅/文件上传到私有 Gist. 在前端, 叫做 '同步' 或 '同步配置'\n\n3️⃣ sync\n\n自定义定时任务名\n便于在脚本编辑器中选择\n若设为 # 可取消定时任务\n\n4️⃣ timeout\n\n脚本超时, 单位为秒\n\n5️⃣ engine\n\n默认为自动使用 webview 引擎, 可设为指定 jsc, 但 jsc 容易爆内存\n\n6️⃣ produce\n\n自定义处理订阅的定时任务名\n一般用于定时处理耗时较长的订阅, 以更新缓存\n这样 Surge 中拉取的时候就能用到缓存, 不至于总是超时\n若设为 # 可取消此定时任务\n默认不开启\n\n7️⃣ produce_cronexp\n\n配置处理订阅的定时任务\n\n默认为每 6 小时\n\n9️⃣ produce_sub\n\n自定义需定时处理的单条订阅名\n多个用 , 连接\n\n🔟 produce_col\n\n自定义需定时处理的组合订阅名\n多个用 , 连接\n\n⚠️ 注意: 是 名称(name) 不是 显示名称(displayName)\n如果名称需要编码, 请编码后再用 , 连接\n顺序: 并发执行单条订阅, 然后并发执行组合订阅
|
||||
|
||||
[MITM]
|
||||
hostname = %APPEND% sub.store
|
||||
@@ -12,4 +12,6 @@ Sub-Store Core=type=http-request,pattern=^https?:\/\/sub\.store\/((download)|api
|
||||
|
||||
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,timeout={{{timeout}}},engine={{{engine}}}
|
||||
|
||||
{{{sync}}}=type=cron,cronexp="{{{cronexp}}}",wake-system=1,timeout={{{timeout}}},script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js,engine={{{engine}}}
|
||||
{{{sync}}}=type=cron,cronexp="{{{cronexp}}}",wake-system=1,timeout={{{timeout}}},script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js,engine={{{engine}}}
|
||||
|
||||
{{{produce}}}=type=cron,cronexp="{{{produce_cronexp}}}",wake-system=1,timeout={{{timeout}}},script-path=https://github.com/sub-store-org/Sub-Store/releases/latest/download/cron-sync-artifacts.min.js,engine={{{engine}}},argument="sub={{{produce_sub}}}&col={{{produce_col}}}"
|
||||
@@ -8,9 +8,11 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
// 结构大致参考了 Clash.Meta(mihomo) 有私货
|
||||
// 可在预览界面点击节点查看 JSON 结构 或查看 `target=JSON` 的通用订阅
|
||||
// 1. `no-resolve` 为不解析域名
|
||||
// 2. 域名解析后 会多一个 `resolved` 字段
|
||||
// 2. 域名解析后 会多一个 `_resolved` 字段
|
||||
// 3. 域名解析后会有`_IPv4`, `_IPv6`, `_IP`(若有多个步骤, 只取第一次成功的 v4 或 v6 数据), `_domain` 字段
|
||||
// 4. 节点字段 `exec` 为 `ssr-local` 路径, 默认 `/usr/local/bin/ssr-local`; 端口从 10000 开始递增(暂不支持配置)
|
||||
// 5. `_subName` 为单条订阅名
|
||||
// 6. `_collectionName` 为组合订阅名
|
||||
|
||||
// $arguments 为传入的脚本参数
|
||||
|
||||
@@ -96,40 +98,9 @@ function operator(proxies = [], targetPlatform, context) {
|
||||
// })
|
||||
|
||||
// 4. 一个比较折腾的方案: 在脚本操作中, 把内容同步到另一个 gist
|
||||
|
||||
// async function operator(proxies = []) {
|
||||
// const $ = $substore
|
||||
// const GITHUB_TOKEN = 'ghp_xxxxxxxxxxxxxxxxxxxxx'
|
||||
// const GIST_NAME = 'share'
|
||||
// const FILENAME = 'mihomo.yaml'
|
||||
// let files = {}
|
||||
// let content = await produceArtifact({
|
||||
// type: 'subscription',
|
||||
// subscription: {},
|
||||
// content: 'proxies:\n' + proxies.map((proxy) => ' - ' + JSON.stringify(proxy) + '\n').join(''),
|
||||
// platform: 'ClashMeta',
|
||||
// })
|
||||
// const manager = new ProxyUtils.Gist({
|
||||
// token: GITHUB_TOKEN,
|
||||
// key: GIST_NAME,
|
||||
// });
|
||||
// files[encodeURIComponent(FILENAME)] = {
|
||||
// content,
|
||||
// };
|
||||
// const res = await manager.upload(files);
|
||||
// let body = {};
|
||||
// try {
|
||||
// body = JSON.parse(res.body);
|
||||
// // eslint-disable-next-line no-empty
|
||||
// } catch (e) {}
|
||||
// const raw_url =
|
||||
// body.files[encodeURIComponent(FILENAME)]?.raw_url;
|
||||
// console.log(raw_url)
|
||||
// const new_url = raw_url?.replace(/\/raw\/[^/]*\/(.*)/, '/raw/$1');
|
||||
// console.log(new_url)
|
||||
// $.notify('🌍 Sub-Store', `更新到 Gist: ${new_url}`);
|
||||
// return proxies
|
||||
// }
|
||||
// 见 https://t.me/zhetengsha/1428
|
||||
//
|
||||
// const content = ProxyUtils.produce(proxies, platform)
|
||||
|
||||
// // YAML
|
||||
// ProxyUtils.yaml.load('YAML String')
|
||||
|
||||
Reference in New Issue
Block a user