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') && HOSTNAME | }
+ {isColVisible('hostname') && HOSTNAME | }
{isColVisible('version') && }
- {isColVisible('wanip') && WAN IP | }
- {isColVisible('lanip') && LAN IP | }
+ {isColVisible('wanip') && WAN IP | }
+ {isColVisible('lanip') && LAN IP | }
{isColVisible('uptime') && }
{isColVisible('cpu') && }
{isColVisible('ram') && }
{isColVisible('disk') && }
- {isColVisible('lastresponse') && LETZTER KONTAKT | }
- {isColVisible('gateways') && GATEWAYS | }
- {isColVisible('agentversion') && AGENT | }
+ {isColVisible('lastresponse') && LETZTER KONTAKT | }
+ {isColVisible('gateways') && GATEWAYS | }
+ {isColVisible('agentversion') && AGENT | }
@@ -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') && (
-
+ |
{a.customer ? {a.customerNumber} : —}
|
)}
{isColVisible('name') && (
-
-
-
- {a.platform === 'linux' ? 'LNX' : 'OPN'}
-
- {a.name}
-
- |
+ {a.name} |
)}
- {isColVisible('hostname') && {a.hostname} | }
- {isColVisible('version') && {a.version || '—'} | }
- {isColVisible('wanip') && {a.wanIp || '—'} | }
- {isColVisible('lanip') && {a.lanIp || '—'} | }
- {isColVisible('uptime') && {a.uptime ? formatUptime(a.uptime) : '—'} | }
- {isColVisible('cpu') && | }
- {isColVisible('ram') && | }
- {isColVisible('disk') && | }
+ {isColVisible('hostname') && {a.hostname} | }
+ {isColVisible('version') && {a.version || '—'} | }
+ {isColVisible('wanip') && {a.wanIp || '—'} | }
+ {isColVisible('lanip') && {a.lanIp || '—'} | }
+ {isColVisible('uptime') && {a.uptime ? formatUptime(a.uptime) : '—'} | }
+ {isColVisible('cpu') && | }
+ {isColVisible('ram') && | }
+ {isColVisible('disk') && | }
{isColVisible('lastresponse') && (
-
+ |
{a.last_heartbeat ? new Date(a.last_heartbeat).toLocaleString('de-DE') : '—'}
|
)}
{isColVisible('gateways') && (
-
+ |
{a.gateways.length > 0 ? (
{a.gateways.map((gw) => (
@@ -337,7 +330,7 @@ export default function Agents() {
|
)}
{isColVisible('agentversion') && (
- {a.agent_version || '—'} |
+ {a.agent_version || '—'} |
)}
))}
@@ -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`}
|