Compare commits

...

4 Commits

Author SHA1 Message Date
xream
64b9505035 feat: token 唯一性检测增加 type 和 name 2025-06-09 18:42:35 +08:00
xream
b0347637bc feat: SOCKS5 解析去除密码首尾双引号 2025-06-09 12:37:32 +08:00
xream
ab67ce9f5a feat: ProxyUtils 新增 JSON5 2025-06-05 11:01:41 +08:00
xream
cacc106c68 doc: README 2025-06-03 16:42:01 +08:00
10 changed files with 48 additions and 58 deletions

0
.gitmodules vendored
View File

View File

@@ -42,7 +42,7 @@ Core functionalities:
- [x] Clash.Meta (Direct, SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard, Hysteria, Hysteria 2, TUIC, SSH, mieru, AnyTLS)
- [x] Stash (SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard, Hysteria, TUIC, Juicity, SSH)
Deprecated:
Deprecated(The frontend doesn't show it, but the backend still supports it, with the query parameter `target=Clash`):
- [x] Clash (SS, SSR, VMess, Trojan, HTTP, SOCKS5, Snell, VLESS, WireGuard)

View File

@@ -1,6 +1,6 @@
{
"name": "sub-store",
"version": "2.19.60",
"version": "2.19.63",
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
"main": "src/main.js",
"scripts": {
@@ -27,17 +27,18 @@
"automerge": "1.0.1-preview.7",
"body-parser": "^1.19.0",
"buffer": "^6.0.3",
"dotenv": "^16.4.7",
"connect-history-api-fallback": "^2.0.0",
"cron": "^3.1.6",
"dns-packet": "^5.6.1",
"dotenv": "^16.4.7",
"express": "^4.17.1",
"mime-types": "^2.1.35",
"http-proxy-middleware": "^3.0.3",
"ip-address": "^9.0.5",
"js-base64": "^3.7.2",
"json5": "^2.2.3",
"jsrsasign": "^11.1.0",
"lodash": "^4.17.21",
"mime-types": "^2.1.35",
"ms": "^2.1.3",
"nanoid": "^3.3.3",
"semver": "^7.6.3",

15
backend/pnpm-lock.yaml generated
View File

@@ -34,6 +34,9 @@ importers:
dns-packet:
specifier: ^5.6.1
version: 5.6.1
dotenv:
specifier: ^16.4.7
version: 16.5.0
express:
specifier: ^4.17.1
version: 4.21.2
@@ -46,12 +49,18 @@ importers:
js-base64:
specifier: ^3.7.2
version: 3.7.7
json5:
specifier: ^2.2.3
version: 2.2.3
jsrsasign:
specifier: ^11.1.0
version: 11.1.0
lodash:
specifier: ^4.17.21
version: 4.17.21
mime-types:
specifier: ^2.1.35
version: 2.1.35
ms:
specifier: ^2.1.3
version: 2.1.3
@@ -1655,6 +1664,10 @@ packages:
resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==}
engines: {node: '>=0.4', npm: '>=1.2'}
dotenv@16.5.0:
resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==}
engines: {node: '>=12'}
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
@@ -6057,6 +6070,8 @@ snapshots:
domain-browser@1.2.0: {}
dotenv@16.5.0: {}
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.1

View File

@@ -25,6 +25,7 @@ import { getFlag, removeFlag, getISO, MMDB } from '@/utils/geo';
import Gist from '@/utils/gist';
import { isPresent } from './producers/utils';
import { doh } from '@/utils/dns';
import JSON5 from 'json5';
function preprocess(raw) {
for (const processor of PROXY_PREPROCESSORS) {
@@ -347,6 +348,7 @@ export const ProxyUtils = {
doh,
Buffer,
Base64,
JSON5,
};
function tryParse(parser, line) {

View File

@@ -180,7 +180,7 @@ username = & {
return true;
}
} { proxy.username = $.username; }
password = comma match:[^,]+ { proxy.password = match.join(""); }
password = comma match:[^,]+ { proxy.password = match.join("").replace(/^"(.*)"$/, '$1'); }
tls = comma "tls" equals flag:bool { proxy.tls = flag; }
sni = comma "sni" equals sni:("off"/domain) {

View File

@@ -177,7 +177,7 @@ username = & {
return true;
}
} { proxy.username = $.username; }
password = comma match:[^,]+ { proxy.password = match.join(""); }
password = comma match:[^,]+ { proxy.password = match.join("").replace(/^"(.*)"$/, '$1'); }
tls = comma "tls" equals flag:bool { proxy.tls = flag; }
sni = comma "sni" equals sni:("off"/domain) {

View File

@@ -53,6 +53,16 @@ async function signToken(req, res) {
try {
const { payload, options } = req.body;
const ms = eval(`require("ms")`);
const type = payload?.type;
const name = payload?.name;
if (!type || !name)
return failed(
res,
new RequestInvalidError(
'INVALID_PAYLOAD',
`payload type and name are required`,
),
);
let token = payload?.token;
if (token != null) {
if (typeof token !== 'string' || token.length < 1) {
@@ -65,7 +75,12 @@ async function signToken(req, res) {
);
}
const tokens = $.read(TOKENS_KEY) || [];
if (tokens.find((t) => t.token === token)) {
if (
tokens.find(
(t) =>
t.token === token && t.type === type && t.name === name,
)
) {
return failed(
res,
new RequestInvalidError(
@@ -75,16 +90,7 @@ async function signToken(req, res) {
);
}
}
const type = payload?.type;
const name = payload?.name;
if (!type || !name)
return failed(
res,
new RequestInvalidError(
'INVALID_PAYLOAD',
`payload type and name are required`,
),
);
if (type === 'col') {
const collections = $.read(COLLECTIONS_KEY) || [];
const collection = collections.find((c) => c.name === name);
@@ -153,7 +159,12 @@ async function signToken(req, res) {
if (!token) {
do {
token = nanoid.customAlphabet(nanoid.urlAlphabet)();
} while (tokens.find((t) => t.token === token));
} while (
tokens.find(
(t) =>
t.token === token && t.type === type && t.name === name,
)
);
}
tokens.push({
...payload,

View File

@@ -1,40 +0,0 @@
upstream api {
server 0.0.0.0:3000;
}
server {
listen 6080;
# allow 127.0.0.1;
# allow 0.0.0.0;
# deny all;
gzip on;
gzip_static on;
gzip_types text/plain application/json application/javascript application/x-javascript text/css application/xml text/javascript;
gzip_proxied any;
gzip_vary on;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.0;
location / {
root /Sub-Store/web/dist;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://api;
}
location /download {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://api;
}
}

View File

@@ -133,6 +133,7 @@ function operator(proxies = [], targetPlatform, context) {
// isValidUUID, // 辅助判断是否为有效的 UUID
// Buffer, // https://github.com/feross/buffer
// Base64, // https://github.com/dankogai/js-base64
// JSON5, // https://github.com/json5/json5
// }
// 为兼容 https://github.com/xishang0128/sparkle 的 JavaScript 覆写, 也可以直接使用 `b64d`(Base64 解码), `b64e`(Base64 编码), `Buffer`, `yaml`(简单兼容了下 `yaml.parse` 和 `yaml.stringify`)