From fd4f6ceb0fb8e2a18a11ed808040a5bb1bb2b085 Mon Sep 17 00:00:00 2001 From: boban Date: Fri, 26 Jun 2026 07:16:11 +0200 Subject: [PATCH] =?UTF-8?q?chore:=20remove=20dead=20code=20=E2=80=94=20sid?= =?UTF-8?q?ecar=20host-PTY=20path,=20node-pty,=20unused=20lang=20keys=20+?= =?UTF-8?q?=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "Clusev host" terminal resolves to an SSH login now (resolve never returns kind:'host'), so the sidecar's local-shell path was unreachable: - docker/terminal/server.js: drop startHost(), the spec.kind==='host' branch, the node-pty require and HOST_SHELL/HOST_CWD constants — the sidecar only ever opens an SSH PTY. - Dockerfile: drop the python3/make/g++ toolchain (only there to compile node-pty's native addon); package.json: drop the node-pty dependency. Leaner, faster image. - compose (dev+prod): drop the now-unused TERMINAL_HOST_CWD env and the .:/workspace:ro mount. - remove grep-confirmed-unused lang keys (settings 2FA/stub-tab keys, accounts twofa/cannot_remove, auth recovery_done/regenerate_confirm, common back/retry/more/loading, servers firewall/fail2ban unavailable variants) and the unused x-server-item Blade component. Verified: lean sidecar rebuilds + the server terminal still connects/runs; full suite 467 pass; all pages 200 with no raw-key leaks and no console errors. Co-Authored-By: Claude Opus 4.8 --- docker-compose.prod.yml | 4 --- docker-compose.yml | 3 -- docker/terminal/Dockerfile | 7 +---- docker/terminal/package.json | 5 ++-- docker/terminal/server.js | 29 ++----------------- lang/de/accounts.php | 4 --- lang/de/auth.php | 2 -- lang/de/common.php | 4 --- lang/de/servers.php | 8 ----- lang/de/settings.php | 9 ------ lang/en/accounts.php | 4 --- lang/en/auth.php | 2 -- lang/en/common.php | 4 --- lang/en/servers.php | 8 ----- lang/en/settings.php | 9 ------ .../views/components/server-item.blade.php | 26 ----------------- 16 files changed, 6 insertions(+), 122 deletions(-) delete mode 100644 resources/views/components/server-item.blade.php diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index bf54d3d..a62dfa0 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -95,10 +95,6 @@ services: environment: TERMINAL_SIDECAR_SECRET: "${TERMINAL_SIDECAR_SECRET:-}" APP_INTERNAL_URL: "http://app:80" - # Land the host shell in a real, writable home (~) — not the read-only repo mount. - TERMINAL_HOST_CWD: "/home/node" - volumes: - - .:/workspace:ro expose: - "3000" networks: diff --git a/docker-compose.yml b/docker-compose.yml index 1353cb4..f10ba38 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -110,10 +110,7 @@ services: environment: TERMINAL_SIDECAR_SECRET: "${TERMINAL_SIDECAR_SECRET:-}" APP_INTERNAL_URL: "http://app:80" - # Land the host shell in a real, writable home (~) — not the read-only repo mount. - TERMINAL_HOST_CWD: "/home/node" volumes: - - .:/workspace:ro # Dev: bind the sidecar source over the baked copy so edits need only `restart terminal` # (no rebuild). Prod bakes it into the image (no mount) — see docker-compose.prod.yml. - ./docker/terminal/server.js:/app/server.js:ro diff --git a/docker/terminal/Dockerfile b/docker/terminal/Dockerfile index 757ece1..97761c6 100644 --- a/docker/terminal/Dockerfile +++ b/docker/terminal/Dockerfile @@ -1,11 +1,6 @@ -# Clusev terminal sidecar — Node ws↔SSH/PTY bridge. +# Clusev terminal sidecar — Node ws↔SSH bridge (pure JS: ws + ssh2, no native addons). FROM node:20-bookworm-slim -# node-pty compiles a native addon → needs python3 + a toolchain at install time. -RUN apt-get update \ - && apt-get install -y --no-install-recommends python3 make g++ ca-certificates \ - && rm -rf /var/lib/apt/lists/* - WORKDIR /app COPY package.json ./ RUN npm install --omit=dev && npm cache clean --force diff --git a/docker/terminal/package.json b/docker/terminal/package.json index 37d3a51..36778e0 100644 --- a/docker/terminal/package.json +++ b/docker/terminal/package.json @@ -2,14 +2,13 @@ "name": "clusev-terminal-sidecar", "version": "1.0.0", "private": true, - "description": "Clusev terminal sidecar — bridges a browser WebSocket to an SSH PTY (per server) or a local shell PTY (the Clusev host).", + "description": "Clusev terminal sidecar — bridges a browser WebSocket to an SSH PTY (per server and the Clusev host).", "main": "server.js", "scripts": { "start": "node server.js" }, "dependencies": { "ssh2": "^1.15.0", - "ws": "^8.18.0", - "node-pty": "^1.0.0" + "ws": "^8.18.0" } } diff --git a/docker/terminal/server.js b/docker/terminal/server.js index fd24bd6..4ac83c1 100644 --- a/docker/terminal/server.js +++ b/docker/terminal/server.js @@ -16,13 +16,10 @@ const http = require('http'); const { WebSocketServer } = require('ws'); const { Client } = require('ssh2'); -const pty = require('node-pty'); const PORT = parseInt(process.env.TERMINAL_PORT || '3000', 10); const APP_URL = (process.env.APP_INTERNAL_URL || 'http://app:80').replace(/\/$/, ''); const SECRET = process.env.TERMINAL_SIDECAR_SECRET || ''; -const HOST_SHELL = process.env.TERMINAL_HOST_SHELL || 'bash'; -const HOST_CWD = process.env.TERMINAL_HOST_CWD || '/home/node'; const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); @@ -66,27 +63,6 @@ function bindInput(ws, io) { }); } -function startHost(ws, size) { - let p; - try { - p = pty.spawn(HOST_SHELL, ['-l'], { - name: 'xterm-256color', cwd: HOST_CWD, cols: size.cols, rows: size.rows, - // HOME = the landing dir so the shell opens at ~ (not a bare absolute path) and the prompt - // reads cleanly; the container's `hostname: clusev` makes \h show "clusev", not the hash. - env: { ...process.env, TERM: 'xterm-256color', HOME: HOST_CWD }, - }); - } catch (e) { - send(ws, { type: 'error', data: 'Shell konnte nicht gestartet werden.' }); - ws.close(); - return; - } - send(ws, { type: 'ready' }); - p.onData((d) => send(ws, { type: 'data', data: d })); - p.onExit(() => { send(ws, { type: 'exit' }); try { ws.close(); } catch (_) {} }); - bindInput(ws, { write: (d) => p.write(d), resize: (c, r) => { try { p.resize(c, r); } catch (_) {} } }); - ws.on('close', () => { try { p.kill(); } catch (_) {} }); -} - function startSsh(ws, spec, size) { const conn = new Client(); let stream = null; @@ -161,8 +137,9 @@ wss.on('connection', async (ws, req) => { return; } - if (spec && spec.kind === 'host') startHost(ws, size); - else if (spec && spec.kind === 'server' && spec.host && spec.username) startSsh(ws, spec, size); + // resolve() always returns kind:'server' (the host terminal resolves to an SSH login too), so the + // sidecar only ever opens an SSH PTY — there is no local-shell path. + if (spec && spec.kind === 'server' && spec.host && spec.username) startSsh(ws, spec, size); else { send(ws, { type: 'error', data: 'Kein gültiges Ziel.' }); ws.close(); } }); diff --git a/lang/de/accounts.php b/lang/de/accounts.php index 917718c..7f27c5d 100644 --- a/lang/de/accounts.php +++ b/lang/de/accounts.php @@ -10,8 +10,6 @@ return [ // List 'you' => 'du', - 'twofa_on' => '2FA aktiv', - 'twofa_off' => '2FA aus', 'none' => 'Keine Konten', // Create modal @@ -41,8 +39,6 @@ return [ 'remove_heading' => 'Konto entfernen', 'remove_body' => 'Löscht das Konto dauerhaft und beendet alle seine Sitzungen. Das kann nicht rückgängig gemacht werden.', 'remove_notify' => 'Konto entfernt.', - 'cannot_remove_self' => 'Du kannst dein eigenes Konto nicht entfernen.', - 'cannot_remove_last' => 'Du kannst das letzte verbleibende Konto nicht entfernen.', // Force logout (R5 confirm) 'logout' => 'Überall abmelden', diff --git a/lang/de/auth.php b/lang/de/auth.php index 050f112..b6d8f8b 100644 --- a/lang/de/auth.php +++ b/lang/de/auth.php @@ -91,9 +91,7 @@ return [ 'recovery_warning' => 'Speichere diese Codes jetzt sicher ab — jeder Code funktioniert genau einmal. Lade sie herunter, solange sie angezeigt werden.', 'recovery_download' => 'Als Datei herunterladen', 'recovery_regenerate' => 'Neu erzeugen', - 'recovery_regenerate_confirm' => 'Neue Codes erzeugen? Die alten werden ungültig.', 'recovery_regenerated' => 'Neue Backup-Codes erzeugt.', - 'recovery_done' => 'Gespeichert — weiter', 'recovery_manage' => 'Backup-Codes verwalten', 'recovery_hidden_notice' => 'Aus Sicherheitsgründen werden Backup-Codes nur einmal bei der Erstellung angezeigt. Erzeuge einen neuen Satz, um ihn zu sehen — die alten werden dabei ungültig.', diff --git a/lang/de/common.php b/lang/de/common.php index ae2a059..4d6a6e9 100644 --- a/lang/de/common.php +++ b/lang/de/common.php @@ -15,11 +15,8 @@ return [ 'disable' => 'Deaktivieren', 'lock' => 'Sperren', 'unlock' => 'Entsperren', - 'back' => 'Zurück', - 'retry' => 'Erneut versuchen', 'confirm' => 'Bestätigen', 'open' => 'Öffnen', - 'more' => 'mehr', 'apply' => 'Anwenden', 'or' => 'oder', @@ -32,7 +29,6 @@ return [ 'secure' => 'sicher', 'unsupported' => 'n. v.', 'unknown' => 'unbekannt', - 'loading' => 'Lädt…', 'none' => '—', // Generic diff --git a/lang/de/servers.php b/lang/de/servers.php index 124e800..4df4f2d 100644 --- a/lang/de/servers.php +++ b/lang/de/servers.php @@ -92,14 +92,10 @@ return [ // ── Show: firewall rules ────────────────────────────────────────────── 'firewall_title' => 'Firewall-Regeln', - 'firewall_unavailable' => 'Nicht verfügbar', 'firewall_sub_active' => ' · aktiv', 'firewall_sub_inactive' => ' · inaktiv', - 'firewall_sub_not_installed' => ' · nicht installiert', 'firewall_add_rule' => 'Regel', - 'firewall_not_supported' => 'Firewall-Verwaltung ist auf diesem System nicht verfügbar.', 'firewall_read_error' => 'Firewall-Status konnte nicht gelesen werden (Verbindung/Rechte). Bitte später erneut laden.', - 'firewall_tool_not_installed' => ':tool ist nicht installiert.', 'firewalld_inactive' => 'firewalld ist installiert, aber inaktiv.', 'firewall_default' => 'Standard', 'firewall_incoming' => 'eingehend: :value', @@ -112,15 +108,11 @@ return [ // ── Show: fail2ban status ───────────────────────────────────────────── 'fail2ban_title' => 'fail2ban-Status', - 'fail2ban_unavailable' => 'Nicht verfügbar', 'fail2ban_sub_active' => 'aktiv · :count gesperrt', 'fail2ban_sub_installed_inactive' => 'installiert · inaktiv', - 'fail2ban_sub_not_installed' => 'nicht installiert', 'fail2ban_ban_ip' => 'IP sperren', - 'fail2ban_not_supported' => 'fail2ban ist auf diesem System nicht verfügbar.', 'fail2ban_read_error' => 'fail2ban-Status konnte nicht gelesen werden. Bitte später erneut laden.', 'fail2ban_state_installed_inactive' => 'fail2ban ist installiert, aber inaktiv.', - 'fail2ban_state_not_installed' => 'fail2ban ist nicht installiert.', 'fail2ban_banned' => 'gebannt: :count', 'fail2ban_failed' => 'Fehlversuche: :count', 'fail2ban_no_banned' => 'Keine gebannten IPs.', diff --git a/lang/de/settings.php b/lang/de/settings.php index a10b6f7..1715975 100644 --- a/lang/de/settings.php +++ b/lang/de/settings.php @@ -5,8 +5,6 @@ return [ // Identity header 'account' => 'Konto', 'role_admin' => 'Administrator', - 'two_factor_on' => '2FA aktiv', - 'two_factor_off' => '2FA aus', // Section nav tabs 'tab_profile' => 'Profil', @@ -53,13 +51,6 @@ return [ 'disable_2fa_notify' => 'Authenticator entfernt.', // Stub tabs (filled in 0.9.0) - 'coming_soon' => 'Wird in 0.9.0 ergänzt.', - 'users_title' => 'Benutzer', - 'users_subtitle' => 'Konten und Rollen verwalten', - 'sessions_title' => 'Sitzungen', - 'sessions_subtitle' => 'Aktive Anmeldungen', - 'email_title' => 'E-Mail', - 'email_subtitle' => 'SMTP und Benachrichtigungen', // Page title 'title' => 'Einstellungen — Clusev', diff --git a/lang/en/accounts.php b/lang/en/accounts.php index e357007..86cef08 100644 --- a/lang/en/accounts.php +++ b/lang/en/accounts.php @@ -10,8 +10,6 @@ return [ // List 'you' => 'you', - 'twofa_on' => '2FA on', - 'twofa_off' => '2FA off', 'none' => 'No accounts', // Create modal @@ -41,8 +39,6 @@ return [ 'remove_heading' => 'Remove account', 'remove_body' => 'This permanently deletes the account and ends all of its sessions. This cannot be undone.', 'remove_notify' => 'Account removed.', - 'cannot_remove_self' => 'You cannot remove your own account.', - 'cannot_remove_last' => 'You cannot remove the last remaining account.', // Force logout (R5 confirm) 'logout' => 'Sign out everywhere', diff --git a/lang/en/auth.php b/lang/en/auth.php index 8722cdb..9f21318 100644 --- a/lang/en/auth.php +++ b/lang/en/auth.php @@ -91,9 +91,7 @@ return [ 'recovery_warning' => 'Save these codes somewhere safe now — each works exactly once. Download them while they are shown.', 'recovery_download' => 'Download as file', 'recovery_regenerate' => 'Regenerate', - 'recovery_regenerate_confirm' => 'Generate new codes? The old ones stop working.', 'recovery_regenerated' => 'New backup codes generated.', - 'recovery_done' => 'Saved — continue', 'recovery_manage' => 'Manage backup codes', 'recovery_hidden_notice' => 'For security, backup codes are shown only once, at creation. Generate a new set to see them — the old ones stop working.', diff --git a/lang/en/common.php b/lang/en/common.php index 4156052..6204ac9 100644 --- a/lang/en/common.php +++ b/lang/en/common.php @@ -15,11 +15,8 @@ return [ 'disable' => 'Disable', 'lock' => 'Lock', 'unlock' => 'Unlock', - 'back' => 'Back', - 'retry' => 'Try again', 'confirm' => 'Confirm', 'open' => 'Open', - 'more' => 'more', 'apply' => 'Apply', 'or' => 'or', @@ -32,7 +29,6 @@ return [ 'secure' => 'secure', 'unsupported' => 'n/a', 'unknown' => 'unknown', - 'loading' => 'Loading…', 'none' => '—', // Generic diff --git a/lang/en/servers.php b/lang/en/servers.php index 1f732b8..835ef19 100644 --- a/lang/en/servers.php +++ b/lang/en/servers.php @@ -92,14 +92,10 @@ return [ // ── Show: firewall rules ────────────────────────────────────────────── 'firewall_title' => 'Firewall rules', - 'firewall_unavailable' => 'Not available', 'firewall_sub_active' => ' · active', 'firewall_sub_inactive' => ' · inactive', - 'firewall_sub_not_installed' => ' · not installed', 'firewall_add_rule' => 'Rule', - 'firewall_not_supported' => 'Firewall management is not available on this system.', 'firewall_read_error' => 'Firewall status could not be read (connection/permissions). Please reload later.', - 'firewall_tool_not_installed' => ':tool is not installed.', 'firewalld_inactive' => 'firewalld is installed but inactive.', 'firewall_default' => 'Default', 'firewall_incoming' => 'incoming: :value', @@ -112,15 +108,11 @@ return [ // ── Show: fail2ban status ───────────────────────────────────────────── 'fail2ban_title' => 'fail2ban status', - 'fail2ban_unavailable' => 'Not available', 'fail2ban_sub_active' => 'active · :count banned', 'fail2ban_sub_installed_inactive' => 'installed · inactive', - 'fail2ban_sub_not_installed' => 'not installed', 'fail2ban_ban_ip' => 'Ban IP', - 'fail2ban_not_supported' => 'fail2ban is not available on this system.', 'fail2ban_read_error' => 'fail2ban status could not be read. Please reload later.', 'fail2ban_state_installed_inactive' => 'fail2ban is installed but inactive.', - 'fail2ban_state_not_installed' => 'fail2ban is not installed.', 'fail2ban_banned' => 'banned: :count', 'fail2ban_failed' => 'failed attempts: :count', 'fail2ban_no_banned' => 'No banned IPs.', diff --git a/lang/en/settings.php b/lang/en/settings.php index 9ce9edb..27790b8 100644 --- a/lang/en/settings.php +++ b/lang/en/settings.php @@ -5,8 +5,6 @@ return [ // Identity header 'account' => 'Account', 'role_admin' => 'Administrator', - 'two_factor_on' => '2FA on', - 'two_factor_off' => '2FA off', // Section nav tabs 'tab_profile' => 'Profile', @@ -53,13 +51,6 @@ return [ 'disable_2fa_notify' => 'Authenticator removed.', // Stub tabs (filled in 0.9.0) - 'coming_soon' => 'Coming in 0.9.0.', - 'users_title' => 'Users', - 'users_subtitle' => 'Manage accounts and roles', - 'sessions_title' => 'Sessions', - 'sessions_subtitle' => 'Active sign-ins', - 'email_title' => 'Email', - 'email_subtitle' => 'SMTP and notifications', // Page title 'title' => 'Settings — Clusev', diff --git a/resources/views/components/server-item.blade.php b/resources/views/components/server-item.blade.php deleted file mode 100644 index 870248c..0000000 --- a/resources/views/components/server-item.blade.php +++ /dev/null @@ -1,26 +0,0 @@ -@props(['name', 'ip', 'status' => 'online', 'cpu' => 0, 'mem' => 0, 'os' => null]) -@php - $label = ['online' => __('common.online'), 'warning' => __('common.warning'), 'offline' => __('common.offline'), 'pending' => __('servers.status_pending')][$status] ?? ucfirst($status); -@endphp - - -
-

{{ $name }}

-

{{ $ip }}@if ($os) · {{ $os }}@endif

-
- - {{ $label }} -