mirror of
https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 00:52:40 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8ed6a3342 | ||
|
|
99b19c410d | ||
|
|
9e54507bbb | ||
|
|
20afa0ad22 | ||
|
|
c5b6960b35 | ||
|
|
4dd86cb368 | ||
|
|
4a0319e95f | ||
|
|
090d8a978f | ||
|
|
bc9fae6062 | ||
|
|
048344268c | ||
|
|
c5746f6a6b | ||
|
|
5cb226da62 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sub-store",
|
||||
"version": "2.14.7",
|
||||
"version": "2.14.16",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -23,12 +23,19 @@ function URI_SS() {
|
||||
};
|
||||
content = content.split('#')[0]; // strip proxy name
|
||||
// handle IPV4 and IPV6
|
||||
const serverAndPort = content.match(/@([^/]*)(\/|$)/)[1];
|
||||
let serverAndPortArray = content.match(/@([^/]*)(\/|$)/);
|
||||
let userInfoStr = Base64.decode(content.split('@')[0]);
|
||||
if (!serverAndPortArray) {
|
||||
content = Base64.decode(content);
|
||||
userInfoStr = content.split('@')[0];
|
||||
serverAndPortArray = content.match(/@([^/]*)(\/|$)/);
|
||||
}
|
||||
const serverAndPort = serverAndPortArray[1];
|
||||
const portIdx = serverAndPort.lastIndexOf(':');
|
||||
proxy.server = serverAndPort.substring(0, portIdx);
|
||||
proxy.port = serverAndPort.substring(portIdx + 1);
|
||||
|
||||
const userInfo = Base64.decode(content.split('@')[0]).split(':');
|
||||
const userInfo = userInfoStr.split(':');
|
||||
proxy.cipher = userInfo[0];
|
||||
proxy.password = userInfo[1];
|
||||
|
||||
@@ -209,7 +216,7 @@ function URI_VMess() {
|
||||
type: 'vmess',
|
||||
server: params.add,
|
||||
port: params.port,
|
||||
cipher: 'auto', // V2rayN has no default cipher! use aes-128-gcm as default.
|
||||
cipher: getIfPresent(params.scy, 'auto'),
|
||||
uuid: params.id,
|
||||
alterId: getIfPresent(params.aid, 0),
|
||||
tls: params.tls === 'tls' || params.tls === true,
|
||||
|
||||
@@ -25,6 +25,9 @@ const grammars = String.raw`
|
||||
proxy.network = "ws";
|
||||
$set(proxy, "ws-opts.path", obfs.path);
|
||||
$set(proxy, "ws-opts.headers", obfs['ws-headers']);
|
||||
if (proxy['ws-opts'] && proxy['ws-opts']['headers'] && proxy['ws-opts']['headers'].Host) {
|
||||
proxy['ws-opts']['headers'].Host = proxy['ws-opts']['headers'].Host.replace(/^"(.*)"$/, '$1')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
proxy.network = "ws";
|
||||
$set(proxy, "ws-opts.path", obfs.path);
|
||||
$set(proxy, "ws-opts.headers", obfs['ws-headers']);
|
||||
if (proxy['ws-opts'] && proxy['ws-opts']['headers'] && proxy['ws-opts']['headers'].Host) {
|
||||
proxy['ws-opts']['headers'].Host = proxy['ws-opts']['headers'].Host.replace(/^"(.*)"$/, '$1')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,26 @@ export default function Clash_Producer() {
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
['vmess', 'vless'].includes(proxy.type) &&
|
||||
proxy.network === 'http'
|
||||
) {
|
||||
let httpPath = proxy['http-opts']?.path;
|
||||
if (
|
||||
isPresent(proxy, 'http-opts.path') &&
|
||||
!Array.isArray(httpPath)
|
||||
) {
|
||||
proxy['http-opts'].path = [httpPath];
|
||||
}
|
||||
let httpHost = proxy['http-opts']?.headers?.Host;
|
||||
if (
|
||||
isPresent(proxy, 'http-opts.headers.Host') &&
|
||||
!Array.isArray(httpHost)
|
||||
) {
|
||||
proxy['http-opts'].headers.Host = [httpHost];
|
||||
}
|
||||
}
|
||||
|
||||
delete proxy['tls-fingerprint'];
|
||||
return ' - ' + JSON.stringify(proxy) + '\n';
|
||||
})
|
||||
|
||||
@@ -127,9 +127,7 @@ function trojan(proxy) {
|
||||
function vmess(proxy) {
|
||||
const result = new Result(proxy);
|
||||
result.append(
|
||||
`${proxy.name}=vmess,${proxy.server},${proxy.port},${
|
||||
proxy.cipher === 'auto' ? 'none' : proxy.cipher
|
||||
},"${proxy.uuid}"`,
|
||||
`${proxy.name}=vmess,${proxy.server},${proxy.port},${proxy.cipher},"${proxy.uuid}"`,
|
||||
);
|
||||
|
||||
// transport
|
||||
@@ -146,12 +144,14 @@ function vmess(proxy) {
|
||||
);
|
||||
} else if (proxy.network === 'http') {
|
||||
result.append(`,transport=http`);
|
||||
let httpPath = proxy['http-opts']?.path;
|
||||
let httpHost = proxy['http-opts']?.headers?.Host;
|
||||
result.appendIfPresent(
|
||||
`,path=${proxy['http-opts'].path}`,
|
||||
`,path=${Array.isArray(httpPath) ? httpPath[0] : httpPath}`,
|
||||
'http-opts.path',
|
||||
);
|
||||
result.appendIfPresent(
|
||||
`,host=${proxy['http-opts'].headers.Host}`,
|
||||
`,host=${Array.isArray(httpHost) ? httpHost[0] : httpHost}`,
|
||||
'http-opts.headers.Host',
|
||||
);
|
||||
} else {
|
||||
@@ -208,12 +208,14 @@ function vless(proxy) {
|
||||
);
|
||||
} else if (proxy.network === 'http') {
|
||||
result.append(`,transport=http`);
|
||||
let httpPath = proxy['http-opts']?.path;
|
||||
let httpHost = proxy['http-opts']?.headers?.Host;
|
||||
result.appendIfPresent(
|
||||
`,path=${proxy['http-opts'].path}`,
|
||||
`,path=${Array.isArray(httpPath) ? httpPath[0] : httpPath}`,
|
||||
'http-opts.path',
|
||||
);
|
||||
result.appendIfPresent(
|
||||
`,host=${proxy['http-opts'].headers.Host}`,
|
||||
`,host=${Array.isArray(httpHost) ? httpHost[0] : httpHost}`,
|
||||
'http-opts.headers.Host',
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -206,12 +206,14 @@ function vmess(proxy) {
|
||||
} else {
|
||||
throw new Error(`network ${proxy.network} is unsupported`);
|
||||
}
|
||||
let httpPath = proxy[`${proxy.network}-opts`]?.path;
|
||||
let httpHost = proxy[`${proxy.network}-opts`]?.headers?.Host;
|
||||
appendIfPresent(
|
||||
`,obfs-uri=${proxy[`${proxy.network}-opts`].path}`,
|
||||
`,obfs-uri=${Array.isArray(httpPath) ? httpPath[0] : httpPath}`,
|
||||
`${proxy.network}-opts.path`,
|
||||
);
|
||||
appendIfPresent(
|
||||
`,obfs-host=${proxy[`${proxy.network}-opts`].headers.Host}`,
|
||||
`,obfs-host=${Array.isArray(httpHost) ? httpHost[0] : httpHost}`,
|
||||
`${proxy.network}-opts.headers.Host`,
|
||||
);
|
||||
} else {
|
||||
@@ -332,11 +334,5 @@ function socks5(proxy) {
|
||||
}
|
||||
|
||||
function needTls(proxy) {
|
||||
return (
|
||||
proxy.tls ||
|
||||
proxy.sni ||
|
||||
typeof proxy['skip-cert-verify'] !== 'undefined' ||
|
||||
typeof proxy['tls-fingerprint'] !== 'undefined' ||
|
||||
typeof proxy['tls-host'] !== 'undefined'
|
||||
);
|
||||
return proxy.tls;
|
||||
}
|
||||
|
||||
@@ -58,6 +58,26 @@ export default function Stash_Producer() {
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
['vmess', 'vless'].includes(proxy.type) &&
|
||||
proxy.network === 'http'
|
||||
) {
|
||||
let httpPath = proxy['http-opts']?.path;
|
||||
if (
|
||||
isPresent(proxy, 'http-opts.path') &&
|
||||
!Array.isArray(httpPath)
|
||||
) {
|
||||
proxy['http-opts'].path = [httpPath];
|
||||
}
|
||||
let httpHost = proxy['http-opts']?.headers?.Host;
|
||||
if (
|
||||
isPresent(proxy, 'http-opts.headers.Host') &&
|
||||
!Array.isArray(httpHost)
|
||||
) {
|
||||
proxy['http-opts'].headers.Host = [httpHost];
|
||||
}
|
||||
}
|
||||
|
||||
delete proxy['tls-fingerprint'];
|
||||
return ' - ' + JSON.stringify(proxy) + '\n';
|
||||
})
|
||||
|
||||
@@ -280,7 +280,7 @@ function tuic(proxy) {
|
||||
);
|
||||
|
||||
// tfo
|
||||
result.appendIfPresent(`,tfo=${proxy.tfo}`, 'fast-open');
|
||||
result.appendIfPresent(`,tfo=${proxy['fast-open']}`, 'fast-open');
|
||||
result.appendIfPresent(`,tfo=${proxy.tfo}`, 'tfo');
|
||||
|
||||
// test-url
|
||||
@@ -301,7 +301,13 @@ function handleTransport(result, proxy) {
|
||||
if (isPresent(proxy, 'ws-opts.headers')) {
|
||||
const headers = proxy['ws-opts'].headers;
|
||||
const value = Object.keys(headers)
|
||||
.map((k) => `${k}:${headers[k]}`)
|
||||
.map((k) => {
|
||||
let v = headers[k];
|
||||
if (['Host'].includes(k)) {
|
||||
v = `"${v}"`;
|
||||
}
|
||||
return `${k}:${v}`;
|
||||
})
|
||||
.join('|');
|
||||
if (isNotBlank(value)) {
|
||||
result.append(`,ws-headers=${value}`);
|
||||
|
||||
@@ -75,6 +75,8 @@ export default function URI_Producer() {
|
||||
case 'trojan':
|
||||
result = `trojan://${proxy.password}@${proxy.server}:${
|
||||
proxy.port
|
||||
}?sni=${encodeURIComponent(proxy.sni || proxy.server)}${
|
||||
proxy['skip-cert-verify'] ? '&allowInsecure=1' : ''
|
||||
}#${encodeURIComponent(proxy.name)}`;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,10 @@ export default function register($app) {
|
||||
if (!$.read(ARTIFACTS_KEY)) $.write({}, ARTIFACTS_KEY);
|
||||
|
||||
// RESTful APIs
|
||||
$app.route('/api/artifacts').get(getAllArtifacts).post(createArtifact);
|
||||
$app.route('/api/artifacts')
|
||||
.get(getAllArtifacts)
|
||||
.post(createArtifact)
|
||||
.put(replaceArtifact);
|
||||
|
||||
$app.route('/api/artifact/:name')
|
||||
.get(getArtifact)
|
||||
@@ -32,6 +35,12 @@ function getAllArtifacts(req, res) {
|
||||
success(res, allArtifacts);
|
||||
}
|
||||
|
||||
function replaceArtifact(req, res) {
|
||||
const allArtifacts = req.body;
|
||||
$.write(allArtifacts, ARTIFACTS_KEY);
|
||||
success(res);
|
||||
}
|
||||
|
||||
async function getArtifact(req, res) {
|
||||
let { name } = req.params;
|
||||
name = decodeURIComponent(name);
|
||||
@@ -131,7 +140,7 @@ async function deleteArtifact(req, res) {
|
||||
files[encodeURIComponent(artifact.name)] = {
|
||||
content: '',
|
||||
};
|
||||
// 当别的Sub 删了同步订阅 或 gist里面删了 当前设备没有删除 时 无法删除的bug
|
||||
// 当别的Sub 删了同步订阅 或 gist里面删了 当前设备没有删除 时 无法删除的bug
|
||||
try {
|
||||
await syncToGist(files);
|
||||
} catch (i) {
|
||||
|
||||
@@ -14,7 +14,8 @@ export default function register($app) {
|
||||
|
||||
$app.route('/api/collections')
|
||||
.get(getAllCollections)
|
||||
.post(createCollection);
|
||||
.post(createCollection)
|
||||
.put(replaceCollection);
|
||||
}
|
||||
|
||||
// collection API
|
||||
@@ -111,3 +112,9 @@ function getAllCollections(req, res) {
|
||||
const allCols = $.read(COLLECTIONS_KEY);
|
||||
success(res, allCols);
|
||||
}
|
||||
|
||||
function replaceCollection(req, res) {
|
||||
const allCols = req.body;
|
||||
$.write(allCols, COLLECTIONS_KEY);
|
||||
success(res);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,10 @@ export default function register($app) {
|
||||
.patch(updateSubscription)
|
||||
.delete(deleteSubscription);
|
||||
|
||||
$app.route('/api/subs').get(getAllSubscriptions).post(createSubscription);
|
||||
$app.route('/api/subs')
|
||||
.get(getAllSubscriptions)
|
||||
.post(createSubscription)
|
||||
.put(replaceSubscriptions);
|
||||
}
|
||||
|
||||
// subscriptions API
|
||||
@@ -66,10 +69,10 @@ async function getFlowInfo(req, res) {
|
||||
}
|
||||
|
||||
// unit is KB
|
||||
const uploadMatch = flowHeaders.match(/upload=(-?)(\d+)/)
|
||||
const uploadMatch = flowHeaders.match(/upload=(-?)(\d+)/);
|
||||
const upload = Number(uploadMatch[1] + uploadMatch[2]);
|
||||
|
||||
const downloadMatch = flowHeaders.match(/download=(-?)(\d+)/)
|
||||
const downloadMatch = flowHeaders.match(/download=(-?)(\d+)/);
|
||||
const download = Number(downloadMatch[1] + downloadMatch[2]);
|
||||
|
||||
const total = Number(flowHeaders.match(/total=(\d+)/)[1]);
|
||||
@@ -202,3 +205,9 @@ function getAllSubscriptions(req, res) {
|
||||
const allSubs = $.read(SUBS_KEY);
|
||||
success(res, allSubs);
|
||||
}
|
||||
|
||||
function replaceSubscriptions(req, res) {
|
||||
const allSubs = req.body;
|
||||
$.write(allSubs, SUBS_KEY);
|
||||
success(res);
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ export function getFlag(name) {
|
||||
'🇲🇴': ['Macao', '澳门', '澳門', 'CTM'],
|
||||
'🇲🇹': ['Malta', '马耳他'],
|
||||
'🇲🇽': ['Mexico', '墨西哥'],
|
||||
'🇲🇾': ['Malaysia', '马来西亚', '馬來西亞', '吉隆坡', '大馬'],
|
||||
'🇲🇾': ['Malaysia', '马来', '馬來', '吉隆坡', '大馬'],
|
||||
'🇳🇱': ['Netherlands', '荷兰', '荷蘭', '尼德蘭', '阿姆斯特丹'],
|
||||
'🇳🇴': ['Norway', '挪威'],
|
||||
'🇳🇵': ['Nepal', '尼泊尔'],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!name=Sub-Store
|
||||
#!desc=高级订阅管理工具 @Peng-YM 无 ability 参数版本,不会爆内存, 如果需要使用指定节点功能 例如[加国旗脚本或者cname脚本] 可以用原版
|
||||
#!desc=高级订阅管理工具 @Peng-YM 无 ability 参数版本,不会爆内存, 如果需要使用指定节点功能 例如[加国旗脚本或者cname脚本] 可以用带 ability 参数
|
||||
|
||||
[MITM]
|
||||
hostname = %APPEND% sub.store
|
||||
|
||||
11
config/Surge-ability.sgmodule
Normal file
11
config/Surge-ability.sgmodule
Normal file
@@ -0,0 +1,11 @@
|
||||
#!name=Sub-Store
|
||||
#!desc=高级订阅管理工具 @Peng-YM 带 ability 参数版本, 可能会爆内存, 如果不需要使用指定节点功能 例如[加国旗脚本或者cname脚本] 可以用不带 ability 参数版本
|
||||
|
||||
[MITM]
|
||||
hostname = %APPEND% sub.store
|
||||
|
||||
[Script]
|
||||
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,ability=http-client-policy
|
||||
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
|
||||
@@ -1,10 +1,11 @@
|
||||
#!name=Sub-Store
|
||||
#!desc=高级订阅管理工具 @Peng-YM
|
||||
#!desc=高级订阅管理工具 @Peng-YM 无 ability 参数版本,不会爆内存, 如果需要使用指定节点功能 例如[加国旗脚本或者cname脚本] 可以用带 ability 参数
|
||||
|
||||
[MITM]
|
||||
hostname=%APPEND% sub.store
|
||||
hostname = %APPEND% sub.store
|
||||
|
||||
[Script]
|
||||
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,ability=http-client-policy
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user