Switche: optionaler SSH-Port statt fest 22

- Neue Spalte switches.ssh_port (nullable INTEGER), Migration für
  bestehende DBs in _ensure_schema(), Feld in create_db.py ergänzt.
- Formular (Anlegen/Bearbeiten): optionales SSH-Port-Feld, Validierung
  1-65535, leer -> NULL (= Standard 22 überall, SWITCH_DEFAULT_SSH_PORT).
  Switch-Liste zeigt den effektiven Port inkl. "(Standard)"-Hinweis.
- generate_ips.py liefert den effektiven Port (Fallback 22) als eigenes
  Pipe-Feld an poe.sh; poe.sh übernimmt es in disable_poe/enable_poe und
  reicht es als "ssh -p <port>" an die expect-Skripte durch (Default
  weiterhin 22 falls Parameter fehlt).
- Web-Terminal (Verbindungstest) sendet den im Formular eingetragenen
  Port statt hartkodiert 22.
- Live getestet: gültiger/leerer/ungültiger Port beim Anlegen, korrekte
  Weitergabe durch generate_ips.py inkl. Feldreihenfolge, die poe.sh
'read' erwartet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 19:56:23 +02:00
co-authored by Claude Sonnet 5
parent 813b17d002
commit 06db4c1382
6 changed files with 75 additions and 20 deletions
+18 -4
View File
@@ -37,15 +37,20 @@
<tr>
<th data-sort-key="hostname">Hostname</th>
<th data-sort-key="ip">IP-Adresse</th>
<th data-sort-key="ssh_port">SSH-Port</th>
<th data-sort-key="credential">Zugangsdaten</th>
<th style="width:1%;">Aktionen</th>
</tr>
</thead>
<tbody>
{% for s in switches %}
<tr data-sort-hostname="{{ s['hostname']|lower }}" data-sort-ip="{{ s['ip']|lower }}" data-sort-credential="{{ (s['credential_name'] or '')|lower }}">
<tr data-sort-hostname="{{ s['hostname']|lower }}" data-sort-ip="{{ s['ip']|lower }}" data-sort-ssh_port="{{ s['ssh_port'] or 22 }}" data-sort-credential="{{ (s['credential_name'] or '')|lower }}">
<td class="cell-name">{{ s['hostname'] }}</td>
<td class="mono">{{ s['ip'] }}</td>
<td class="mono">
{{ s['ssh_port'] or 22 }}
{% if not s['ssh_port'] %}<span class="text-faint" style="font-size:11px;">(Standard)</span>{% endif %}
</td>
<td>
{% if s['credential_name'] %}
{{ s['credential_name'] }} <span class="text-faint mono" style="font-size:11.5px;">({{ s['credential_username'] }})</span>
@@ -89,6 +94,10 @@
<input type="text" name="ip" value="{{ s['ip'] }}" required placeholder="z.B. 192.168.1.100">
<div class="invalid-feedback">Bitte eine gültige IP-Adresse eingeben.</div>
</div>
<div class="field"><label>SSH-Port</label>
<input type="number" name="ssh_port" value="{{ s['ssh_port'] or '' }}" min="1" max="65535" placeholder="22 (Standard)">
<div class="field-hint">Leer lassen, wenn der Switch den Standard-Port 22 verwendet.</div>
</div>
<div class="field">
<label>Zugangsdaten</label>
<select name="credential_choice" class="credential-select" onchange="toggleNewCredentialFields(this)">
@@ -127,7 +136,7 @@
</div>
{% endif %}
{% else %}
<tr class="empty-row"><td colspan="4">Noch keine Switche vorhanden.</td></tr>
<tr class="empty-row"><td colspan="5">Noch keine Switche vorhanden.</td></tr>
{% endfor %}
</tbody>
</table>
@@ -152,6 +161,10 @@
<input type="text" name="ip" required placeholder="z.B. 192.168.1.100">
<div class="invalid-feedback">Bitte eine gültige IP-Adresse eingeben.</div>
</div>
<div class="field"><label>SSH-Port</label>
<input type="number" name="ssh_port" min="1" max="65535" placeholder="22 (Standard)">
<div class="field-hint">Leer lassen, wenn der Switch den Standard-Port 22 verwendet.</div>
</div>
<div class="field">
<label>Zugangsdaten</label>
<select name="credential_choice" class="credential-select" onchange="toggleNewCredentialFields(this)">
@@ -300,6 +313,7 @@ function getCredentialInfo(form) {
function openTerminal(form) {
const host = (form.querySelector("input[name='ip']") || {}).value?.trim();
const port = parseInt((form.querySelector("input[name='ssh_port']") || {}).value, 10) || 22;
const { username, passwordInput } = getCredentialInfo(form);
activePasswordInput = passwordInput;
@@ -311,7 +325,7 @@ function openTerminal(form) {
PoeUI.openModal("terminalModal");
ensureTerminal();
term.reset();
document.getElementById("termTarget").innerText = `${username}@${host}`;
document.getElementById("termTarget").innerText = `${username}@${host}:${port}`;
setTermStatus("Verbinde…", "unknown");
setTimeout(() => fitAddon && fitAddon.fit(), 60);
@@ -321,7 +335,7 @@ function openTerminal(form) {
termSocket = new WebSocket(`${proto}//${location.host}/ws/ssh_terminal`);
termSocket.onopen = () => {
termSocket.send(JSON.stringify({ host, username, port: 22 }));
termSocket.send(JSON.stringify({ host, username, port }));
setTermStatus("Verbunden", "online");
};
termSocket.onmessage = (event) => term.write(event.data);