feat: 新增游客订阅链接与节点拖动排序

游客链接:可建多个带独立 token 的订阅,按节点名勾选白名单
(抗重新抓取),/surge|/clash|/ssr 复用同一路径回落匹配 guest
token,三个 generator 接收可选 whitelist 过滤生成。

节点排序:fetched_nodes 新增 sort_order,重抓时按名保留顺序,
选择/节点面板支持原生拖拽排序,输出按此顺序排列。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 23:43:28 +08:00
parent c3d8e1a961
commit fb7a30489e
14 changed files with 737 additions and 44 deletions

View File

@@ -1,6 +1,11 @@
import db from '../db.js';
export function generateSurgeConfig(hostUrl: string): string {
/**
* @param whitelist When provided (guest links), only nodes whose name is in the
* set are included, independent of their global enabled state. When omitted,
* the normal enabled-only behavior applies.
*/
export function generateSurgeConfig(hostUrl: string, whitelist?: Set<string>): string {
// Get first enabled subscription's raw_config as base template
const sub = db.prepare(
'SELECT raw_config FROM subscriptions WHERE enabled = 1 AND raw_config IS NOT NULL ORDER BY id LIMIT 1'
@@ -10,14 +15,18 @@ export function generateSurgeConfig(hostUrl: string): string {
return '# No subscription config available. Add and fetch a subscription first.';
}
// Collect enabled fetched nodes (exclude vless — Surge doesn't support it)
// Collect fetched nodes (exclude vless — Surge doesn't support it; tuic uses `tuic-v5`)
const fetchedNodes = db.prepare(
'SELECT surge_line FROM fetched_nodes WHERE enabled = 1 AND type != \'vless\' ORDER BY subscription_id, id'
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"
).all() as any[];
// Collect enabled static nodes (these go FIRST, exclude vless)
// Collect static nodes (these go FIRST, exclude vless)
const staticNodes = db.prepare(
'SELECT surge_line FROM static_nodes WHERE enabled = 1 AND type != \'vless\' ORDER BY sort_order, id'
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"
).all() as any[];
// Collect enabled rules
@@ -26,8 +35,10 @@ export function generateSurgeConfig(hostUrl: string): string {
).all() as any[];
// Static nodes first, then fetched nodes
const staticLines = staticNodes.map((n: any) => n.surge_line);
const fetchedLines = fetchedNodes.map((n: any) => n.surge_line);
const lineName = (l: string) => l.split(' = ')[0].trim();
const keep = (l: string) => !whitelist || whitelist.has(lineName(l));
const staticLines = staticNodes.map((n: any) => n.surge_line).filter(keep);
const fetchedLines = fetchedNodes.map((n: any) => n.surge_line).filter(keep);
const allNodeLines = [...staticLines, ...fetchedLines];
const allNodeNames = allNodeLines.map((l: string) => l.split(' = ')[0].trim());