mirror of
https://git.mirrors.martin98.com/https://github.com/sub-store-org/Sub-Store.git
synced 2026-03-21 02:32:36 +08:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b75c11587 | ||
|
|
c287dcad3b | ||
|
|
ce6cd794c8 | ||
|
|
e05475aa5e | ||
|
|
c35e9d37ae | ||
|
|
8f2dbfe3df | ||
|
|
a0a998dfdd | ||
|
|
12491ac7c0 | ||
|
|
78e3024cec | ||
|
|
5e21a20e37 | ||
|
|
76b5dc5809 | ||
|
|
a1776644a0 | ||
|
|
7aaa03d4ca |
8
.github/workflows/main.yml
vendored
8
.github/workflows/main.yml
vendored
@@ -45,18 +45,14 @@ jobs:
|
||||
cd backend
|
||||
SUBSTORE_RELEASE=`node --eval="process.stdout.write(require('./package.json').version)"`
|
||||
echo "release_tag=$SUBSTORE_RELEASE" >> $GITHUB_OUTPUT
|
||||
- name: Prepare release
|
||||
run: |
|
||||
cd backend
|
||||
pnpm i -D conventional-changelog-cli
|
||||
pnpm run changelog
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: ${{ success() }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
body_path: ./backend/CHANGELOG.md
|
||||
tag_name: ${{ steps.tag.outputs.release_tag }}
|
||||
generate_release_notes: true
|
||||
files: |
|
||||
./backend/sub-store.min.js
|
||||
./backend/dist/sub-store-0.min.js
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require('fs');
|
||||
|
||||
const path = require('path');
|
||||
const { build } = require('esbuild');
|
||||
|
||||
let content = fs.readFileSync(path.join(__dirname, 'sub-store.min.js'), {
|
||||
encoding: 'utf8',
|
||||
@@ -14,10 +14,12 @@ fs.writeFileSync(path.join(__dirname, 'dist/sub-store.no-bundle.js'), content, {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
|
||||
const { build } = require('estrella');
|
||||
build({
|
||||
entry: 'dist/sub-store.no-bundle.js',
|
||||
outfile: 'dist/sub-store.bundle.js',
|
||||
entryPoints: ['dist/sub-store.no-bundle.js'],
|
||||
bundle: true,
|
||||
minify: true,
|
||||
sourcemap: true,
|
||||
platform: 'node',
|
||||
format: 'cjs',
|
||||
outfile: 'dist/sub-store.bundle.js',
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sub-store",
|
||||
"version": "2.14.117",
|
||||
"version": "2.14.129",
|
||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||
"main": "src/main.js",
|
||||
"scripts": {
|
||||
@@ -9,15 +9,16 @@
|
||||
"serve": "node sub-store.min.js",
|
||||
"start": "nodemon -w src -w package.json --exec babel-node src/main.js",
|
||||
"build": "gulp",
|
||||
"bundle": "node bundle.js",
|
||||
"changelog": "conventional-changelog -p cli -i CHANGELOG.md -s"
|
||||
"bundle": "node bundle.js"
|
||||
},
|
||||
"author": "Peng-YM",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"automerge": "1.0.1-preview.7",
|
||||
"body-parser": "^1.19.0",
|
||||
"connect-history-api-fallback": "^2.0.0",
|
||||
"express": "^4.17.1",
|
||||
"http-proxy-middleware": "^2.0.6",
|
||||
"js-base64": "^3.7.2",
|
||||
"lodash": "^4.17.21",
|
||||
"request": "^2.88.2",
|
||||
@@ -38,6 +39,7 @@
|
||||
"browser-pack-flat": "^3.4.2",
|
||||
"browserify": "^17.0.0",
|
||||
"chai": "^4.3.6",
|
||||
"esbuild": "^0.19.8",
|
||||
"eslint": "^8.16.0",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-babel": "^8.0.0",
|
||||
|
||||
2496
backend/pnpm-lock.yaml
generated
2496
backend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -409,10 +409,10 @@ function URI_VLESS() {
|
||||
function URI_Hysteria2() {
|
||||
const name = 'URI Hysteria2 Parser';
|
||||
const test = (line) => {
|
||||
return /^hysteria2:\/\//.test(line);
|
||||
return /^(hysteria2|hy2):\/\//.test(line);
|
||||
};
|
||||
const parse = (line) => {
|
||||
line = line.split('hysteria2://')[1];
|
||||
line = line.split(/(hysteria2|hy2):\/\//)[2];
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
let [__, password, server, ___, port, addons, name] =
|
||||
/^(.*?)@(.*?)(:(\d+))?\/?\?(.*?)(?:#(.*?))$/.exec(line);
|
||||
|
||||
@@ -605,6 +605,22 @@ function ScriptFilter(script, targetPlatform, $arguments, source) {
|
||||
})();
|
||||
return output;
|
||||
},
|
||||
nodeFunc: async (proxies) => {
|
||||
let output = FULL(proxies.length, true);
|
||||
await (async function () {
|
||||
const filter = createDynamicFunction(
|
||||
'filter',
|
||||
`async function filter(proxies = []) {
|
||||
return proxies.filter(($server = {}) => {
|
||||
${script}
|
||||
})
|
||||
}`,
|
||||
$arguments,
|
||||
);
|
||||
output = filter(proxies, targetPlatform, { source, ...env });
|
||||
})();
|
||||
return output;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -635,7 +651,29 @@ async function ApplyFilter(filter, objs) {
|
||||
} catch (err) {
|
||||
// print log and skip this filter
|
||||
$.error(`Cannot apply filter ${filter.name}\n Reason: ${err}`);
|
||||
throw new Error(`脚本过滤失败 ${err.message ?? err}`);
|
||||
let funcErr = '';
|
||||
let funcErrMsg = `${err.message ?? err}`;
|
||||
if (funcErrMsg.includes('$server is not defined')) {
|
||||
funcErr = '';
|
||||
} else {
|
||||
funcErr = `执行 function filter 失败 ${funcErrMsg}; `;
|
||||
}
|
||||
try {
|
||||
selected = await filter.nodeFunc(objs);
|
||||
} catch (err) {
|
||||
$.error(
|
||||
`Cannot apply filter ${filter.name}(node script)! Reason: ${err}`,
|
||||
);
|
||||
let nodeErr = '';
|
||||
let nodeErrMsg = `${err.message ?? err}`;
|
||||
if (funcErr && nodeErrMsg === funcErrMsg) {
|
||||
nodeErr = '';
|
||||
funcErr = `执行失败 ${funcErrMsg}`;
|
||||
} else {
|
||||
nodeErr = `执行节点快捷过滤脚本 失败 ${nodeErr}`;
|
||||
}
|
||||
throw new Error(`脚本过滤 ${funcErr}${nodeErr}`);
|
||||
}
|
||||
}
|
||||
return objs.filter((_, i) => selected[i]);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ export default function Clash_Producer() {
|
||||
// https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/docs/config.yaml#L532
|
||||
// github.com/Dreamacro/clash/pull/2891/files
|
||||
// filter unsupported proxies
|
||||
// https://clash.wiki/configuration/outbound.html#shadowsocks
|
||||
proxies = proxies.filter((proxy) => {
|
||||
if (
|
||||
![
|
||||
@@ -20,6 +21,23 @@ export default function Clash_Producer() {
|
||||
'trojan',
|
||||
'wireguard',
|
||||
].includes(proxy.type) ||
|
||||
(proxy.type === 'ss' &&
|
||||
![
|
||||
'aes-128-gcm',
|
||||
'aes-192-gcm',
|
||||
'aes-256-gcm',
|
||||
'aes-128-cfb',
|
||||
'aes-192-cfb',
|
||||
'aes-256-cfb',
|
||||
'aes-128-ctr',
|
||||
'aes-192-ctr',
|
||||
'aes-256-ctr',
|
||||
'rc4-md5',
|
||||
'chacha20-ietf',
|
||||
'xchacha20',
|
||||
'chacha20-ietf-poly1305',
|
||||
'xchacha20-ietf-poly1305',
|
||||
].includes(proxy.cipher)) ||
|
||||
(proxy.type === 'snell' && String(proxy.version) === '4') ||
|
||||
(proxy.type === 'vless' &&
|
||||
(typeof proxy.flow !== 'undefined' ||
|
||||
|
||||
@@ -99,7 +99,7 @@ function trojan(proxy) {
|
||||
if (proxy.network === 'ws') {
|
||||
result.append(`,transport=ws`);
|
||||
result.appendIfPresent(
|
||||
`,path=${proxy['ws-opts'].path}`,
|
||||
`,path=${proxy['ws-opts']?.path}`,
|
||||
'ws-opts.path',
|
||||
);
|
||||
result.appendIfPresent(
|
||||
|
||||
@@ -3,6 +3,7 @@ import { isPresent } from '@/core/proxy-utils/producers/utils';
|
||||
export default function Stash_Producer() {
|
||||
const type = 'ALL';
|
||||
const produce = (proxies) => {
|
||||
// https://stash.wiki/proxy-protocols/proxy-types#shadowsocks
|
||||
return (
|
||||
'proxies:\n' +
|
||||
proxies
|
||||
@@ -22,6 +23,23 @@ export default function Stash_Producer() {
|
||||
'hysteria',
|
||||
'hysteria2',
|
||||
].includes(proxy.type) ||
|
||||
(proxy.type === 'ss' &&
|
||||
![
|
||||
'aes-128-gcm',
|
||||
'aes-192-gcm',
|
||||
'aes-256-gcm',
|
||||
'aes-128-cfb',
|
||||
'aes-192-cfb',
|
||||
'aes-256-cfb',
|
||||
'aes-128-ctr',
|
||||
'aes-192-ctr',
|
||||
'aes-256-ctr',
|
||||
'rc4-md5',
|
||||
'chacha20-ietf',
|
||||
'xchacha20',
|
||||
'chacha20-ietf-poly1305',
|
||||
'xchacha20-ietf-poly1305',
|
||||
].includes(proxy.cipher)) ||
|
||||
(proxy.type === 'snell' &&
|
||||
String(proxy.version) === '4') ||
|
||||
(proxy.type === 'vless' && proxy['reality-opts'])
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import express from '@/vendor/express';
|
||||
import $ from '@/core/app';
|
||||
import migrate from '@/utils/migration';
|
||||
import download from '@/utils/download';
|
||||
|
||||
import registerSubscriptionRoutes from './subscriptions';
|
||||
import registerCollectionRoutes from './collections';
|
||||
@@ -18,8 +20,8 @@ export default function serve() {
|
||||
let port;
|
||||
let host;
|
||||
if ($.env.isNode) {
|
||||
port = eval('process.env.SUB_STORE_BACKEND_API_PORT');
|
||||
host = eval('process.env.SUB_STORE_BACKEND_API_HOST');
|
||||
port = eval('process.env.SUB_STORE_BACKEND_API_PORT') || 3000;
|
||||
host = eval('process.env.SUB_STORE_BACKEND_API_HOST') || '::';
|
||||
}
|
||||
const $app = express({ substore: $, port, host });
|
||||
// register routes
|
||||
@@ -37,4 +39,120 @@ export default function serve() {
|
||||
registerMiscRoutes($app);
|
||||
|
||||
$app.start();
|
||||
|
||||
if ($.env.isNode) {
|
||||
const path = eval(`require("path")`);
|
||||
const fs = eval(`require("fs")`);
|
||||
const data_url = eval('process.env.SUB_STORE_DATA_URL');
|
||||
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 =
|
||||
eval('process.env.SUB_STORE_FRONTEND_HOST') || host || '::';
|
||||
const fe_path = eval('process.env.SUB_STORE_FRONTEND_PATH');
|
||||
const fe_abs_path = path.resolve(
|
||||
fe_path || path.join(__dirname, 'frontend'),
|
||||
);
|
||||
if (fe_path) {
|
||||
try {
|
||||
fs.accessSync(path.join(fe_abs_path, 'index.html'));
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`[FRONTEND] index.html file not found in ${fe_abs_path}`,
|
||||
);
|
||||
}
|
||||
|
||||
const express_ = eval(`require("express")`);
|
||||
const history = eval(`require("connect-history-api-fallback")`);
|
||||
const { createProxyMiddleware } = eval(
|
||||
`require("http-proxy-middleware")`,
|
||||
);
|
||||
|
||||
const app = express_();
|
||||
|
||||
const staticFileMiddleware = express_.static(fe_path);
|
||||
|
||||
let be_api_rewrite = '';
|
||||
let be_download_rewrite = '';
|
||||
let be_api = '/api/';
|
||||
let be_download = '/download/';
|
||||
if (fe_be_path) {
|
||||
if (!fe_be_path.startsWith('/')) {
|
||||
throw new Error(
|
||||
'SUB_STORE_FRONTEND_BACKEND_PATH should start with /',
|
||||
);
|
||||
}
|
||||
be_api_rewrite = `${
|
||||
fe_be_path === '/' ? '' : fe_be_path
|
||||
}${be_api}`;
|
||||
be_download_rewrite = `${
|
||||
fe_be_path === '/' ? '' : fe_be_path
|
||||
}${be_download}`;
|
||||
app.use(
|
||||
be_api_rewrite,
|
||||
createProxyMiddleware({
|
||||
target: `http://127.0.0.1:${port}`,
|
||||
changeOrigin: true,
|
||||
pathRewrite: (path) => {
|
||||
return path.startsWith(be_api_rewrite)
|
||||
? path.replace(be_api_rewrite, be_api)
|
||||
: path;
|
||||
},
|
||||
}),
|
||||
);
|
||||
app.use(
|
||||
be_download_rewrite,
|
||||
createProxyMiddleware({
|
||||
target: `http://127.0.0.1:${port}`,
|
||||
changeOrigin: true,
|
||||
pathRewrite: (path) => {
|
||||
return path.startsWith(be_download_rewrite)
|
||||
? path.replace(be_download_rewrite, be_download)
|
||||
: path;
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
app.use(staticFileMiddleware);
|
||||
app.use(
|
||||
history({
|
||||
disableDotRule: true,
|
||||
verbose: false,
|
||||
}),
|
||||
);
|
||||
app.use(staticFileMiddleware);
|
||||
|
||||
const listener = app.listen(fe_port, fe_host, () => {
|
||||
const { address: fe_address, port: fe_port } =
|
||||
listener.address();
|
||||
$.info(`[FRONTEND] ${fe_address}:${fe_port}`);
|
||||
if (fe_be_path) {
|
||||
$.info(
|
||||
`[FRONTEND -> BACKEND] ${fe_address}:${fe_port}${be_api_rewrite} -> http://127.0.0.1:${port}${be_api}`,
|
||||
);
|
||||
$.info(
|
||||
`[FRONTEND -> BACKEND] ${fe_address}:${fe_port}${be_download_rewrite} -> http://127.0.0.1:${port}${be_download}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (data_url) {
|
||||
$.info(`[BACKEND] downloading data from ${data_url}`);
|
||||
download(data_url)
|
||||
.then((content) => {
|
||||
$.write(content, '#sub-store');
|
||||
|
||||
$.cache = JSON.parse(content);
|
||||
$.persistCache();
|
||||
|
||||
migrate();
|
||||
$.info(`[BACKEND] restored data from ${data_url}`);
|
||||
})
|
||||
.catch((e) => {
|
||||
$.error(`[BACKEND] restore data failed`);
|
||||
console.error(e);
|
||||
throw e;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
4
backend/src/vendor/express.js
vendored
4
backend/src/vendor/express.js
vendored
@@ -2,8 +2,6 @@
|
||||
import { ENV } from './open-api';
|
||||
|
||||
export default function express({ substore: $, port, host }) {
|
||||
port = port || 3000;
|
||||
host = host || '::';
|
||||
const { isNode } = ENV();
|
||||
const DEFAULT_HEADERS = {
|
||||
'Content-Type': 'text/plain;charset=UTF-8',
|
||||
@@ -32,7 +30,7 @@ export default function express({ substore: $, port, host }) {
|
||||
app.start = () => {
|
||||
const listener = app.listen(port, host, () => {
|
||||
const { address, port } = listener.address();
|
||||
$.info(`Express started on ${address}:${port}`);
|
||||
$.info(`[BACKEND] ${address}:${port}`);
|
||||
});
|
||||
};
|
||||
return app;
|
||||
|
||||
40
package-lock.json
generated
40
package-lock.json
generated
@@ -1,40 +0,0 @@
|
||||
{
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"axios": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
|
||||
"integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
|
||||
"requires": {
|
||||
"follow-redirects": "1.5.10"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
|
||||
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
|
||||
"requires": {
|
||||
"debug": "=3.1.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user