fix(inbound): offer node share-address strategy only when a node exists

The `node` share-address strategy resolves to an address only when the
inbound can live on a node; for a local inbound it is always empty and
behaves like `listen`. Drop the `node` option from the picker unless an
enabled, node-eligible node exists, and coerce the value to `listen`
otherwise so the Select never shows an option that does nothing.
This commit is contained in:
MHSanaei
2026-06-11 23:32:24 +02:00
parent 825778144c
commit c47a905ad2
@@ -176,6 +176,10 @@ export default function InboundFormModal({
const selectableNodes = (availableNodes || []).filter((n) => n.enable);
const protocol = (Form.useWatch('protocol', form) ?? '') as string;
const isNodeEligible = NODE_ELIGIBLE_PROTOCOLS.has(protocol);
// The `node` share-address strategy only means something when the inbound can
// actually live on a node — otherwise the node address it would resolve to is
// always empty. Offer it only then; `listen`/`custom` work for local inbounds.
const nodeShareOptionAvailable = selectableNodes.length > 0 && isNodeEligible;
const sniffingEnabled = Form.useWatch(['sniffing', 'enabled'], form) ?? false;
const vlessEncryption = Form.useWatch(['settings', 'encryption'], form) ?? '';
const ssMethod = Form.useWatch(['settings', 'method'], form);
@@ -370,6 +374,18 @@ export default function InboundFormModal({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [open, wPort, wNodeId, protocol, network, mixedUdpOn, wSsNetwork, wTunnelNetwork]);
// Keep the strategy value inside the visible option set: when `node` isn't
// offered (no node, or a protocol that can't deploy to one) fall back to
// `listen`, which yields the same link for a local inbound. Mirrors how the
// protocol reset drops a nodeId that no longer applies.
useEffect(() => {
if (!open) return;
if (!nodeShareOptionAvailable && shareAddrStrategy === 'node') {
form.setFieldValue('shareAddrStrategy', 'listen');
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [open, nodeShareOptionAvailable, shareAddrStrategy]);
// Why: protocol picker reset cascades through the form — clearing the
// settings DU branch and dropping a nodeId that no longer applies. The
// legacy modal did this imperatively in onProtocolChange; here we hook
@@ -532,10 +548,12 @@ export default function InboundFormModal({
extra={t('pages.inbounds.form.shareAddrStrategyHelp')}
>
<Select
options={SHARE_ADDR_STRATEGIES.map((strategy) => ({
value: strategy,
label: t(`pages.inbounds.form.shareAddrStrategyOptions.${strategy}`),
}))}
options={SHARE_ADDR_STRATEGIES
.filter((strategy) => strategy !== 'node' || nodeShareOptionAvailable)
.map((strategy) => ({
value: strategy,
label: t(`pages.inbounds.form.shareAddrStrategyOptions.${strategy}`),
}))}
/>
</Form.Item>