feat(outbounds): pick dialerProxy from other outbound tags for proxy chaining

Turn the outbound sockopt dialerProxy free-text input into a searchable Select populated with the other outbound tags, so users can build a proxy chain (route one outbound through another) without typing tags by hand. The list excludes the current outbound, so self-reference cycles cannot be selected. A tooltip and placeholder explain the chaining concept. Adds dialerProxyPlaceholder and dialerProxyHint to all 13 locales.

Closes #4446
This commit is contained in:
MHSanaei
2026-06-02 01:52:38 +02:00
parent 8fa248c621
commit 7bc31dd194
15 changed files with 51 additions and 3 deletions

View File

@@ -575,7 +575,9 @@ export default function OutboundFormModal({
{security === 'reality' && realityAllowed && <RealityForm />}
{((streamAllowed && network) || !streamAllowed) && <SockoptForm form={form} />}
{((streamAllowed && network) || !streamAllowed) && (
<SockoptForm form={form} outboundTags={existingTags} />
)}
<FinalMaskForm
name={['streamSettings', 'finalmask']}

View File

@@ -7,7 +7,13 @@ import type { OutboundFormValues } from '@/schemas/forms/outbound-form';
import { ADDRESS_PORT_STRATEGY_OPTIONS } from '../outbound-form-constants';
export default function SockoptForm({ form }: { form: FormInstance<OutboundFormValues> }) {
export default function SockoptForm({
form,
outboundTags = [],
}: {
form: FormInstance<OutboundFormValues>;
outboundTags?: string[];
}) {
const { t } = useTranslation();
return (
<Form.Item shouldUpdate noStyle>
@@ -16,6 +22,14 @@ export default function SockoptForm({ form }: { form: FormInstance<OutboundFormV
'streamSettings',
'sockopt',
]);
const dialerProxy = (form.getFieldValue([
'streamSettings',
'sockopt',
'dialerProxy',
]) ?? '') as string;
const dialerProxyOptions = Array.from(
new Set([...outboundTags, dialerProxy].filter(Boolean)),
).map((tg) => ({ value: tg, label: tg }));
return (
<>
<Form.Item label={t('pages.xray.outboundForm.sockopts')}>
@@ -34,8 +48,14 @@ export default function SockoptForm({ form }: { form: FormInstance<OutboundFormV
<Form.Item
label={t('pages.inbounds.form.dialerProxy')}
name={['streamSettings', 'sockopt', 'dialerProxy']}
tooltip={t('pages.xray.outboundForm.dialerProxyHint')}
>
<Input />
<Select
allowClear
showSearch
placeholder={t('pages.xray.outboundForm.dialerProxyPlaceholder')}
options={dialerProxyOptions}
/>
</Form.Item>
<Form.Item
label={t('pages.xray.wireguard.domainStrategy')}