From 6b4285bc82eb06827fda4f45729cb49ff57f70fb Mon Sep 17 00:00:00 2001 From: YANG JIANKUAN Date: Tue, 21 Jul 2026 18:09:25 +0800 Subject: [PATCH] feat: fix ss --- server/src/parsers/ss.ts | 17 +++++++++++++++-- server/src/parsers/trojan.ts | 9 ++++++++- server/src/scripts/regen-surge-lines.ts | 5 +++-- server/src/services/generator.ts | 15 +++++++++------ 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/server/src/parsers/ss.ts b/server/src/parsers/ss.ts index a146128..0c4ab9c 100644 --- a/server/src/parsers/ss.ts +++ b/server/src/parsers/ss.ts @@ -6,6 +6,12 @@ export interface ParsedNode { surgeLine: string; } +// Surge 使用与标准 Shadowsocks 不同的加密方式命名,需做别名映射 +const ENCRYPT_METHOD_ALIASES: Record = { + 'chacha20-poly1305': 'chacha20-ietf-poly1305', + 'xchacha20-poly1305': 'xchacha20-ietf-poly1305', +}; + export function parseSS(uri: string): ParsedNode { const url = new URL(uri); 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 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); // SS 2022 has built-in encryption, Surge does not support SS over TLS 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 }; } diff --git a/server/src/parsers/trojan.ts b/server/src/parsers/trojan.ts index 9e94909..532a121 100644 --- a/server/src/parsers/trojan.ts +++ b/server/src/parsers/trojan.ts @@ -1,4 +1,5 @@ import type { ParsedNode } from './ss.js'; +import { normalizeWsPath } from './util.js'; export function parseTrojan(uri: string): ParsedNode { const url = new URL(uri); @@ -9,7 +10,13 @@ export function parseTrojan(uri: string): ParsedNode { const params = url.searchParams; 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 }; } diff --git a/server/src/scripts/regen-surge-lines.ts b/server/src/scripts/regen-surge-lines.ts index 095f158..6f7118d 100644 --- a/server/src/scripts/regen-surge-lines.ts +++ b/server/src/scripts/regen-surge-lines.ts @@ -14,11 +14,12 @@ function renameSurgeLine(surgeLine: string, oldName: string, newName: string): s 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; name: string; uri: string; surge_line: string; + type: string; }>; const update = db.prepare('UPDATE static_nodes SET surge_line = ?, type = ? WHERE id = ?'); @@ -31,7 +32,7 @@ for (const row of rows) { continue; } 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); changed++; console.log(`[updated] id=${row.id} "${row.name}"`); diff --git a/server/src/services/generator.ts b/server/src/services/generator.ts index 5e1a3d1..81e654a 100644 --- a/server/src/services/generator.ts +++ b/server/src/services/generator.ts @@ -15,18 +15,21 @@ export function generateSurgeConfig(hostUrl: string, whitelist?: Set): s 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( whitelist - ? "SELECT surge_line FROM fetched_nodes WHERE type != 'vless' 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 ${UNSUPPORTED} 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[]; - // Collect static nodes (these go FIRST, exclude vless) + // Collect static nodes (these go FIRST) const staticNodes = db.prepare( whitelist - ? "SELECT surge_line FROM static_nodes WHERE type != 'vless' 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 ${UNSUPPORTED} ORDER BY sort_order, id` + : `SELECT surge_line FROM static_nodes WHERE enabled = 1 AND ${UNSUPPORTED} ORDER BY sort_order, id` ).all() as any[]; // Collect enabled rules