summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/de.js14
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/en.js14
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/es.js14
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js14
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/it.js14
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js14
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js14
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js14
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js14
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js14
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/node-page.js104
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/style.css53
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/transport.js7
13 files changed, 303 insertions, 1 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 86258d6..604be3c 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js
@@ -743,4 +743,18 @@ export default {
'wizard.done_message': 'Ihre Gruppe ist bereit. Ihr Node hostet sie und die Verschlüsselung ist eingerichtet.',
'wizard.go_to_group': 'Zur Gruppe',
'wizard.finish_later': 'Einrichtung später abschließen',
+ 'node.stun_servers': 'STUN Servers',
+ 'node.stun_hint': 'Servers used for WebRTC NAT traversal (ICE candidate gathering).',
+ 'node.stun_add': 'Add',
+ 'node.stun_add_placeholder': 'stun:host:port',
+ 'node.stun_remove': 'Remove',
+ 'node.stun_move_up': 'Move up',
+ 'node.stun_move_down': 'Move down',
+ 'node.stun_save': 'Save',
+ 'node.stun_saving': 'Saving…',
+ 'node.stun_saved': 'STUN servers saved.',
+ 'node.stun_reset': 'Reset to defaults',
+ '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:',
};
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 5d2725c..a58619e 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js
@@ -722,4 +722,18 @@ export default {
'node.setting_unit_minutes': 'min',
'node.setting_on': 'On',
'node.setting_off': 'Off',
+ 'node.stun_servers': 'STUN Servers',
+ 'node.stun_hint': 'Servers used for WebRTC NAT traversal (ICE candidate gathering).',
+ 'node.stun_add': 'Add',
+ 'node.stun_add_placeholder': 'stun:host:port',
+ 'node.stun_remove': 'Remove',
+ 'node.stun_move_up': 'Move up',
+ 'node.stun_move_down': 'Move down',
+ 'node.stun_save': 'Save',
+ 'node.stun_saving': 'Saving…',
+ 'node.stun_saved': 'STUN servers saved.',
+ 'node.stun_reset': 'Reset to defaults',
+ '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:',
};
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 7080677..bcb0b93 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js
@@ -739,4 +739,18 @@ export default {
'wizard.done_message': 'Su grupo está listo. Su node lo aloja y el cifrado está configurado.',
'wizard.go_to_group': 'Ir al grupo',
'wizard.finish_later': 'Finalizar configuración más tarde',
+ 'node.stun_servers': 'STUN Servers',
+ 'node.stun_hint': 'Servers used for WebRTC NAT traversal (ICE candidate gathering).',
+ 'node.stun_add': 'Add',
+ 'node.stun_add_placeholder': 'stun:host:port',
+ 'node.stun_remove': 'Remove',
+ 'node.stun_move_up': 'Move up',
+ 'node.stun_move_down': 'Move down',
+ 'node.stun_save': 'Save',
+ 'node.stun_saving': 'Saving…',
+ 'node.stun_saved': 'STUN servers saved.',
+ 'node.stun_reset': 'Reset to defaults',
+ '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:',
};
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 eee4292..2e3e4d6 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js
@@ -755,4 +755,18 @@ export default {
'wizard.done_message': 'Votre groupe est prêt. Votre node l\'héberge et le chiffrement est configuré.',
'wizard.go_to_group': 'Aller au groupe',
'wizard.finish_later': 'Terminer la configuration plus tard',
+ 'node.stun_servers': 'STUN Servers',
+ 'node.stun_hint': 'Servers used for WebRTC NAT traversal (ICE candidate gathering).',
+ 'node.stun_add': 'Add',
+ 'node.stun_add_placeholder': 'stun:host:port',
+ 'node.stun_remove': 'Remove',
+ 'node.stun_move_up': 'Move up',
+ 'node.stun_move_down': 'Move down',
+ 'node.stun_save': 'Save',
+ 'node.stun_saving': 'Saving…',
+ 'node.stun_saved': 'STUN servers saved.',
+ 'node.stun_reset': 'Reset to defaults',
+ '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:',
};
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 1031fed..dc1d16f 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js
@@ -753,4 +753,18 @@ export default {
'wizard.done_message': 'Il suo gruppo è pronto. Il suo node lo ospita e la cifratura è configurata.',
'wizard.go_to_group': 'Vai al gruppo',
'wizard.finish_later': 'Completa la configurazione in seguito',
+ 'node.stun_servers': 'STUN Servers',
+ 'node.stun_hint': 'Servers used for WebRTC NAT traversal (ICE candidate gathering).',
+ 'node.stun_add': 'Add',
+ 'node.stun_add_placeholder': 'stun:host:port',
+ 'node.stun_remove': 'Remove',
+ 'node.stun_move_up': 'Move up',
+ 'node.stun_move_down': 'Move down',
+ 'node.stun_save': 'Save',
+ 'node.stun_saving': 'Saving…',
+ 'node.stun_saved': 'STUN servers saved.',
+ 'node.stun_reset': 'Reset to defaults',
+ '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:',
};
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 39863b1..90a5f24 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js
@@ -737,4 +737,18 @@ export default {
'wizard.done_message': 'グループの準備ができました。node がホストし、暗号化が設定されています。',
'wizard.go_to_group': 'グループを開く',
'wizard.finish_later': 'セットアップを後で完了',
+ 'node.stun_servers': 'STUN Servers',
+ 'node.stun_hint': 'Servers used for WebRTC NAT traversal (ICE candidate gathering).',
+ 'node.stun_add': 'Add',
+ 'node.stun_add_placeholder': 'stun:host:port',
+ 'node.stun_remove': 'Remove',
+ 'node.stun_move_up': 'Move up',
+ 'node.stun_move_down': 'Move down',
+ 'node.stun_save': 'Save',
+ 'node.stun_saving': 'Saving…',
+ 'node.stun_saved': 'STUN servers saved.',
+ 'node.stun_reset': 'Reset to defaults',
+ '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:',
};
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 21d14d7..3347087 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js
@@ -755,4 +755,18 @@ export default {
'wizard.done_message': 'Uw groep is klaar. Uw node host de groep en de versleuteling is ingesteld.',
'wizard.go_to_group': 'Naar groep',
'wizard.finish_later': 'Later voltooien',
+ 'node.stun_servers': 'STUN Servers',
+ 'node.stun_hint': 'Servers used for WebRTC NAT traversal (ICE candidate gathering).',
+ 'node.stun_add': 'Add',
+ 'node.stun_add_placeholder': 'stun:host:port',
+ 'node.stun_remove': 'Remove',
+ 'node.stun_move_up': 'Move up',
+ 'node.stun_move_down': 'Move down',
+ 'node.stun_save': 'Save',
+ 'node.stun_saving': 'Saving…',
+ 'node.stun_saved': 'STUN servers saved.',
+ 'node.stun_reset': 'Reset to defaults',
+ '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:',
};
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 d6ca6af..499a54f 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js
@@ -782,4 +782,18 @@ export default {
'wizard.done_message': 'Grupa jest gotowa. Node ją hostuje, a szyfrowanie jest skonfigurowane.',
'wizard.go_to_group': 'Przejdź do grupy',
'wizard.finish_later': 'Dokończ konfigurację później',
+ 'node.stun_servers': 'STUN Servers',
+ 'node.stun_hint': 'Servers used for WebRTC NAT traversal (ICE candidate gathering).',
+ 'node.stun_add': 'Add',
+ 'node.stun_add_placeholder': 'stun:host:port',
+ 'node.stun_remove': 'Remove',
+ 'node.stun_move_up': 'Move up',
+ 'node.stun_move_down': 'Move down',
+ 'node.stun_save': 'Save',
+ 'node.stun_saving': 'Saving…',
+ 'node.stun_saved': 'STUN servers saved.',
+ 'node.stun_reset': 'Reset to defaults',
+ '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:',
};
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 5e6dd42..03a9f6e 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
@@ -740,4 +740,18 @@ export default {
'wizard.done_message': 'Seu grupo está pronto. Seu node o está hospedando e a criptografia está configurada.',
'wizard.go_to_group': 'Ir para o grupo',
'wizard.finish_later': 'Finalizar configuração depois',
+ 'node.stun_servers': 'STUN Servers',
+ 'node.stun_hint': 'Servers used for WebRTC NAT traversal (ICE candidate gathering).',
+ 'node.stun_add': 'Add',
+ 'node.stun_add_placeholder': 'stun:host:port',
+ 'node.stun_remove': 'Remove',
+ 'node.stun_move_up': 'Move up',
+ 'node.stun_move_down': 'Move down',
+ 'node.stun_save': 'Save',
+ 'node.stun_saving': 'Saving…',
+ 'node.stun_saved': 'STUN servers saved.',
+ 'node.stun_reset': 'Reset to defaults',
+ '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:',
};
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 da97162..8004868 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
@@ -724,4 +724,18 @@ export default {
'wizard.done_message': '您的群组已就绪。您的 node 正在托管它,加密已设置完成。',
'wizard.go_to_group': '前往群组',
'wizard.finish_later': '稍后完成设置',
+ 'node.stun_servers': 'STUN Servers',
+ 'node.stun_hint': 'Servers used for WebRTC NAT traversal (ICE candidate gathering).',
+ 'node.stun_add': 'Add',
+ 'node.stun_add_placeholder': 'stun:host:port',
+ 'node.stun_remove': 'Remove',
+ 'node.stun_move_up': 'Move up',
+ 'node.stun_move_down': 'Move down',
+ 'node.stun_save': 'Save',
+ 'node.stun_saving': 'Saving…',
+ 'node.stun_saved': 'STUN servers saved.',
+ 'node.stun_reset': 'Reset to defaults',
+ '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:',
};
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 9a6db7d..87a9815 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/node-page.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/node-page.js
@@ -102,6 +102,9 @@ export function NodePage({ groups }) {
const [nodeSettings, setNodeSettings] = useState(null);
const [editSettings, setEditSettings] = useState(null);
const [savingSettings, setSavingSettings] = useState(false);
+ const [editStun, setEditStun] = useState(null);
+ const [savingStun, setSavingStun] = useState(false);
+ const [stunInput, setStunInput] = useState('');
const [operatorPaired, setOperatorPaired] = useState(false);
const [pairBusy, setPairBusy] = useState(false);
const [pairStatus, setPairStatus] = useState('');
@@ -131,6 +134,7 @@ export function NodePage({ groups }) {
useEffect(() => {
if (nodeSettings && !editSettings) setEditSettings({ ...nodeSettings });
+ if (nodeSettings && !editStun) setEditStun([...(nodeSettings.stun_servers || [])]);
}, [nodeSettings]);
const refresh = useCallback(async () => {
@@ -291,6 +295,61 @@ export function NodePage({ groups }) {
}
}, [editSettings]);
+ const saveStun = useCallback(async () => {
+ if (!editStun) return;
+ setSavingStun(true);
+ setActionMsg('');
+ try {
+ await nodeCall('PUT', '/api/node-settings', { stun_servers: editStun });
+ setNodeSettings(s => s ? { ...s, stun_servers: [...editStun] } : s);
+ setActionMsg(t('node.stun_saved'));
+ } catch (err) {
+ setActionMsg(platform.bridgeMessage(err));
+ } finally {
+ setSavingStun(false);
+ }
+ }, [editStun]);
+
+ const addStunServer = useCallback(() => {
+ const url = stunInput.trim();
+ if (!url) return;
+ if (!url.startsWith('stun:')) {
+ setActionMsg(t('node.stun_invalid'));
+ return;
+ }
+ if (editStun && editStun.includes(url)) {
+ setActionMsg(t('node.stun_duplicate'));
+ return;
+ }
+ setEditStun(s => [...(s || []), url]);
+ setStunInput('');
+ setActionMsg('');
+ }, [stunInput, editStun]);
+
+ const removeStunServer = useCallback((idx) => {
+ setEditStun(s => s.filter((_, i) => i !== idx));
+ }, []);
+
+ const moveStunServer = useCallback((idx, dir) => {
+ setEditStun(s => {
+ const a = [...s];
+ const target = idx + dir;
+ if (target < 0 || target >= a.length) return a;
+ [a[idx], a[target]] = [a[target], a[idx]];
+ return a;
+ });
+ }, []);
+
+ const resetStunDefaults = useCallback(() => {
+ setEditStun([
+ 'stun:stun.l.google.com:19302',
+ 'stun:stun1.l.google.com:19302',
+ 'stun:stun.cloudflare.com:3478',
+ 'stun:stun.services.mozilla.com:3478',
+ ]);
+ setActionMsg('');
+ }, []);
+
const doPairOperator = useCallback(async () => {
setPairBusy(true);
setPairStatus('');
@@ -559,6 +618,51 @@ export function NodePage({ groups }) {
</div>
</div>
`}
+
+ ${editStun && html`
+ <div class="node-group">
+ <span class="settings-heading">${t('node.stun_servers')}</span>
+ <p class="node-hint">${t('node.stun_hint')}</p>
+ ${editStun.length === 0 ? html`
+ <p class="node-hint">${t('node.stun_empty')}</p>
+ ` : html`
+ <div class="stun-server-list">
+ ${editStun.map((url, idx) => html`
+ <div class="stun-server-entry" key=${url + idx}>
+ <span class="stun-server-url">${url}</span>
+ <div class="stun-server-actions">
+ <button class="btn btn-small btn-icon" disabled=${idx === 0}
+ onClick=${() => moveStunServer(idx, -1)}
+ title=${t('node.stun_move_up')}>▲</button>
+ <button class="btn btn-small btn-icon" disabled=${idx === editStun.length - 1}
+ onClick=${() => moveStunServer(idx, 1)}
+ title=${t('node.stun_move_down')}>▼</button>
+ <button class="btn btn-small btn-danger btn-icon"
+ onClick=${() => removeStunServer(idx)}
+ title=${t('node.stun_remove')}>✕</button>
+ </div>
+ </div>
+ `)}
+ </div>
+ `}
+ <div class="stun-add-row">
+ <input type="text" placeholder=${t('node.stun_add_placeholder')}
+ value=${stunInput}
+ onInput=${e => setStunInput(e.target.value)}
+ onKeyDown=${e => { if (e.key === 'Enter') addStunServer(); }} />
+ <button class="btn btn-small btn-secondary" onClick=${addStunServer}>
+ ${t('node.stun_add')}</button>
+ </div>
+ <div class="node-settings-actions">
+ <button class="btn btn-primary btn-small" disabled=${savingStun || busy}
+ onClick=${saveStun}>
+ ${savingStun ? t('node.stun_saving') : t('node.stun_save')}</button>
+ <button class="btn btn-small btn-secondary" disabled=${savingStun || busy}
+ onClick=${resetStunDefaults}>
+ ${t('node.stun_reset')}</button>
+ </div>
+ </div>
+ `}
</div>
`;
}
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/style.css b/packages/meshbay-hub/src/meshbay_hub/static/style.css
index 2523c1a..e217cc8 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/style.css
+++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css
@@ -2708,6 +2708,59 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; }
}
.node-settings-actions {
margin-top: 12px;
+ display: flex;
+ gap: 8px;
+}
+
+/* ── STUN server list ─────────────────────────────────────────────────────── */
+.stun-server-list {
+ display: flex;
+ flex-direction: column;
+ gap: 0;
+ margin-top: 8px;
+}
+.stun-server-entry {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ padding: 6px 0;
+ border-bottom: 1px solid var(--border);
+}
+.stun-server-url {
+ font-family: monospace;
+ font-size: 0.85em;
+ flex: 1;
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.stun-server-actions {
+ display: flex;
+ gap: 2px;
+ flex-shrink: 0;
+}
+.btn-icon {
+ min-width: 28px;
+ padding: 2px 6px;
+ font-size: 0.75em;
+ line-height: 1;
+}
+.stun-add-row {
+ display: flex;
+ gap: 6px;
+ margin-top: 10px;
+}
+.stun-add-row input {
+ flex: 1;
+ padding: 6px 10px;
+ border: 1px solid var(--border);
+ border-radius: 5px;
+ background: var(--bg-base);
+ color: var(--text);
+ font-family: monospace;
+ font-size: 0.85em;
}
/* ── Create Group Wizard ──────────────────────────────────────────────────── */
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/transport.js b/packages/meshbay-hub/src/meshbay_hub/static/transport.js
index b60bd93..286bc9d 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/transport.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/transport.js
@@ -321,7 +321,12 @@ class MeshBayTransport {
this._newNodeBundle = null;
this._joinError = null;
this._pc = new RTCPeerConnection({
- iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
+ iceServers: [
+ { urls: 'stun:stun.l.google.com:19302' },
+ { urls: 'stun:stun1.l.google.com:19302' },
+ { urls: 'stun:stun.cloudflare.com:3478' },
+ { urls: 'stun:stun.services.mozilla.com:3478' },
+ ],
});
this._channel = this._pc.createDataChannel('mnp', { ordered: true });