feat: fix ss
This commit is contained in:
@@ -6,6 +6,12 @@ export interface ParsedNode {
|
|||||||
surgeLine: string;
|
surgeLine: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Surge 使用与标准 Shadowsocks 不同的加密方式命名,需做别名映射
|
||||||
|
const ENCRYPT_METHOD_ALIASES: Record<string, string> = {
|
||||||
|
'chacha20-poly1305': 'chacha20-ietf-poly1305',
|
||||||
|
'xchacha20-poly1305': 'xchacha20-ietf-poly1305',
|
||||||
|
};
|
||||||
|
|
||||||
export function parseSS(uri: string): ParsedNode {
|
export function parseSS(uri: string): ParsedNode {
|
||||||
const url = new URL(uri);
|
const url = new URL(uri);
|
||||||
const name = decodeURIComponent(url.hash.slice(1));
|
const name = decodeURIComponent(url.hash.slice(1));
|
||||||
@@ -14,11 +20,18 @@ export function parseSS(uri: string): ParsedNode {
|
|||||||
|
|
||||||
const decoded = Buffer.from(url.username, 'base64').toString();
|
const decoded = Buffer.from(url.username, 'base64').toString();
|
||||||
const firstColon = decoded.indexOf(':');
|
const firstColon = decoded.indexOf(':');
|
||||||
const method = decoded.slice(0, firstColon);
|
const rawMethod = decoded.slice(0, firstColon);
|
||||||
|
const method = ENCRYPT_METHOD_ALIASES[rawMethod] ?? rawMethod;
|
||||||
const password = decoded.slice(firstColon + 1);
|
const password = decoded.slice(firstColon + 1);
|
||||||
|
|
||||||
// SS 2022 has built-in encryption, Surge does not support SS over TLS
|
// SS 2022 has built-in encryption, Surge does not support SS over TLS
|
||||||
const surgeLine = `${name} = ss, ${server}, ${port}, encrypt-method=${method}, password=${password}`;
|
const surgeLine = `${name} = ss, ${server}, ${port}, encrypt-method=${method}, password=${password}`;
|
||||||
|
|
||||||
return { name, type: 'ss', server, port, surgeLine };
|
// Surge's `ss` proxy type has no WebSocket/TLS transport support at all (unlike
|
||||||
|
// trojan/vmess/vless) — this Xray-style ss+ws+tls link can never work in Surge,
|
||||||
|
// so it's tagged 'ss-ws' and excluded from Surge output (see generator.ts).
|
||||||
|
// Clash/SSR generators re-parse the raw uri independently and are unaffected.
|
||||||
|
const type = url.searchParams.get('type') === 'ws' ? 'ss-ws' : 'ss';
|
||||||
|
|
||||||
|
return { name, type, server, port, surgeLine };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { ParsedNode } from './ss.js';
|
import type { ParsedNode } from './ss.js';
|
||||||
|
import { normalizeWsPath } from './util.js';
|
||||||
|
|
||||||
export function parseTrojan(uri: string): ParsedNode {
|
export function parseTrojan(uri: string): ParsedNode {
|
||||||
const url = new URL(uri);
|
const url = new URL(uri);
|
||||||
@@ -9,7 +10,13 @@ export function parseTrojan(uri: string): ParsedNode {
|
|||||||
const params = url.searchParams;
|
const params = url.searchParams;
|
||||||
const sni = params.get('sni') || server;
|
const sni = params.get('sni') || server;
|
||||||
|
|
||||||
const surgeLine = `${name} = trojan, ${server}, ${port}, password=${password}, tls=true, sni=${sni}, skip-cert-verify=false`;
|
let surgeLine = `${name} = trojan, ${server}, ${port}, password=${password}, tls=true, sni=${sni}, skip-cert-verify=false`;
|
||||||
|
|
||||||
|
if (params.get('type') === 'ws') {
|
||||||
|
const path = params.get('path') || '/';
|
||||||
|
const host = params.get('host') || sni;
|
||||||
|
surgeLine += `, ws=true, ws-path=${normalizeWsPath(path)}, ws-headers=Host:${host}`;
|
||||||
|
}
|
||||||
|
|
||||||
return { name, type: 'trojan', server, port, surgeLine };
|
return { name, type: 'trojan', server, port, surgeLine };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,12 @@ function renameSurgeLine(surgeLine: string, oldName: string, newName: string): s
|
|||||||
return surgeLine;
|
return surgeLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rows = db.prepare('SELECT id, name, uri, surge_line FROM static_nodes').all() as Array<{
|
const rows = db.prepare('SELECT id, name, uri, surge_line, type FROM static_nodes').all() as Array<{
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
uri: string;
|
uri: string;
|
||||||
surge_line: string;
|
surge_line: string;
|
||||||
|
type: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
const update = db.prepare('UPDATE static_nodes SET surge_line = ?, type = ? WHERE id = ?');
|
const update = db.prepare('UPDATE static_nodes SET surge_line = ?, type = ? WHERE id = ?');
|
||||||
@@ -31,7 +32,7 @@ for (const row of rows) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const newLine = renameSurgeLine(node.surgeLine, node.name, row.name);
|
const newLine = renameSurgeLine(node.surgeLine, node.name, row.name);
|
||||||
if (newLine !== row.surge_line) {
|
if (newLine !== row.surge_line || node.type !== row.type) {
|
||||||
update.run(newLine, node.type, row.id);
|
update.run(newLine, node.type, row.id);
|
||||||
changed++;
|
changed++;
|
||||||
console.log(`[updated] id=${row.id} "${row.name}"`);
|
console.log(`[updated] id=${row.id} "${row.name}"`);
|
||||||
|
|||||||
@@ -15,18 +15,21 @@ export function generateSurgeConfig(hostUrl: string, whitelist?: Set<string>): s
|
|||||||
return '# No subscription config available. Add and fetch a subscription first.';
|
return '# No subscription config available. Add and fetch a subscription first.';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect fetched nodes (exclude vless — Surge doesn't support it; tuic uses `tuic-v5`)
|
// Exclude vless and ss-ws (Surge doesn't support either transport)
|
||||||
|
const UNSUPPORTED = "type NOT IN ('vless', 'ss-ws')";
|
||||||
|
|
||||||
|
// Collect fetched nodes (tuic uses `tuic-v5`)
|
||||||
const fetchedNodes = db.prepare(
|
const fetchedNodes = db.prepare(
|
||||||
whitelist
|
whitelist
|
||||||
? "SELECT surge_line FROM fetched_nodes WHERE type != 'vless' ORDER BY subscription_id, sort_order, id"
|
? `SELECT surge_line FROM fetched_nodes WHERE ${UNSUPPORTED} ORDER BY subscription_id, sort_order, id`
|
||||||
: "SELECT surge_line FROM fetched_nodes WHERE enabled = 1 AND type != 'vless' ORDER BY subscription_id, sort_order, id"
|
: `SELECT surge_line FROM fetched_nodes WHERE enabled = 1 AND ${UNSUPPORTED} ORDER BY subscription_id, sort_order, id`
|
||||||
).all() as any[];
|
).all() as any[];
|
||||||
|
|
||||||
// Collect static nodes (these go FIRST, exclude vless)
|
// Collect static nodes (these go FIRST)
|
||||||
const staticNodes = db.prepare(
|
const staticNodes = db.prepare(
|
||||||
whitelist
|
whitelist
|
||||||
? "SELECT surge_line FROM static_nodes WHERE type != 'vless' ORDER BY sort_order, id"
|
? `SELECT surge_line FROM static_nodes WHERE ${UNSUPPORTED} ORDER BY sort_order, id`
|
||||||
: "SELECT surge_line FROM static_nodes WHERE enabled = 1 AND type != 'vless' ORDER BY sort_order, id"
|
: `SELECT surge_line FROM static_nodes WHERE enabled = 1 AND ${UNSUPPORTED} ORDER BY sort_order, id`
|
||||||
).all() as any[];
|
).all() as any[];
|
||||||
|
|
||||||
// Collect enabled rules
|
// Collect enabled rules
|
||||||
|
|||||||
Reference in New Issue
Block a user