diff --git a/frontend/src/components/AgentPanel.jsx b/frontend/src/components/AgentPanel.jsx index 935d320..d9e293a 100644 --- a/frontend/src/components/AgentPanel.jsx +++ b/frontend/src/components/AgentPanel.jsx @@ -1,6 +1,7 @@ import { useEffect, useState } from 'react' import api from '../api/client' import StatusBadge from './StatusBadge' +import { copyToClipboard } from '../utils/clipboard' import { X, Cpu, MemoryStick, HardDrive, Clock, Network, Shield, Route, Globe, Key, Download, Terminal, Wifi, Search, Plus, Trash2, Copy, Check, ExternalLink, Unplug, Plug, MonitorSmartphone, @@ -565,6 +566,7 @@ function TunnelTab({ agentId, agent }) { const [customHost, setCustomHost] = useState('127.0.0.1') const [customPort, setCustomPort] = useState('') const [error, setError] = useState('') + const [sshCopied, setSshCopied] = useState(null) const backendHost = '192.168.85.13' @@ -594,10 +596,17 @@ function TunnelTab({ agentId, agent }) { const data = resp?.data || resp // Reload tunnel list loadTunnels() - // If WebGUI, open in new tab - if (targetPort === 4444 && data?.proxy_port) { + // Auto-open for known services + if (data?.proxy_port) { setTimeout(() => { - window.open(`https://${backendHost}:${data.proxy_port}`, '_blank') + if (targetPort === 4444) { + window.open(`https://${backendHost}:${data.proxy_port}`, '_blank') + } else if (targetPort === 22) { + const cmd = `ssh root@${backendHost} -p ${data.proxy_port}` + copyToClipboard(cmd) + setSshCopied(data.proxy_port) + setTimeout(() => setSshCopied(null), 4000) + } }, 1000) } return data @@ -687,6 +696,12 @@ function TunnelTab({ agentId, agent }) { )} + {sshCopied && ( +
+ + SSH-Befehl kopiert — in CMD/Terminal einfuegen und ausfuehren +
+ )} {error &&
{error}
} {/* Active Tunnels */} @@ -719,6 +734,7 @@ function TunnelTab({ agentId, agent }) { : t.target || '—' const proxyUrl = proxyPort ? `${backendHost}:${proxyPort}` : '—' const isWebGUI = t.target_port === 4444 || t.target_port === '4444' + const isSSH = t.target_port === 22 || t.target_port === '22' return ( @@ -737,6 +753,25 @@ function TunnelTab({ agentId, agent }) { )} + {isSSH && proxyPort && ( + + )} @@ -832,7 +867,7 @@ function WireGuardTab({ agentId, sys }) { } const copyConfig = (text) => { - navigator.clipboard.writeText(text) + copyToClipboard(text) setCopied(true) setTimeout(() => setCopied(false), 2000) } diff --git a/frontend/src/components/InstallGuide.jsx b/frontend/src/components/InstallGuide.jsx index 2b58124..29a4a31 100644 --- a/frontend/src/components/InstallGuide.jsx +++ b/frontend/src/components/InstallGuide.jsx @@ -1,5 +1,6 @@ import { useState } from 'react' import { Copy, Check, Terminal, Monitor } from 'lucide-react' +import { copyToClipboard } from '../utils/clipboard' const BACKEND_URL = 'https://dsbmueller.spdns.org:8443' const API_KEY = '01532e5a7c9e70bf2757df77a2f5b9b9' @@ -9,7 +10,7 @@ export default function InstallGuide({ agentName, platform: initialPlatform }) { const [copied, setCopied] = useState(null) const copyText = (text, id) => { - navigator.clipboard.writeText(text) + copyToClipboard(text) setCopied(id) setTimeout(() => setCopied(null), 2000) } diff --git a/frontend/src/pages/Agents.jsx b/frontend/src/pages/Agents.jsx index c64f71a..3efe9fe 100644 --- a/frontend/src/pages/Agents.jsx +++ b/frontend/src/pages/Agents.jsx @@ -267,23 +267,23 @@ export default function Agents() {
Laden...
) : (
- +
- + {isColVisible('customer') && } {isColVisible('name') && } - {isColVisible('hostname') && } + {isColVisible('hostname') && } {isColVisible('version') && } - {isColVisible('wanip') && } - {isColVisible('lanip') && } + {isColVisible('wanip') && } + {isColVisible('lanip') && } {isColVisible('uptime') && } {isColVisible('cpu') && } {isColVisible('ram') && } {isColVisible('disk') && } - {isColVisible('lastresponse') && } - {isColVisible('gateways') && } - {isColVisible('agentversion') && } + {isColVisible('lastresponse') && } + {isColVisible('gateways') && } + {isColVisible('agentversion') && } @@ -293,37 +293,30 @@ export default function Agents() { onClick={() => setSelectedId(a.id)} className={`hover:bg-gray-800/50 transition-colors cursor-pointer ${selectedId === a.id ? 'bg-gray-800/70' : ''}`} > - + {isColVisible('customer') && ( - )} {isColVisible('name') && ( - + )} - {isColVisible('hostname') && } - {isColVisible('version') && } - {isColVisible('wanip') && } - {isColVisible('lanip') && } - {isColVisible('uptime') && } - {isColVisible('cpu') && } - {isColVisible('ram') && } - {isColVisible('disk') && } + {isColVisible('hostname') && } + {isColVisible('version') && } + {isColVisible('wanip') && } + {isColVisible('lanip') && } + {isColVisible('uptime') && } + {isColVisible('cpu') && } + {isColVisible('ram') && } + {isColVisible('disk') && } {isColVisible('lastresponse') && ( - )} {isColVisible('gateways') && ( - )} {isColVisible('agentversion') && ( - + )} ))} @@ -463,14 +456,14 @@ function gwDotColor(status) { } function MiniBar({ value }) { - if (value === null || value === undefined) return + if (value === null || value === undefined) return const color = value > 90 ? 'bg-red-500' : value > 70 ? 'bg-yellow-500' : value > 50 ? 'bg-blue-500' : 'bg-green-500' return ( -
-
+
+
- {value.toFixed(0)}% + {value.toFixed(0)}%
) } diff --git a/frontend/src/pages/Firmware.jsx b/frontend/src/pages/Firmware.jsx index 3daa37f..a93356d 100644 --- a/frontend/src/pages/Firmware.jsx +++ b/frontend/src/pages/Firmware.jsx @@ -1,6 +1,7 @@ import { useEffect, useState, useRef } from 'react' import api from '../api/client' import { Upload, RefreshCw, CheckCircle, AlertTriangle, ArrowUpCircle, X, Monitor, Server, Cpu, Package, Copy, Check } from 'lucide-react' +import { copyToClipboard } from '../utils/clipboard' const PLATFORMS = [ { id: 'freebsd', label: 'FreeBSD / OPNsense', icon: Server }, @@ -245,7 +246,7 @@ export default function Firmware() {
{`fetch --no-verify-peer --no-verify-hostname -o /tmp/installer.zip "${BACKEND_URL}/api/v1/installer/download?platform=freebsd&api_key=01532e5a7c9e70bf2757df77a2f5b9b9" && cd /tmp && unzip -o installer.zip && sh /tmp/install.sh`}
HOSTNAMEHOSTNAMEWAN IPLAN IPWAN IPLAN IPLETZTER KONTAKTGATEWAYSAGENTLETZTER KONTAKTGATEWAYSAGENT
+ {a.customer ? {a.customerNumber} : } - - - {a.platform === 'linux' ? 'LNX' : 'OPN'} - - {a.name} - - {a.name}{a.hostname}{a.version || '—'}{a.wanIp || '—'}{a.lanIp || '—'}{a.uptime ? formatUptime(a.uptime) : '—'}{a.hostname}{a.version || '—'}{a.wanIp || '—'}{a.lanIp || '—'}{a.uptime ? formatUptime(a.uptime) : '—'} + {a.last_heartbeat ? new Date(a.last_heartbeat).toLocaleString('de-DE') : '—'} + {a.gateways.length > 0 ? (
{a.gateways.map((gw) => ( @@ -337,7 +330,7 @@ export default function Agents() {
{a.agent_version || '—'}{a.agent_version || '—'}