mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-19 17:40:59 +00:00
5eec178483
Add a per-inbound "Route through Xray" toggle (off by default) plus an optional outbound picker on MTProto inbounds. mtg only supports a SOCKS5 upstream, so when enabled the panel injects a loopback SOCKS bridge into the generated Xray config — tagged with the inbound's own tag — and mtg dials Telegram through it via a [network] proxies upstream. The router then governs Telegram egress: matchable in the Routing tab, or forced to a chosen outbound/balancer via the picker. - mtproto: Instance carries RouteThroughXray + XrayRoutePort (in the fingerprint); InstanceFromInbound parses them; renderConfig emits the socks5 [network] upstream; freeLocalPort exported as FreeLocalPort. - xray.go: injectMtprotoEgress appends the loopback SOCKS bridge and prepends an optional inboundTag->outbound/balancer rule, hot-appliable like injectPanelEgress. - inbound.go: backend-owned egress port persisted in settings, allocated once and carried across edits (stored value wins); stripped with the inert outboundTag when routing is off; allocation failure fails the save; routed add/update/del force a config regen. - mtproto_job: skip folding mtg metrics for routed inbounds (the bridge, carrying the inbound tag, is metered by xray_traffic_job) to avoid double-counting. - frontend: toggle + outbound/balancer Select (useOutboundTags) on the MTProto form; i18n keys for all locales.
102 lines
3.9 KiB
TypeScript
102 lines
3.9 KiB
TypeScript
import { useTranslation } from 'react-i18next';
|
|
import { Button, Form, Input, InputNumber, Select, Space, Switch } from 'antd';
|
|
import { ReloadOutlined } from '@ant-design/icons';
|
|
|
|
import { generateMtprotoSecret, mtprotoSecretForDomain } from '@/lib/xray/inbound-defaults';
|
|
import { useOutboundTags } from '@/api/queries/useOutboundTags';
|
|
|
|
export default function MtprotoFields() {
|
|
const { t } = useTranslation();
|
|
const form = Form.useFormInstance();
|
|
const routeThroughXray = Form.useWatch(['settings', 'routeThroughXray'], form) as boolean | undefined;
|
|
const { data: outboundTags } = useOutboundTags();
|
|
return (
|
|
<>
|
|
<Form.Item name={['settings', 'fakeTlsDomain']} label={t('pages.inbounds.form.fakeTlsDomain')}>
|
|
<Input
|
|
placeholder="www.cloudflare.com"
|
|
onChange={(e) => {
|
|
const current = (form.getFieldValue(['settings', 'secret']) as string) ?? '';
|
|
form.setFieldValue(['settings', 'secret'], mtprotoSecretForDomain(current, e.target.value));
|
|
}}
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item label={t('pages.inbounds.form.mtprotoSecret')}>
|
|
<Space.Compact block>
|
|
<Form.Item name={['settings', 'secret']} noStyle>
|
|
<Input readOnly style={{ width: 'calc(100% - 32px)' }} />
|
|
</Form.Item>
|
|
<Button
|
|
icon={<ReloadOutlined />}
|
|
onClick={() => {
|
|
const domain = form.getFieldValue(['settings', 'fakeTlsDomain']);
|
|
form.setFieldValue(['settings', 'secret'], generateMtprotoSecret(domain as string));
|
|
}}
|
|
/>
|
|
</Space.Compact>
|
|
</Form.Item>
|
|
<Form.Item
|
|
name={['settings', 'domainFronting', 'ip']}
|
|
label={t('pages.inbounds.form.mtgDomainFrontingIp')}
|
|
tooltip={t('pages.inbounds.form.mtgDomainFrontingHint')}
|
|
>
|
|
<Input placeholder="127.0.0.1" />
|
|
</Form.Item>
|
|
<Form.Item name={['settings', 'domainFronting', 'port']} label={t('pages.inbounds.form.mtgDomainFrontingPort')}>
|
|
<InputNumber min={0} max={65535} placeholder="443" style={{ width: '100%' }} />
|
|
</Form.Item>
|
|
<Form.Item
|
|
name={['settings', 'domainFronting', 'proxyProtocol']}
|
|
label={t('pages.inbounds.form.mtgDomainFrontingProxyProtocol')}
|
|
valuePropName="checked"
|
|
>
|
|
<Switch />
|
|
</Form.Item>
|
|
<Form.Item
|
|
name={['settings', 'proxyProtocolListener']}
|
|
label={t('pages.inbounds.form.mtgProxyProtocolListener')}
|
|
valuePropName="checked"
|
|
>
|
|
<Switch />
|
|
</Form.Item>
|
|
<Form.Item name={['settings', 'preferIp']} label={t('pages.inbounds.form.mtgPreferIp')}>
|
|
<Select
|
|
allowClear
|
|
placeholder="prefer-ipv6"
|
|
options={[
|
|
{ value: 'prefer-ipv6', label: 'prefer-ipv6' },
|
|
{ value: 'prefer-ipv4', label: 'prefer-ipv4' },
|
|
{ value: 'only-ipv6', label: 'only-ipv6' },
|
|
{ value: 'only-ipv4', label: 'only-ipv4' },
|
|
]}
|
|
/>
|
|
</Form.Item>
|
|
<Form.Item name={['settings', 'debug']} label={t('pages.inbounds.form.mtgDebug')} valuePropName="checked">
|
|
<Switch />
|
|
</Form.Item>
|
|
<Form.Item
|
|
name={['settings', 'routeThroughXray']}
|
|
label={t('pages.inbounds.form.mtgRouteThroughXray')}
|
|
tooltip={t('pages.inbounds.form.mtgRouteThroughXrayHint')}
|
|
valuePropName="checked"
|
|
>
|
|
<Switch />
|
|
</Form.Item>
|
|
{routeThroughXray && (
|
|
<Form.Item
|
|
name={['settings', 'outboundTag']}
|
|
label={t('pages.inbounds.form.mtgRouteOutbound')}
|
|
tooltip={t('pages.inbounds.form.mtgRouteOutboundHint')}
|
|
>
|
|
<Select
|
|
allowClear
|
|
showSearch
|
|
placeholder={t('pages.inbounds.form.mtgRouteOutboundPlaceholder')}
|
|
options={(outboundTags ?? []).map((tag) => ({ value: tag, label: tag }))}
|
|
/>
|
|
</Form.Item>
|
|
)}
|
|
</>
|
|
);
|
|
}
|