feat: 支持自定义订阅节点并兼容现有数据库
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
import { parse, stringify } from "yaml";
|
||||
import {
|
||||
parseCustomProxyYaml,
|
||||
renderCustomProxyYaml,
|
||||
extractProxyNameFromYaml,
|
||||
} from "../../lib/custom-proxy";
|
||||
import { buildSubscriptionConfig } from "../../lib/subscription";
|
||||
import { encryptText } from "../../lib/crypto";
|
||||
import type { Hy2NodeRow, Hy2UserRow } from "../../lib/store";
|
||||
|
||||
const proxy = {
|
||||
name: 'HK "one": #香港',
|
||||
type: "anytls",
|
||||
server: "hk.example.com",
|
||||
port: 443,
|
||||
password: 'test:p"ass#word',
|
||||
udp: true,
|
||||
alpn: ["h2", "http/1.1"],
|
||||
};
|
||||
for (const [name, source] of Object.entries({
|
||||
object: stringify(proxy),
|
||||
sequence: stringify([proxy]),
|
||||
wrapper: stringify({ proxies: [proxy] }),
|
||||
flow: JSON.stringify(proxy),
|
||||
})) {
|
||||
test(`accepts ${name} and preserves protocol fields and quoted names on output`, () => {
|
||||
assert.deepEqual(parseCustomProxyYaml(source), proxy);
|
||||
assert.equal(extractProxyNameFromYaml(source), proxy.name);
|
||||
assert.deepEqual(parse(renderCustomProxyYaml(source)), [proxy]);
|
||||
});
|
||||
}
|
||||
for (const value of [
|
||||
"",
|
||||
"[]",
|
||||
"[a, b]",
|
||||
"- name: [bad",
|
||||
"name: X\nname: Y\ntype: anytls",
|
||||
"name: 123\ntype: anytls",
|
||||
"name: X",
|
||||
"name: X\ntype: anytls\npassword: *missing",
|
||||
"proxies:\n - name: X\n type: anytls\n - name: Y\n type: anytls",
|
||||
]) {
|
||||
test(`rejects malformed or multiple proxies: ${JSON.stringify(value).slice(0, 40)}`, () =>
|
||||
assert.throws(() => parseCustomProxyYaml(value)));
|
||||
}
|
||||
|
||||
test("mixed subscriptions preserve order, names, group references, credentials and disabled filtering", () => {
|
||||
const custom = {
|
||||
id: 2,
|
||||
slug: "custom",
|
||||
node_type: "custom",
|
||||
name: "Display name",
|
||||
client_name: "Old fallback",
|
||||
custom_config_yaml: stringify({ proxies: [proxy] }),
|
||||
enabled: 1,
|
||||
} as Hy2NodeRow;
|
||||
const hy2 = {
|
||||
id: 1,
|
||||
slug: "hy2",
|
||||
node_type: "hy2",
|
||||
name: "Tokyo",
|
||||
client_name: 'Tokyo "HY2"',
|
||||
server_host: "jp.example.com",
|
||||
server_port: 443,
|
||||
enabled: 1,
|
||||
custom_config_yaml: "",
|
||||
bandwidth_up_mbps: 30,
|
||||
bandwidth_down_mbps: 30,
|
||||
} as Hy2NodeRow;
|
||||
const user = {
|
||||
username: "member",
|
||||
auth_secret_encrypted: encryptText('test:abc"def'),
|
||||
} as Hy2UserRow;
|
||||
const settings = {
|
||||
template: "mode: rule",
|
||||
profileName: 'Profile: "1"',
|
||||
extraProxyGroups: "",
|
||||
mainProxyGroupMemberSlugs: ["custom", "hy2"],
|
||||
proxyGroups: [
|
||||
{
|
||||
id: "work",
|
||||
name: 'Work: "1"',
|
||||
type: "select" as const,
|
||||
memberSlugs: ["custom", "disabled", "hy2"],
|
||||
},
|
||||
],
|
||||
};
|
||||
const result = parse(
|
||||
buildSubscriptionConfig({
|
||||
user,
|
||||
nodes: [hy2, custom, { ...custom, slug: "disabled", enabled: 0 }],
|
||||
settings,
|
||||
}),
|
||||
);
|
||||
assert.deepEqual(result.proxies[0], proxy);
|
||||
assert.equal(result.proxies[1].password, 'member:test:abc"def');
|
||||
assert.equal(result.proxies[1].name, hy2.client_name);
|
||||
assert.deepEqual(
|
||||
result["proxy-groups"].map((group: { proxies: string[] }) => group.proxies),
|
||||
[
|
||||
[proxy.name, hy2.client_name],
|
||||
[proxy.name, hy2.client_name],
|
||||
],
|
||||
);
|
||||
assert.equal(result["proxy-groups"][0].name, settings.profileName);
|
||||
assert.equal(result["proxy-groups"][1].name, settings.proxyGroups[0].name);
|
||||
assert.equal(result.proxies.length, 2);
|
||||
// Custom-only subscriptions do not require HY2 credentials.
|
||||
assert.deepEqual(
|
||||
parse(
|
||||
buildSubscriptionConfig({
|
||||
user: { ...user, auth_secret_encrypted: null },
|
||||
nodes: [custom],
|
||||
settings,
|
||||
}),
|
||||
).proxies,
|
||||
[proxy],
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user