install.sh: auto-download from backend, InstallGuide component for frontend dialogs
This commit is contained in:
@@ -2,7 +2,8 @@ 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, Plus, Copy, Check, X } from 'lucide-react'
|
||||
import { Search, ChevronUp, ChevronDown, Plus, X } from 'lucide-react'
|
||||
import InstallGuide from '../components/InstallGuide'
|
||||
|
||||
export default function Agents() {
|
||||
const [agents, setAgents] = useState([])
|
||||
@@ -276,10 +277,6 @@ function AddDeviceDialog({ customers, result, onAdd, onClose }) {
|
||||
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 }
|
||||
@@ -293,39 +290,9 @@ function AddDeviceDialog({ customers, result, onAdd, onClose }) {
|
||||
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 das Geraet 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="bg-gray-900 border border-gray-700 rounded-lg shadow-xl w-full max-w-2xl mx-4 max-h-[90vh] overflow-y-auto" 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">Geraet hinzufuegen</h2>
|
||||
<button onClick={onClose} className="text-gray-500 hover:text-white"><X className="w-5 h-5" /></button>
|
||||
@@ -373,37 +340,7 @@ ssh root@<FIREWALL-IP> '/bin/sh /tmp/install.sh'
|
||||
<div className="bg-green-900/30 border border-green-800 rounded p-3 text-sm text-green-300">
|
||||
Geraet "{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>
|
||||
<InstallGuide agentName={result.name} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,8 @@ import { useEffect, useState, useMemo } from 'react'
|
||||
import api from '../api/client'
|
||||
import StatusBadge from '../components/StatusBadge'
|
||||
import ProxmoxPanel from '../components/ProxmoxPanel'
|
||||
import { Search, ChevronUp, ChevronDown, Plus, Copy, Check, X } from 'lucide-react'
|
||||
import { Search, ChevronUp, ChevronDown, Plus, X } from 'lucide-react'
|
||||
import InstallGuide from '../components/InstallGuide'
|
||||
|
||||
export default function ProxmoxServers() {
|
||||
const [agents, setAgents] = useState([])
|
||||
@@ -273,10 +274,6 @@ function AddServerDialog({ customers, result, onAdd, onClose }) {
|
||||
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 }
|
||||
@@ -290,81 +287,9 @@ function AddServerDialog({ customers, result, onAdd, onClose }) {
|
||||
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 = `# Proxmox/Linux Server Installation:
|
||||
|
||||
# 1. Agent + Updater auf den Server kopieren:
|
||||
scp build/rmm-agent-linux build/rmm-updater-linux root@<SERVER-IP>:/tmp/
|
||||
|
||||
# 2. Installation auf dem Server:
|
||||
ssh root@<SERVER-IP> 'bash -s' << 'INSTALL'
|
||||
mkdir -p /usr/local/bin /etc/rmm
|
||||
|
||||
cp /tmp/rmm-agent-linux /usr/local/bin/rmm-agent
|
||||
cp /tmp/rmm-updater-linux /usr/local/bin/rmm-updater
|
||||
chmod +x /usr/local/bin/rmm-agent /usr/local/bin/rmm-updater
|
||||
|
||||
# Config erstellen:
|
||||
cat > /etc/rmm/config.yaml << EOF
|
||||
backend_url: ${BACKEND_URL}
|
||||
api_key: ${API_KEY}
|
||||
agent_name: ${name || '<NAME>'}
|
||||
heartbeat_interval: 30
|
||||
insecure: true
|
||||
EOF
|
||||
|
||||
# Agent Service:
|
||||
cat > /etc/systemd/system/rmm-agent.service << EOF
|
||||
[Unit]
|
||||
Description=RMM Agent
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=/usr/local/bin/rmm-agent
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# Updater Service:
|
||||
cat > /etc/systemd/system/rmm-updater.service << EOF
|
||||
[Unit]
|
||||
Description=RMM Updater
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=/usr/local/bin/rmm-updater
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now rmm-agent rmm-updater
|
||||
INSTALL`
|
||||
|
||||
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="bg-gray-900 border border-gray-700 rounded-lg shadow-xl w-full max-w-2xl mx-4 max-h-[90vh] overflow-y-auto" 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">Server hinzufuegen</h2>
|
||||
<button onClick={onClose} className="text-gray-500 hover:text-white"><X className="w-5 h-5" /></button>
|
||||
@@ -412,37 +337,7 @@ INSTALL`
|
||||
<div className="bg-green-900/30 border border-green-800 rounded p-3 text-sm text-green-300">
|
||||
Server "{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>
|
||||
<InstallGuide agentName={result.name} platform="linux" />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user