Compare commits

...

9 Commits

7 changed files with 106 additions and 16 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "sub-store", "name": "sub-store",
"version": "2.15.2", "version": "2.16.0",
"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": {

View File

@@ -72,6 +72,15 @@ export default function Egern_Producer() {
return true; return true;
}) })
.map((proxy) => { .map((proxy) => {
if (proxy.tls && !proxy.sni) {
proxy.sni = proxy.server;
}
const prev_hop =
proxy.prev_hop ||
proxy['underlying-proxy'] ||
proxy['dialer-proxy'] ||
proxy.detour;
if (proxy.type === 'http') { if (proxy.type === 'http') {
proxy = { proxy = {
type: 'http', type: 'http',
@@ -130,6 +139,8 @@ export default function Egern_Producer() {
next_hop: proxy.next_hop, next_hop: proxy.next_hop,
sni: proxy.sni, sni: proxy.sni,
skip_tls_verify: proxy['skip-cert-verify'], skip_tls_verify: proxy['skip-cert-verify'],
port_hopping: proxy.ports,
port_hopping_interval: proxy['hop-interval'],
}; };
if (proxy['obfs-password'] && proxy.obfs == 'salamander') { if (proxy['obfs-password'] && proxy.obfs == 'salamander') {
proxy.obfs = 'salamander'; proxy.obfs = 'salamander';
@@ -284,6 +295,7 @@ export default function Egern_Producer() {
[proxy.type]: { [proxy.type]: {
...proxy, ...proxy,
type: undefined, type: undefined,
prev_hop,
}, },
}; };
}); });

View File

@@ -568,7 +568,7 @@ const hysteriaParser = (proxy = {}) => {
smuxParser(proxy.smux, parsedProxy); smuxParser(proxy.smux, parsedProxy);
return parsedProxy; return parsedProxy;
}; };
const hysteria2Parser = (proxy = {}) => { const hysteria2Parser = (proxy = {}, includeUnsupportedProxy) => {
const parsedProxy = { const parsedProxy = {
tag: proxy.name, tag: proxy.name,
type: 'hysteria2', type: 'hysteria2',
@@ -580,6 +580,16 @@ const hysteria2Parser = (proxy = {}) => {
}; };
if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535) if (parsedProxy.server_port < 0 || parsedProxy.server_port > 65535)
throw 'invalid port'; throw 'invalid port';
if (includeUnsupportedProxy) {
if (proxy['hop-interval'])
parsedProxy.hop_interval = /^\d+$/.test(proxy['hop-interval'])
? `${proxy['hop-interval']}s`
: proxy['hop-interval'];
if (proxy['ports'])
parsedProxy.server_ports = proxy['ports']
.split(/\s*,\s*/)
.map((p) => p.replace(/\s*-\s*/g, ':'));
}
if (proxy.up) parsedProxy.up_mbps = parseInt(`${proxy.up}`, 10); if (proxy.up) parsedProxy.up_mbps = parseInt(`${proxy.up}`, 10);
if (proxy.down) parsedProxy.down_mbps = parseInt(`${proxy.down}`, 10); if (proxy.down) parsedProxy.down_mbps = parseInt(`${proxy.down}`, 10);
if (proxy.obfs === 'salamander') parsedProxy.obfs.type = 'salamander'; if (proxy.obfs === 'salamander') parsedProxy.obfs.type = 'salamander';
@@ -790,7 +800,12 @@ export default function singbox_Producer() {
list.push(hysteriaParser(proxy)); list.push(hysteriaParser(proxy));
break; break;
case 'hysteria2': case 'hysteria2':
list.push(hysteria2Parser(proxy)); list.push(
hysteria2Parser(
proxy,
opts['include-unsupported-proxy'],
),
);
break; break;
case 'tuic': case 'tuic':
if (!proxy.token || proxy.token.length === 0) { if (!proxy.token || proxy.token.length === 0) {

View File

@@ -50,11 +50,21 @@ function createCollection(req, res) {
function getCollection(req, res) { function getCollection(req, res) {
let { name } = req.params; let { name } = req.params;
let { raw } = req.query;
name = decodeURIComponent(name); name = decodeURIComponent(name);
const allCols = $.read(COLLECTIONS_KEY); const allCols = $.read(COLLECTIONS_KEY);
const collection = findByName(allCols, name); const collection = findByName(allCols, name);
if (collection) { if (collection) {
success(res, collection); if (raw) {
res.set('content-type', 'application/json')
.set(
'content-disposition',
`attachment; filename="${encodeURIComponent(name)}.json"`,
)
.send(JSON.stringify(collection));
} else {
success(res, collection);
}
} else { } else {
failed( failed(
res, res,

View File

@@ -13,11 +13,44 @@ import { getISO } from '@/utils/geo';
import env from '@/utils/env'; import env from '@/utils/env';
export default function register($app) { export default function register($app) {
$app.get('/share/col/:name/:target', async (req, res) => {
const { target } = req.params;
if (target) {
req.query.target = target;
$.info(`使用路由指定目标: ${target}`);
}
await downloadCollection(req, res);
});
$app.get('/share/col/:name', downloadCollection); $app.get('/share/col/:name', downloadCollection);
$app.get('/share/sub/:name/:target', async (req, res) => {
const { target } = req.params;
if (target) {
req.query.target = target;
$.info(`使用路由指定目标: ${target}`);
}
await downloadSubscription(req, res);
});
$app.get('/share/sub/:name', downloadSubscription); $app.get('/share/sub/:name', downloadSubscription);
$app.get('/download/collection/:name/:target', async (req, res) => {
const { target } = req.params;
if (target) {
req.query.target = target;
$.info(`使用路由指定目标: ${target}`);
}
await downloadCollection(req, res);
});
$app.get('/download/collection/:name', downloadCollection); $app.get('/download/collection/:name', downloadCollection);
$app.get('/download/:name/:target', async (req, res) => {
const { target } = req.params;
if (target) {
req.query.target = target;
$.info(`使用路由指定目标: ${target}`);
}
await downloadSubscription(req, res);
});
$app.get('/download/:name', downloadSubscription); $app.get('/download/:name', downloadSubscription);
$app.get( $app.get(
'/download/collection/:name/api/v1/server/details', '/download/collection/:name/api/v1/server/details',
async (req, res) => { async (req, res) => {
@@ -59,11 +92,9 @@ async function downloadSubscription(req, res) {
const platform = const platform =
req.query.target || getPlatformFromHeaders(req.headers) || 'JSON'; req.query.target || getPlatformFromHeaders(req.headers) || 'JSON';
const reqUA = req.headers['user-agent'] || req.headers['User-Agent'];
$.info( $.info(
`正在下载订阅:${name}\n请求 User-Agent: ${ `正在下载订阅:${name}\n请求 User-Agent: ${reqUA}\n请求 target: ${req.query.target}\n实际输出: ${platform}`,
req.headers['user-agent'] || req.headers['User-Agent']
}\n请求 target: ${req.query.target}\n实际输出: ${platform}`,
); );
let { let {
url, url,
@@ -98,6 +129,14 @@ async function downloadSubscription(req, res) {
if (url) { if (url) {
url = decodeURIComponent(url); url = decodeURIComponent(url);
$.info(`指定远程订阅 URL: ${url}`); $.info(`指定远程订阅 URL: ${url}`);
if (!/^https?:\/\//.test(url)) {
content = url;
$.info(`URL 不是链接,视为本地订阅`);
}
}
if (content) {
content = decodeURIComponent(content);
$.info(`指定本地订阅: ${content}`);
} }
if (proxy) { if (proxy) {
proxy = decodeURIComponent(proxy); proxy = decodeURIComponent(proxy);
@@ -107,10 +146,7 @@ async function downloadSubscription(req, res) {
ua = decodeURIComponent(ua); ua = decodeURIComponent(ua);
$.info(`指定远程订阅 User-Agent: ${ua}`); $.info(`指定远程订阅 User-Agent: ${ua}`);
} }
if (content) {
content = decodeURIComponent(content);
$.info(`指定本地订阅: ${content}`);
}
if (mergeSources) { if (mergeSources) {
mergeSources = decodeURIComponent(mergeSources); mergeSources = decodeURIComponent(mergeSources);
$.info(`指定合并来源: ${mergeSources}`); $.info(`指定合并来源: ${mergeSources}`);
@@ -140,6 +176,13 @@ async function downloadSubscription(req, res) {
const sub = findByName(allSubs, name); const sub = findByName(allSubs, name);
if (sub) { if (sub) {
try { try {
const passThroughUA = sub.passThroughUA;
if (passThroughUA) {
$.info(
`订阅开启了透传 User-Agent, 使用请求的 User-Agent: ${reqUA}`,
);
ua = reqUA;
}
let output = await produceArtifact({ let output = await produceArtifact({
type: 'subscription', type: 'subscription',
name, name,

View File

@@ -197,11 +197,21 @@ async function getFile(req, res) {
} }
function getWholeFile(req, res) { function getWholeFile(req, res) {
let { name } = req.params; let { name } = req.params;
let { raw } = req.query;
name = decodeURIComponent(name); name = decodeURIComponent(name);
const allFiles = $.read(FILES_KEY); const allFiles = $.read(FILES_KEY);
const file = findByName(allFiles, name); const file = findByName(allFiles, name);
if (file) { if (file) {
success(res, file); if (raw) {
res.set('content-type', 'application/json')
.set(
'content-disposition',
`attachment; filename="${encodeURIComponent(name)}.json"`,
)
.send(JSON.stringify(file));
} else {
success(res, file);
}
} else { } else {
failed( failed(
res, res,

View File

@@ -73,7 +73,7 @@ async function produceArtifact({
proxy || sub.proxy, proxy || sub.proxy,
undefined, undefined,
awaitCustomCache, awaitCustomCache,
noCache, noCache || sub.noCache,
true, true,
); );
} catch (err) { } catch (err) {
@@ -122,7 +122,7 @@ async function produceArtifact({
proxy || sub.proxy, proxy || sub.proxy,
undefined, undefined,
awaitCustomCache, awaitCustomCache,
noCache, noCache || sub.noCache,
true, true,
); );
} catch (err) { } catch (err) {
@@ -244,7 +244,7 @@ async function produceArtifact({
collection.proxy, collection.proxy,
undefined, undefined,
undefined, undefined,
noCache, noCache || sub.noCache,
true, true,
); );
} catch (err) { } catch (err) {