diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-30 22:36:56 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-30 22:36:56 +0200 |
| commit | 2d6c276734fd19f80edf84cdb95f91a929df6365 (patch) | |
| tree | 41a697eb9c048218e0d800cbcb7fa39f4ede2b1d /packages/meshbay-hub/src/meshbay_hub/static | |
| parent | 07e6e4271ea3a45e9cd364eb6ec05801a653d9e8 (diff) | |
| download | meshbay-2d6c276734fd19f80edf84cdb95f91a929df6365.tar.gz | |
feat(ui): configurable ICE interfaces in the Node page
The ice_interfaces setting (auto-exclude vs explicit whitelist) is now
editable from the Node page, persisted via the settings API and roster,
and hot-swapped at runtime by re-installing the aioice filter.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static')
11 files changed, 191 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js index 604be3c..b61d93c 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js @@ -757,4 +757,15 @@ export default { 'node.stun_empty': 'No STUN servers configured. NAT traversal will not work.', 'node.stun_duplicate': 'This server is already in the list.', 'node.stun_invalid': 'STUN URL must start with stun:', + 'node.ice_interfaces': 'ICE Interfaces', + 'node.ice_auto_hint': 'Auto mode — virtual and VPN interfaces (Tailscale, Docker, libvirt, etc.) are excluded automatically.', + 'node.ice_manual_hint': 'Manual mode — only the listed interfaces are used for ICE candidate gathering.', + 'node.ice_add': 'Add', + 'node.ice_add_placeholder': 'e.g. eth0, wlp0s20f3', + 'node.ice_remove': 'Remove', + 'node.ice_save': 'Save', + 'node.ice_saving': 'Saving…', + 'node.ice_saved': 'ICE interfaces saved.', + 'node.ice_reset_auto': 'Switch to auto', + 'node.ice_duplicate': 'This interface is already in the list.', }; diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js index a58619e..851911c 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js @@ -736,4 +736,15 @@ export default { 'node.stun_empty': 'No STUN servers configured. NAT traversal will not work.', 'node.stun_duplicate': 'This server is already in the list.', 'node.stun_invalid': 'STUN URL must start with stun:', + 'node.ice_interfaces': 'ICE Interfaces', + 'node.ice_auto_hint': 'Auto mode — virtual and VPN interfaces (Tailscale, Docker, libvirt, etc.) are excluded automatically.', + 'node.ice_manual_hint': 'Manual mode — only the listed interfaces are used for ICE candidate gathering.', + 'node.ice_add': 'Add', + 'node.ice_add_placeholder': 'e.g. eth0, wlp0s20f3', + 'node.ice_remove': 'Remove', + 'node.ice_save': 'Save', + 'node.ice_saving': 'Saving…', + 'node.ice_saved': 'ICE interfaces saved.', + 'node.ice_reset_auto': 'Switch to auto', + 'node.ice_duplicate': 'This interface is already in the list.', }; diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js index bcb0b93..84af4cd 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js @@ -753,4 +753,15 @@ export default { 'node.stun_empty': 'No STUN servers configured. NAT traversal will not work.', 'node.stun_duplicate': 'This server is already in the list.', 'node.stun_invalid': 'STUN URL must start with stun:', + 'node.ice_interfaces': 'ICE Interfaces', + 'node.ice_auto_hint': 'Auto mode — virtual and VPN interfaces (Tailscale, Docker, libvirt, etc.) are excluded automatically.', + 'node.ice_manual_hint': 'Manual mode — only the listed interfaces are used for ICE candidate gathering.', + 'node.ice_add': 'Add', + 'node.ice_add_placeholder': 'e.g. eth0, wlp0s20f3', + 'node.ice_remove': 'Remove', + 'node.ice_save': 'Save', + 'node.ice_saving': 'Saving…', + 'node.ice_saved': 'ICE interfaces saved.', + 'node.ice_reset_auto': 'Switch to auto', + 'node.ice_duplicate': 'This interface is already in the list.', }; diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js index 2e3e4d6..e540b4b 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js @@ -769,4 +769,15 @@ export default { 'node.stun_empty': 'No STUN servers configured. NAT traversal will not work.', 'node.stun_duplicate': 'This server is already in the list.', 'node.stun_invalid': 'STUN URL must start with stun:', + 'node.ice_interfaces': 'ICE Interfaces', + 'node.ice_auto_hint': 'Auto mode — virtual and VPN interfaces (Tailscale, Docker, libvirt, etc.) are excluded automatically.', + 'node.ice_manual_hint': 'Manual mode — only the listed interfaces are used for ICE candidate gathering.', + 'node.ice_add': 'Add', + 'node.ice_add_placeholder': 'e.g. eth0, wlp0s20f3', + 'node.ice_remove': 'Remove', + 'node.ice_save': 'Save', + 'node.ice_saving': 'Saving…', + 'node.ice_saved': 'ICE interfaces saved.', + 'node.ice_reset_auto': 'Switch to auto', + 'node.ice_duplicate': 'This interface is already in the list.', }; diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js index dc1d16f..b009460 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js @@ -767,4 +767,15 @@ export default { 'node.stun_empty': 'No STUN servers configured. NAT traversal will not work.', 'node.stun_duplicate': 'This server is already in the list.', 'node.stun_invalid': 'STUN URL must start with stun:', + 'node.ice_interfaces': 'ICE Interfaces', + 'node.ice_auto_hint': 'Auto mode — virtual and VPN interfaces (Tailscale, Docker, libvirt, etc.) are excluded automatically.', + 'node.ice_manual_hint': 'Manual mode — only the listed interfaces are used for ICE candidate gathering.', + 'node.ice_add': 'Add', + 'node.ice_add_placeholder': 'e.g. eth0, wlp0s20f3', + 'node.ice_remove': 'Remove', + 'node.ice_save': 'Save', + 'node.ice_saving': 'Saving…', + 'node.ice_saved': 'ICE interfaces saved.', + 'node.ice_reset_auto': 'Switch to auto', + 'node.ice_duplicate': 'This interface is already in the list.', }; diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js index 90a5f24..9b317a1 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js @@ -751,4 +751,15 @@ export default { 'node.stun_empty': 'No STUN servers configured. NAT traversal will not work.', 'node.stun_duplicate': 'This server is already in the list.', 'node.stun_invalid': 'STUN URL must start with stun:', + 'node.ice_interfaces': 'ICE Interfaces', + 'node.ice_auto_hint': 'Auto mode — virtual and VPN interfaces (Tailscale, Docker, libvirt, etc.) are excluded automatically.', + 'node.ice_manual_hint': 'Manual mode — only the listed interfaces are used for ICE candidate gathering.', + 'node.ice_add': 'Add', + 'node.ice_add_placeholder': 'e.g. eth0, wlp0s20f3', + 'node.ice_remove': 'Remove', + 'node.ice_save': 'Save', + 'node.ice_saving': 'Saving…', + 'node.ice_saved': 'ICE interfaces saved.', + 'node.ice_reset_auto': 'Switch to auto', + 'node.ice_duplicate': 'This interface is already in the list.', }; diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js index 3347087..0436a31 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js @@ -769,4 +769,15 @@ export default { 'node.stun_empty': 'No STUN servers configured. NAT traversal will not work.', 'node.stun_duplicate': 'This server is already in the list.', 'node.stun_invalid': 'STUN URL must start with stun:', + 'node.ice_interfaces': 'ICE Interfaces', + 'node.ice_auto_hint': 'Auto mode — virtual and VPN interfaces (Tailscale, Docker, libvirt, etc.) are excluded automatically.', + 'node.ice_manual_hint': 'Manual mode — only the listed interfaces are used for ICE candidate gathering.', + 'node.ice_add': 'Add', + 'node.ice_add_placeholder': 'e.g. eth0, wlp0s20f3', + 'node.ice_remove': 'Remove', + 'node.ice_save': 'Save', + 'node.ice_saving': 'Saving…', + 'node.ice_saved': 'ICE interfaces saved.', + 'node.ice_reset_auto': 'Switch to auto', + 'node.ice_duplicate': 'This interface is already in the list.', }; diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js index 499a54f..391b8cd 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js @@ -796,4 +796,15 @@ export default { 'node.stun_empty': 'No STUN servers configured. NAT traversal will not work.', 'node.stun_duplicate': 'This server is already in the list.', 'node.stun_invalid': 'STUN URL must start with stun:', + 'node.ice_interfaces': 'ICE Interfaces', + 'node.ice_auto_hint': 'Auto mode — virtual and VPN interfaces (Tailscale, Docker, libvirt, etc.) are excluded automatically.', + 'node.ice_manual_hint': 'Manual mode — only the listed interfaces are used for ICE candidate gathering.', + 'node.ice_add': 'Add', + 'node.ice_add_placeholder': 'e.g. eth0, wlp0s20f3', + 'node.ice_remove': 'Remove', + 'node.ice_save': 'Save', + 'node.ice_saving': 'Saving…', + 'node.ice_saved': 'ICE interfaces saved.', + 'node.ice_reset_auto': 'Switch to auto', + 'node.ice_duplicate': 'This interface is already in the list.', }; diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js index 03a9f6e..16e0559 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js @@ -754,4 +754,15 @@ export default { 'node.stun_empty': 'No STUN servers configured. NAT traversal will not work.', 'node.stun_duplicate': 'This server is already in the list.', 'node.stun_invalid': 'STUN URL must start with stun:', + 'node.ice_interfaces': 'ICE Interfaces', + 'node.ice_auto_hint': 'Auto mode — virtual and VPN interfaces (Tailscale, Docker, libvirt, etc.) are excluded automatically.', + 'node.ice_manual_hint': 'Manual mode — only the listed interfaces are used for ICE candidate gathering.', + 'node.ice_add': 'Add', + 'node.ice_add_placeholder': 'e.g. eth0, wlp0s20f3', + 'node.ice_remove': 'Remove', + 'node.ice_save': 'Save', + 'node.ice_saving': 'Saving…', + 'node.ice_saved': 'ICE interfaces saved.', + 'node.ice_reset_auto': 'Switch to auto', + 'node.ice_duplicate': 'This interface is already in the list.', }; diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js index 8004868..edd9edc 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js @@ -738,4 +738,15 @@ export default { 'node.stun_empty': 'No STUN servers configured. NAT traversal will not work.', 'node.stun_duplicate': 'This server is already in the list.', 'node.stun_invalid': 'STUN URL must start with stun:', + 'node.ice_interfaces': 'ICE Interfaces', + 'node.ice_auto_hint': 'Auto mode — virtual and VPN interfaces (Tailscale, Docker, libvirt, etc.) are excluded automatically.', + 'node.ice_manual_hint': 'Manual mode — only the listed interfaces are used for ICE candidate gathering.', + 'node.ice_add': 'Add', + 'node.ice_add_placeholder': 'e.g. eth0, wlp0s20f3', + 'node.ice_remove': 'Remove', + 'node.ice_save': 'Save', + 'node.ice_saving': 'Saving…', + 'node.ice_saved': 'ICE interfaces saved.', + 'node.ice_reset_auto': 'Switch to auto', + 'node.ice_duplicate': 'This interface is already in the list.', }; diff --git a/packages/meshbay-hub/src/meshbay_hub/static/node-page.js b/packages/meshbay-hub/src/meshbay_hub/static/node-page.js index 87a9815..82fc56e 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/node-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/node-page.js @@ -105,6 +105,9 @@ export function NodePage({ groups }) { const [editStun, setEditStun] = useState(null); const [savingStun, setSavingStun] = useState(false); const [stunInput, setStunInput] = useState(''); + const [editIce, setEditIce] = useState(null); + const [savingIce, setSavingIce] = useState(false); + const [iceInput, setIceInput] = useState(''); const [operatorPaired, setOperatorPaired] = useState(false); const [pairBusy, setPairBusy] = useState(false); const [pairStatus, setPairStatus] = useState(''); @@ -135,6 +138,7 @@ export function NodePage({ groups }) { useEffect(() => { if (nodeSettings && !editSettings) setEditSettings({ ...nodeSettings }); if (nodeSettings && !editStun) setEditStun([...(nodeSettings.stun_servers || [])]); + if (nodeSettings && !editIce) setEditIce([...(nodeSettings.ice_interfaces || [])]); }, [nodeSettings]); const refresh = useCallback(async () => { @@ -350,6 +354,42 @@ export function NodePage({ groups }) { setActionMsg(''); }, []); + const saveIce = useCallback(async () => { + if (!editIce) return; + setSavingIce(true); + setActionMsg(''); + try { + await nodeCall('PUT', '/api/node-settings', { ice_interfaces: editIce }); + setNodeSettings(s => s ? { ...s, ice_interfaces: [...editIce] } : s); + setActionMsg(t('node.ice_saved')); + } catch (err) { + setActionMsg(platform.bridgeMessage(err)); + } finally { + setSavingIce(false); + } + }, [editIce]); + + const addIceInterface = useCallback(() => { + const name = iceInput.trim(); + if (!name) return; + if (editIce && editIce.includes(name)) { + setActionMsg(t('node.ice_duplicate')); + return; + } + setEditIce(s => [...(s || []), name]); + setIceInput(''); + setActionMsg(''); + }, [iceInput, editIce]); + + const removeIceInterface = useCallback((idx) => { + setEditIce(s => s.filter((_, i) => i !== idx)); + }, []); + + const resetIceAuto = useCallback(() => { + setEditIce([]); + setActionMsg(''); + }, []); + const doPairOperator = useCallback(async () => { setPairBusy(true); setPairStatus(''); @@ -663,6 +703,47 @@ export function NodePage({ groups }) { </div> </div> `} + + ${editIce && html` + <div class="node-group"> + <span class="settings-heading">${t('node.ice_interfaces')}</span> + <p class="node-hint">${editIce.length === 0 + ? t('node.ice_auto_hint') + : t('node.ice_manual_hint')}</p> + ${editIce.length > 0 && html` + <div class="stun-server-list"> + ${editIce.map((name, idx) => html` + <div class="stun-server-entry" key=${name + idx}> + <span class="stun-server-url">${name}</span> + <div class="stun-server-actions"> + <button class="btn btn-small btn-danger btn-icon" + onClick=${() => removeIceInterface(idx)} + title=${t('node.ice_remove')}>✕</button> + </div> + </div> + `)} + </div> + `} + <div class="stun-add-row"> + <input type="text" placeholder=${t('node.ice_add_placeholder')} + value=${iceInput} + onInput=${e => setIceInput(e.target.value)} + onKeyDown=${e => { if (e.key === 'Enter') addIceInterface(); }} /> + <button class="btn btn-small btn-secondary" onClick=${addIceInterface}> + ${t('node.ice_add')}</button> + </div> + <div class="node-settings-actions"> + <button class="btn btn-primary btn-small" disabled=${savingIce || busy} + onClick=${saveIce}> + ${savingIce ? t('node.ice_saving') : t('node.ice_save')}</button> + ${editIce.length > 0 && html` + <button class="btn btn-small btn-secondary" disabled=${savingIce || busy} + onClick=${resetIceAuto}> + ${t('node.ice_reset_auto')}</button> + `} + </div> + </div> + `} </div> `; } |