diff --git a/frontend/public/openapi.json b/frontend/public/openapi.json index c33733fe1..8f014b715 100644 --- a/frontend/public/openapi.json +++ b/frontend/public/openapi.json @@ -120,8 +120,8 @@ "minimum": 0, "type": "integer" }, - "panelProxy": { - "description": "Proxy URL for the panel's own outbound requests (GitHub/Telegram)", + "panelOutbound": { + "description": "Xray outbound tag for the panel's own outbound HTTP (update checks/downloads, Telegram, geo updates, outbound-subscription fetches)", "type": "string" }, "remarkModel": { @@ -383,7 +383,7 @@ "ldapUserFilter", "ldapVlessField", "pageSize", - "panelProxy", + "panelOutbound", "remarkModel", "restartXrayOnClientDisable", "sessionMaxAge", @@ -554,8 +554,8 @@ "minimum": 0, "type": "integer" }, - "panelProxy": { - "description": "Proxy URL for the panel's own outbound requests (GitHub/Telegram)", + "panelOutbound": { + "description": "Xray outbound tag for the panel's own outbound HTTP (update checks/downloads, Telegram, geo updates, outbound-subscription fetches)", "type": "string" }, "remarkModel": { @@ -823,7 +823,7 @@ "ldapUserFilter", "ldapVlessField", "pageSize", - "panelProxy", + "panelOutbound", "remarkModel", "restartXrayOnClientDisable", "sessionMaxAge", diff --git a/frontend/src/generated/examples.ts b/frontend/src/generated/examples.ts index bb5c1b864..fc406257d 100644 --- a/frontend/src/generated/examples.ts +++ b/frontend/src/generated/examples.ts @@ -26,7 +26,7 @@ export const EXAMPLES: Record = { "ldapUserFilter": "", "ldapVlessField": "", "pageSize": 0, - "panelProxy": "", + "panelOutbound": "", "remarkModel": "", "restartXrayOnClientDisable": false, "sessionMaxAge": 1, @@ -115,7 +115,7 @@ export const EXAMPLES: Record = { "ldapUserFilter": "", "ldapVlessField": "", "pageSize": 0, - "panelProxy": "", + "panelOutbound": "", "remarkModel": "", "restartXrayOnClientDisable": false, "sessionMaxAge": 1, diff --git a/frontend/src/generated/schemas.ts b/frontend/src/generated/schemas.ts index 485f6afaf..7ff2a4aeb 100644 --- a/frontend/src/generated/schemas.ts +++ b/frontend/src/generated/schemas.ts @@ -94,8 +94,8 @@ export const SCHEMAS: Record = { "minimum": 0, "type": "integer" }, - "panelProxy": { - "description": "Proxy URL for the panel's own outbound requests (GitHub/Telegram)", + "panelOutbound": { + "description": "Xray outbound tag for the panel's own outbound HTTP (update checks/downloads, Telegram, geo updates, outbound-subscription fetches)", "type": "string" }, "remarkModel": { @@ -357,7 +357,7 @@ export const SCHEMAS: Record = { "ldapUserFilter", "ldapVlessField", "pageSize", - "panelProxy", + "panelOutbound", "remarkModel", "restartXrayOnClientDisable", "sessionMaxAge", @@ -528,8 +528,8 @@ export const SCHEMAS: Record = { "minimum": 0, "type": "integer" }, - "panelProxy": { - "description": "Proxy URL for the panel's own outbound requests (GitHub/Telegram)", + "panelOutbound": { + "description": "Xray outbound tag for the panel's own outbound HTTP (update checks/downloads, Telegram, geo updates, outbound-subscription fetches)", "type": "string" }, "remarkModel": { @@ -797,7 +797,7 @@ export const SCHEMAS: Record = { "ldapUserFilter", "ldapVlessField", "pageSize", - "panelProxy", + "panelOutbound", "remarkModel", "restartXrayOnClientDisable", "sessionMaxAge", diff --git a/frontend/src/generated/types.ts b/frontend/src/generated/types.ts index 842cf6cfd..e32ed7cf8 100644 --- a/frontend/src/generated/types.ts +++ b/frontend/src/generated/types.ts @@ -30,7 +30,7 @@ export interface AllSetting { ldapUserFilter: string; ldapVlessField: string; pageSize: number; - panelProxy: string; + panelOutbound: string; remarkModel: string; restartXrayOnClientDisable: boolean; sessionMaxAge: number; @@ -120,7 +120,7 @@ export interface AllSettingView { ldapUserFilter: string; ldapVlessField: string; pageSize: number; - panelProxy: string; + panelOutbound: string; remarkModel: string; restartXrayOnClientDisable: boolean; sessionMaxAge: number; diff --git a/frontend/src/generated/zod.ts b/frontend/src/generated/zod.ts index cf2eb0e4e..436d9f005 100644 --- a/frontend/src/generated/zod.ts +++ b/frontend/src/generated/zod.ts @@ -38,7 +38,7 @@ export const AllSettingSchema = z.object({ ldapUserFilter: z.string(), ldapVlessField: z.string(), pageSize: z.number().int().min(0).max(1000), - panelProxy: z.string(), + panelOutbound: z.string(), remarkModel: z.string(), restartXrayOnClientDisable: z.boolean(), sessionMaxAge: z.number().int().min(1).max(525600), @@ -129,7 +129,7 @@ export const AllSettingViewSchema = z.object({ ldapUserFilter: z.string(), ldapVlessField: z.string(), pageSize: z.number().int().min(0).max(1000), - panelProxy: z.string(), + panelOutbound: z.string(), remarkModel: z.string(), restartXrayOnClientDisable: z.boolean(), sessionMaxAge: z.number().int().min(1).max(525600), diff --git a/frontend/src/models/setting.ts b/frontend/src/models/setting.ts index f2d199070..33f5b93b5 100644 --- a/frontend/src/models/setting.ts +++ b/frontend/src/models/setting.ts @@ -9,7 +9,7 @@ export class AllSetting { webBasePath = '/'; sessionMaxAge = 360; trustedProxyCIDRs = '127.0.0.1/32,::1/128'; - panelProxy = ''; + panelOutbound = ''; pageSize = 25; expireDiff = 0; trafficDiff = 0; diff --git a/frontend/src/pages/index/GeodataSection.tsx b/frontend/src/pages/index/GeodataSection.tsx index 5facc1d8c..b41ae5970 100644 --- a/frontend/src/pages/index/GeodataSection.tsx +++ b/frontend/src/pages/index/GeodataSection.tsx @@ -65,10 +65,14 @@ export default function GeodataSection({ active, onBusy, onClose }: GeodataSecti ); // Download outbound candidates: template outbounds + subscription outbounds. + // Skip blackhole outbounds — routing a download through one just drops it. const tags = new Set(); const outbounds = Array.isArray(template.outbounds) ? template.outbounds : []; for (const o of outbounds) { - const tag = o && typeof o === 'object' ? (o as Record).tag : undefined; + if (!o || typeof o !== 'object') continue; + const rec = o as Record; + if (rec.protocol === 'blackhole') continue; + const tag = rec.tag; if (typeof tag === 'string' && tag) tags.add(tag); } const subTags = Array.isArray(payload.subscriptionOutboundTags) diff --git a/frontend/src/pages/settings/GeneralTab.tsx b/frontend/src/pages/settings/GeneralTab.tsx index 16ca8ca13..bcf2150ec 100644 --- a/frontend/src/pages/settings/GeneralTab.tsx +++ b/frontend/src/pages/settings/GeneralTab.tsx @@ -43,6 +43,7 @@ export default function GeneralTab({ allSetting, updateSetting }: GeneralTabProp const [lang, setLang] = useState(() => LanguageManager.getLanguage()); const [inboundOptions, setInboundOptions] = useState<{ label: string; value: string }[]>([]); + const [outboundOptions, setOutboundOptions] = useState<{ label: string; value: string }[]>([]); useEffect(() => { let cancelled = false; @@ -65,6 +66,38 @@ export default function GeneralTab({ allSetting, updateSetting }: GeneralTabProp return () => { cancelled = true; }; }, []); + useEffect(() => { + let cancelled = false; + (async () => { + // Outbound tags for the panel egress picker: template outbounds plus + // subscription-derived outbounds, same candidate set as the geodata + // download picker. + const msg = await HttpUtil.post('/panel/api/xray/', undefined, { silent: true }) as ApiMsg; + if (cancelled || !msg?.success || typeof msg.obj !== 'string') return; + try { + const payload = JSON.parse(msg.obj) as Record; + const template = (payload.xraySetting || {}) as Record; + const tags = new Set(); + const outbounds = Array.isArray(template.outbounds) ? template.outbounds : []; + for (const o of outbounds) { + if (!o || typeof o !== 'object') continue; + const rec = o as Record; + if (rec.protocol === 'blackhole') continue; // dropping traffic is never a useful egress + const tag = rec.tag; + if (typeof tag === 'string' && tag) tags.add(tag); + } + const subTags = Array.isArray(payload.subscriptionOutboundTags) ? payload.subscriptionOutboundTags : []; + for (const tag of subTags) { + if (typeof tag === 'string' && tag) tags.add(tag); + } + setOutboundOptions([...tags].map((tag) => ({ label: tag, value: tag }))); + } catch { + setOutboundOptions([]); + } + })(); + return () => { cancelled = true; }; + }, []); + const ldapInboundTagList = useMemo(() => { const csv = allSetting.ldapInboundTags || ''; return csv.length ? csv.split(',').map((s) => s.trim()).filter(Boolean) : []; @@ -133,11 +166,15 @@ export default function GeneralTab({ allSetting, updateSetting }: GeneralTabProp /> - - updateSetting({ panelProxy: e.target.value })} + +