import type { XraySettingsValue } from '@/hooks/useXraySetting'; import { blockedSettings, directSettings } from './constants'; export function ruleGetter(t: XraySettingsValue | null, outboundTag: string, property: string): string[] { if (!t?.routing?.rules) return []; const out: string[] = []; for (const rule of t.routing.rules) { if ( rule && Object.prototype.hasOwnProperty.call(rule, property) && Object.prototype.hasOwnProperty.call(rule, 'outboundTag') && rule.outboundTag === outboundTag ) { const v = (rule as Record)[property]; if (Array.isArray(v)) out.push(...(v as string[])); } } return out; } export function ruleSetter(t: XraySettingsValue, outboundTag: string, property: string, data: string[]): void { if (!t.routing) return; if (!Array.isArray(t.routing.rules)) t.routing.rules = []; const current = ruleGetter(t, outboundTag, property); if (current.length === 0) { t.routing.rules.push({ type: 'field', outboundTag, [property]: data }); return; } const next: typeof t.routing.rules = []; let inserted = false; for (const rule of t.routing.rules) { const matches = rule && Object.prototype.hasOwnProperty.call(rule, property) && Object.prototype.hasOwnProperty.call(rule, 'outboundTag') && rule.outboundTag === outboundTag; if (matches) { if (!inserted && data.length > 0) { (rule as Record)[property] = data; next.push(rule); inserted = true; } } else { next.push(rule); } } t.routing.rules = next; } export function syncOutbound(t: XraySettingsValue, tag: string, settings: Record) { if (!t.outbounds || !t.routing) return; const rules = t.routing.rules || []; const haveRules = rules.some((r) => r?.outboundTag === tag); const idx = t.outbounds.findIndex((o) => o?.tag === tag); if (!haveRules && idx > 0) t.outbounds.splice(idx, 1); if (haveRules && idx < 0) t.outbounds.push(settings as never); } export function getDefaultOutboundTag(t: XraySettingsValue | null): string { const tag = t?.outbounds?.[0]?.tag; return typeof tag === 'string' && tag.length > 0 ? tag : 'direct'; } export function setDefaultOutboundTag(t: XraySettingsValue, tag: string): void { if (!tag) return; if (!Array.isArray(t.outbounds)) t.outbounds = []; const idx = t.outbounds.findIndex((o) => o?.tag === tag); if (idx < 0) { if (tag === 'direct') t.outbounds.push(directSettings as never); else if (tag === 'blocked') t.outbounds.push(blockedSettings as never); else return; const newIdx = t.outbounds.length - 1; const [moved] = t.outbounds.splice(newIdx, 1); t.outbounds.unshift(moved); } else if (idx > 0) { const [moved] = t.outbounds.splice(idx, 1); t.outbounds.unshift(moved); } } export function propagateOutboundTagRename( t: XraySettingsValue, oldTag: string, newTag: string, ): void { if (!oldTag || !newTag || oldTag === newTag) return; const rules = t.routing?.rules; if (Array.isArray(rules)) { for (const rule of rules) { if (rule?.outboundTag === oldTag) rule.outboundTag = newTag; } } const balancers = t.routing?.balancers; if (Array.isArray(balancers)) { for (const balancer of balancers) { if (balancer?.fallbackTag === oldTag) balancer.fallbackTag = newTag; if (Array.isArray(balancer?.selector)) { balancer.selector = balancer.selector.map((sel) => (sel === oldTag ? newTag : sel)); } } } if (Array.isArray(t.outbounds)) { for (const outbound of t.outbounds) { const sockopt = (outbound as { streamSettings?: { sockopt?: { dialerProxy?: string } } }) ?.streamSettings?.sockopt; if (sockopt?.dialerProxy === oldTag) sockopt.dialerProxy = newTag; } } }