Fix: SettingsController in install.sh aktualisiert (rmmagent[general] Parsing)

This commit is contained in:
cynfo3000
2026-03-01 01:37:35 +01:00
parent 77ec200b42
commit c114df215a
11 changed files with 534 additions and 60 deletions
+164 -1
View File
@@ -2,7 +2,7 @@ import { useEffect, useState, useMemo } from 'react'
import api from '../api/client'
import StatusBadge from '../components/StatusBadge'
import AgentPanel from '../components/AgentPanel'
import { Search, ChevronUp, ChevronDown } from 'lucide-react'
import { Search, ChevronUp, ChevronDown, Plus, Copy, Check, X } from 'lucide-react'
export default function Agents() {
const [agents, setAgents] = useState([])
@@ -12,6 +12,8 @@ export default function Agents() {
const [sortKey, setSortKey] = useState('customer')
const [sortDir, setSortDir] = useState('asc')
const [selectedId, setSelectedId] = useState(null)
const [showAddDialog, setShowAddDialog] = useState(false)
const [addResult, setAddResult] = useState(null)
const reload = () => {
Promise.all([api.getAgents(), api.getCustomers()])
@@ -137,6 +139,12 @@ export default function Agents() {
<div className="space-y-4">
<div className="flex items-center justify-between">
<h1 className="text-xl font-bold text-white">Firewalls ({agents.length})</h1>
<button
onClick={() => { setShowAddDialog(true); setAddResult(null) }}
className="flex items-center gap-2 px-3 py-1.5 bg-orange-600 hover:bg-orange-500 text-white text-sm rounded transition-colors"
>
<Plus className="w-4 h-4" /> Firewall hinzufuegen
</button>
</div>
<div className="flex items-center gap-3">
@@ -234,6 +242,161 @@ export default function Agents() {
onReload={reload}
/>
)}
{/* Firewall hinzufuegen Dialog */}
{showAddDialog && (
<AddFirewallDialog
customers={customers}
result={addResult}
onAdd={async (name, custId) => {
const res = await api.preRegisterAgent(name, custId)
setAddResult(res)
reload()
}}
onClose={() => { setShowAddDialog(false); setAddResult(null) }}
/>
)}
</div>
)
}
function AddFirewallDialog({ customers, result, onAdd, onClose }) {
const [name, setName] = useState('')
const [custId, setCustId] = useState('')
const [error, setError] = useState('')
const [submitting, setSubmitting] = useState(false)
const [copied, setCopied] = useState(null)
const BACKEND_URL = 'https://192.168.85.13:8443'
const API_KEY = '01532e5a7c9e70bf2757df77a2f5b9b9'
const handleSubmit = async () => {
if (!name.trim()) { setError('Name ist erforderlich'); return }
setError('')
setSubmitting(true)
try {
await onAdd(name.trim(), custId ? parseInt(custId) : null)
} catch (e) {
setError(e.message)
}
setSubmitting(false)
}
const copyText = (text, id) => {
navigator.clipboard.writeText(text)
setCopied(id)
setTimeout(() => setCopied(null), 2000)
}
const CopyBtn = ({ text, id }) => (
<button onClick={() => copyText(text, id)} className="ml-2 text-gray-500 hover:text-orange-400 transition-colors">
{copied === id ? <Check className="w-3.5 h-3.5 text-green-400" /> : <Copy className="w-3.5 h-3.5" />}
</button>
)
const installCmd = `# Vom Mac Studio aus ausfuehren:
# 1. Ins RMM-Verzeichnis wechseln:
cd /Users/cynfo/.openclaw/workspace/rmm
# 2. Agent + Updater + Installer auf die Firewall kopieren:
scp build/rmm-agent build/rmm-updater root@<FIREWALL-IP>:/tmp/
scp opnsense-plugin/install.sh root@<FIREWALL-IP>:/tmp/
# 3. Installation starten:
ssh root@<FIREWALL-IP> '/bin/sh /tmp/install.sh'
# 4. Im OPNsense WebGUI unter Services > RMM Agent:
# Backend URL: ${BACKEND_URL}
# API Key: ${API_KEY}
# Agent Name: ${name || '<NAME>'}
# -> Save & Apply`
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60" onClick={onClose}>
<div className="bg-gray-900 border border-gray-700 rounded-lg shadow-xl w-full max-w-2xl mx-4" onClick={e => e.stopPropagation()}>
<div className="flex items-center justify-between px-5 py-3 border-b border-gray-800">
<h2 className="text-lg font-semibold text-white">Firewall hinzufuegen</h2>
<button onClick={onClose} className="text-gray-500 hover:text-white"><X className="w-5 h-5" /></button>
</div>
<div className="px-5 py-4 space-y-4">
{!result ? (
<>
<div>
<label className="block text-sm text-gray-400 mb-1">Name *</label>
<input
type="text"
value={name}
onChange={e => setName(e.target.value)}
placeholder="z.B. OPN-KUNDE.intra.example.net"
className="w-full bg-gray-800 border border-gray-700 rounded px-3 py-2 text-sm text-white placeholder-gray-600 focus:outline-none focus:border-orange-500"
autoFocus
onKeyDown={e => e.key === 'Enter' && handleSubmit()}
/>
</div>
<div>
<label className="block text-sm text-gray-400 mb-1">Kunde (optional)</label>
<select
value={custId}
onChange={e => setCustId(e.target.value)}
className="w-full bg-gray-800 border border-gray-700 rounded px-3 py-2 text-sm text-white focus:outline-none focus:border-orange-500"
>
<option value="">Kein Kunde</option>
{customers.map(c => (
<option key={c.id} value={c.id}>{c.number} — {c.name}</option>
))}
</select>
</div>
{error && <p className="text-red-400 text-sm">{error}</p>}
<button
onClick={handleSubmit}
disabled={submitting}
className="w-full py-2 bg-orange-600 hover:bg-orange-500 disabled:opacity-50 text-white text-sm font-medium rounded transition-colors"
>
{submitting ? 'Wird angelegt...' : 'Firewall anlegen'}
</button>
</>
) : (
<>
<div className="bg-green-900/30 border border-green-800 rounded p-3 text-sm text-green-300">
Firewall "{result.name}" erfolgreich angelegt (ID: {result.id?.substring(0, 12)}...)
</div>
<div>
<h3 className="text-sm font-medium text-white mb-2">Installationsanleitung</h3>
<div className="bg-gray-950 rounded p-3 text-xs font-mono text-gray-300 whitespace-pre-wrap relative">
{installCmd}
<button
onClick={() => copyText(installCmd, 'install')}
className="absolute top-2 right-2 text-gray-600 hover:text-orange-400 transition-colors"
>
{copied === 'install' ? <Check className="w-4 h-4 text-green-400" /> : <Copy className="w-4 h-4" />}
</button>
</div>
</div>
<div className="grid grid-cols-2 gap-3 text-sm">
<div>
<span className="text-gray-500">Backend URL:</span>
<span className="text-white ml-2">{BACKEND_URL}</span>
<CopyBtn text={BACKEND_URL} id="url" />
</div>
<div>
<span className="text-gray-500">API Key:</span>
<span className="text-white ml-2 font-mono text-xs">{API_KEY}</span>
<CopyBtn text={API_KEY} id="key" />
</div>
</div>
<p className="text-xs text-gray-500">
Der Agent verbindet sich nach der Installation automatisch mit dem Backend.
Hostname und IP werden beim ersten Heartbeat aktualisiert.
</p>
</>
)}
</div>
</div>
</div>
)
}