2026-03-19 12:42:52 +01:00
|
|
|
import { useState } from 'react'
|
|
|
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
feat: Phase 9 — Offline Detection, API Key Auth, Agent Self-Update
Offline Detection (9.1):
- AgentOfflineDetectorService: BackgroundService, prüft alle 60s
ob Agents seit >5 min kein Heartbeat hatten → Status=Offline
- IServiceScopeFactory für korrektes Scoped-DI im Singleton
- SignalR-Push AgentStatusChanged bei jeder Offline-Markierung
API Key Auth (9.2):
- ApiKeyMiddleware: prüft X-Api-Key Header gegen Security:ApiKey Config
- Deaktiviert wenn ApiKey leer (Dev-Modus), Swagger/hubs bypassed
- Frontend: getApiKey() aus localStorage, automatisch in allen Requests
- Settings-Modal in Sidebar: API-Key eingeben + maskiert anzeigen
Agent Self-Update (9.3):
- internal/updater/updater.go: CheckForUpdate() + Update()
Download, SHA256-Verify, Windows Batch-Neustart / Linux Shell-Neustart
- AgentReleasesController: GET /api/v1/agent/releases/latest,
GET /api/v1/agent/releases/download/{platform}
- AgentReleaseOptions: LatestVersion, ReleasePath, Checksum in appsettings
- executeCommand() erhält cfg *Config statt agentID string
(für ServerAddress-Ableitung im UpdateAgent-Case)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 15:41:24 +01:00
|
|
|
import { LayoutDashboard, Ticket, Bell, Package, Network, Menu, X, Settings } from 'lucide-react'
|
2026-03-19 12:42:52 +01:00
|
|
|
import { DashboardPage } from './pages/DashboardPage'
|
|
|
|
|
import { AgentDetailPage } from './pages/AgentDetailPage'
|
|
|
|
|
import TicketsPage from './pages/TicketsPage'
|
feat: implement Phase 5 — Alerting & Monitoring
Backend:
- AlertEvaluationService: evaluates metrics against AlertRules after each heartbeat
- Supports cpu_usage_percent and memory_usage_percent metric paths
- Operators: >, >=, <, <=, ==
- 15-minute dedup window to prevent alert spam
- AlertRulesController: full CRUD for alert rules (GET/POST/PUT/DELETE)
- AlertsController: list with acknowledged filter + POST acknowledge endpoint
- IRmmHubClient: added AlertTriggered push method
- Program.cs: AlertEvaluationService registered as Scoped
Frontend:
- AlertsPage: two-tab layout (active alerts + rules)
- Alerts tab: severity badges, acknowledge button, all/unack/ack filter
- Rules tab: condition display, enabled toggle, delete with confirm
- Create rule modal with MetricPath/Operator/Threshold/Severity selects
- api/types.ts: AlertRule, AlertItem, CreateAlertRuleRequest types
- api/client.ts: alertRulesApi and alertsApi
- useAgentSignalR: handles AlertTriggered → invalidates alerts query
- App.tsx: Alerts nav item with Bell icon
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 14:00:19 +01:00
|
|
|
import AlertsPage from './pages/AlertsPage'
|
feat: implement Phase 6 — Software Deployment
Backend:
- SoftwarePackage model (Name, Version, OsType, PackageManager, PackageName, InstallerUrl, Checksum, SilentArgs)
- RmmDbContext: SoftwarePackages DbSet + unique index on (Name, Version, OsType)
- SoftwarePackagesController: full CRUD with OsType filter
- DeployController: POST /api/v1/deploy creates InstallSoftware/UninstallSoftware TaskItem
- EF Migration: AddSoftwarePackages (20260319130448)
Go Agent:
- internal/deployer/deployer.go: Install() and Uninstall() with:
- Chocolatey (Windows), apt/dnf (Linux), auto-detect
- Direct installer fallback: HTTP download + SHA256 verify + silent install
- Supports .msi, .exe (Windows) and .deb, .rpm (Linux)
- main.go: COMMAND_TYPE_INSTALL_SOFTWARE and COMMAND_TYPE_UNINSTALL_SOFTWARE routed to deployer
Frontend:
- SoftwarePage: Katalog tab (CRUD, OS filter, smart package manager select) + Deploy tab
- api/types.ts: SoftwarePackage, PackageManager, DeployRequest/Response types
- api/client.ts: softwarePackagesApi and deployApi
- App.tsx: Software nav item with Package icon
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 14:06:40 +01:00
|
|
|
import SoftwarePage from './pages/SoftwarePage'
|
feat: Phase 8 — Network Discovery + Windows Dev-Setup-Skripte
Network Discovery:
- Go Agent: internal/scanner/scanner.go mit TCP-Sweep (Port 445/80/22/443),
ARP-Tabellen-Parser (Windows: arp -a, Linux: /proc/net/arp), Reverse-DNS,
50 gleichzeitige Goroutines mit Semaphore
- Go Agent main.go: COMMAND_TYPE_NETWORK_SCAN Case → scanner.Scan() → JSON stdout
- Backend: NetworkDevice Model (Id, AgentId, IpAddress, MacAddress, Hostname,
Vendor, IsManaged, FirstSeen, LastSeen)
- Backend: EF Migration AddNetworkDevices + Index auf IpAddress + MacAddress
- Backend: NetworkDevicesController GET /api/v1/network-devices + DELETE /{id}
- Backend: AgentGrpcService.ProcessNetworkScanResultAsync — upsert via MAC,
IsManaged=true wenn IP einem bekannten Agent entspricht
- Frontend: NetworkPage.tsx mit Scan-Panel, Device-Tabelle, Filter, Delete
- Frontend: App.tsx — 'Netzwerk' Nav-Eintrag mit Network Icon
Windows Dev-Setup:
- dev-start.ps1 — Startet Docker/Postgres, EF-Migrationen, Backend+Frontend
in separaten PowerShell-Fenstern; Voraussetzungen-Check (docker/dotnet/node/go)
- dev-stop.ps1 — Stoppt alle NexusRMM-Prozesse + PostgreSQL Container
- build-agent.ps1 — Baut nexus-agent.exe (Windows) + optional nexus-agent-linux
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 14:53:35 +01:00
|
|
|
import NetworkPage from './pages/NetworkPage'
|
2026-03-19 12:42:52 +01:00
|
|
|
import { cn } from './lib/utils'
|
|
|
|
|
|
|
|
|
|
const queryClient = new QueryClient({
|
|
|
|
|
defaultOptions: {
|
|
|
|
|
queries: {
|
|
|
|
|
staleTime: 30_000,
|
|
|
|
|
retry: 2,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
feat: Phase 8 — Network Discovery + Windows Dev-Setup-Skripte
Network Discovery:
- Go Agent: internal/scanner/scanner.go mit TCP-Sweep (Port 445/80/22/443),
ARP-Tabellen-Parser (Windows: arp -a, Linux: /proc/net/arp), Reverse-DNS,
50 gleichzeitige Goroutines mit Semaphore
- Go Agent main.go: COMMAND_TYPE_NETWORK_SCAN Case → scanner.Scan() → JSON stdout
- Backend: NetworkDevice Model (Id, AgentId, IpAddress, MacAddress, Hostname,
Vendor, IsManaged, FirstSeen, LastSeen)
- Backend: EF Migration AddNetworkDevices + Index auf IpAddress + MacAddress
- Backend: NetworkDevicesController GET /api/v1/network-devices + DELETE /{id}
- Backend: AgentGrpcService.ProcessNetworkScanResultAsync — upsert via MAC,
IsManaged=true wenn IP einem bekannten Agent entspricht
- Frontend: NetworkPage.tsx mit Scan-Panel, Device-Tabelle, Filter, Delete
- Frontend: App.tsx — 'Netzwerk' Nav-Eintrag mit Network Icon
Windows Dev-Setup:
- dev-start.ps1 — Startet Docker/Postgres, EF-Migrationen, Backend+Frontend
in separaten PowerShell-Fenstern; Voraussetzungen-Check (docker/dotnet/node/go)
- dev-stop.ps1 — Stoppt alle NexusRMM-Prozesse + PostgreSQL Container
- build-agent.ps1 — Baut nexus-agent.exe (Windows) + optional nexus-agent-linux
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 14:53:35 +01:00
|
|
|
type Page = 'dashboard' | 'agent-detail' | 'tickets' | 'alerts' | 'network' | 'software'
|
2026-03-19 12:42:52 +01:00
|
|
|
|
|
|
|
|
interface NavItem {
|
|
|
|
|
id: Page
|
|
|
|
|
label: string
|
|
|
|
|
icon: React.ReactNode
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const navItems: NavItem[] = [
|
|
|
|
|
{ id: 'dashboard', label: 'Dashboard', icon: <LayoutDashboard size={18} /> },
|
|
|
|
|
{ id: 'tickets', label: 'Tickets', icon: <Ticket size={18} /> },
|
feat: implement Phase 5 — Alerting & Monitoring
Backend:
- AlertEvaluationService: evaluates metrics against AlertRules after each heartbeat
- Supports cpu_usage_percent and memory_usage_percent metric paths
- Operators: >, >=, <, <=, ==
- 15-minute dedup window to prevent alert spam
- AlertRulesController: full CRUD for alert rules (GET/POST/PUT/DELETE)
- AlertsController: list with acknowledged filter + POST acknowledge endpoint
- IRmmHubClient: added AlertTriggered push method
- Program.cs: AlertEvaluationService registered as Scoped
Frontend:
- AlertsPage: two-tab layout (active alerts + rules)
- Alerts tab: severity badges, acknowledge button, all/unack/ack filter
- Rules tab: condition display, enabled toggle, delete with confirm
- Create rule modal with MetricPath/Operator/Threshold/Severity selects
- api/types.ts: AlertRule, AlertItem, CreateAlertRuleRequest types
- api/client.ts: alertRulesApi and alertsApi
- useAgentSignalR: handles AlertTriggered → invalidates alerts query
- App.tsx: Alerts nav item with Bell icon
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 14:00:19 +01:00
|
|
|
{ id: 'alerts', label: 'Alerts', icon: <Bell size={18} /> },
|
feat: Phase 8 — Network Discovery + Windows Dev-Setup-Skripte
Network Discovery:
- Go Agent: internal/scanner/scanner.go mit TCP-Sweep (Port 445/80/22/443),
ARP-Tabellen-Parser (Windows: arp -a, Linux: /proc/net/arp), Reverse-DNS,
50 gleichzeitige Goroutines mit Semaphore
- Go Agent main.go: COMMAND_TYPE_NETWORK_SCAN Case → scanner.Scan() → JSON stdout
- Backend: NetworkDevice Model (Id, AgentId, IpAddress, MacAddress, Hostname,
Vendor, IsManaged, FirstSeen, LastSeen)
- Backend: EF Migration AddNetworkDevices + Index auf IpAddress + MacAddress
- Backend: NetworkDevicesController GET /api/v1/network-devices + DELETE /{id}
- Backend: AgentGrpcService.ProcessNetworkScanResultAsync — upsert via MAC,
IsManaged=true wenn IP einem bekannten Agent entspricht
- Frontend: NetworkPage.tsx mit Scan-Panel, Device-Tabelle, Filter, Delete
- Frontend: App.tsx — 'Netzwerk' Nav-Eintrag mit Network Icon
Windows Dev-Setup:
- dev-start.ps1 — Startet Docker/Postgres, EF-Migrationen, Backend+Frontend
in separaten PowerShell-Fenstern; Voraussetzungen-Check (docker/dotnet/node/go)
- dev-stop.ps1 — Stoppt alle NexusRMM-Prozesse + PostgreSQL Container
- build-agent.ps1 — Baut nexus-agent.exe (Windows) + optional nexus-agent-linux
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 14:53:35 +01:00
|
|
|
{ id: 'network', label: 'Netzwerk', icon: <Network size={18} /> },
|
feat: implement Phase 6 — Software Deployment
Backend:
- SoftwarePackage model (Name, Version, OsType, PackageManager, PackageName, InstallerUrl, Checksum, SilentArgs)
- RmmDbContext: SoftwarePackages DbSet + unique index on (Name, Version, OsType)
- SoftwarePackagesController: full CRUD with OsType filter
- DeployController: POST /api/v1/deploy creates InstallSoftware/UninstallSoftware TaskItem
- EF Migration: AddSoftwarePackages (20260319130448)
Go Agent:
- internal/deployer/deployer.go: Install() and Uninstall() with:
- Chocolatey (Windows), apt/dnf (Linux), auto-detect
- Direct installer fallback: HTTP download + SHA256 verify + silent install
- Supports .msi, .exe (Windows) and .deb, .rpm (Linux)
- main.go: COMMAND_TYPE_INSTALL_SOFTWARE and COMMAND_TYPE_UNINSTALL_SOFTWARE routed to deployer
Frontend:
- SoftwarePage: Katalog tab (CRUD, OS filter, smart package manager select) + Deploy tab
- api/types.ts: SoftwarePackage, PackageManager, DeployRequest/Response types
- api/client.ts: softwarePackagesApi and deployApi
- App.tsx: Software nav item with Package icon
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 14:06:40 +01:00
|
|
|
{ id: 'software', label: 'Software', icon: <Package size={18} /> },
|
2026-03-19 12:42:52 +01:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
function AppContent() {
|
|
|
|
|
const [page, setPage] = useState<Page>('dashboard')
|
|
|
|
|
const [selectedAgentId, setSelectedAgentId] = useState<string | null>(null)
|
|
|
|
|
const [sidebarOpen, setSidebarOpen] = useState(true)
|
feat: Phase 9 — Offline Detection, API Key Auth, Agent Self-Update
Offline Detection (9.1):
- AgentOfflineDetectorService: BackgroundService, prüft alle 60s
ob Agents seit >5 min kein Heartbeat hatten → Status=Offline
- IServiceScopeFactory für korrektes Scoped-DI im Singleton
- SignalR-Push AgentStatusChanged bei jeder Offline-Markierung
API Key Auth (9.2):
- ApiKeyMiddleware: prüft X-Api-Key Header gegen Security:ApiKey Config
- Deaktiviert wenn ApiKey leer (Dev-Modus), Swagger/hubs bypassed
- Frontend: getApiKey() aus localStorage, automatisch in allen Requests
- Settings-Modal in Sidebar: API-Key eingeben + maskiert anzeigen
Agent Self-Update (9.3):
- internal/updater/updater.go: CheckForUpdate() + Update()
Download, SHA256-Verify, Windows Batch-Neustart / Linux Shell-Neustart
- AgentReleasesController: GET /api/v1/agent/releases/latest,
GET /api/v1/agent/releases/download/{platform}
- AgentReleaseOptions: LatestVersion, ReleasePath, Checksum in appsettings
- executeCommand() erhält cfg *Config statt agentID string
(für ServerAddress-Ableitung im UpdateAgent-Case)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 15:41:24 +01:00
|
|
|
const [settingsOpen, setSettingsOpen] = useState(false)
|
|
|
|
|
const [apiKeyInput, setApiKeyInput] = useState('')
|
|
|
|
|
|
|
|
|
|
const storedKey = localStorage.getItem('nexusrmm_api_key') ?? ''
|
|
|
|
|
const maskedKey = storedKey.length > 0
|
|
|
|
|
? storedKey.substring(0, Math.min(8, storedKey.length)) + '...'
|
|
|
|
|
: '(nicht gesetzt)'
|
|
|
|
|
|
|
|
|
|
function handleSaveApiKey() {
|
|
|
|
|
localStorage.setItem('nexusrmm_api_key', apiKeyInput)
|
|
|
|
|
setSettingsOpen(false)
|
|
|
|
|
setApiKeyInput('')
|
|
|
|
|
}
|
2026-03-19 12:42:52 +01:00
|
|
|
|
|
|
|
|
function handleSelectAgent(agentId: string) {
|
|
|
|
|
setSelectedAgentId(agentId)
|
|
|
|
|
setPage('agent-detail')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function handleBack() {
|
|
|
|
|
setPage('dashboard')
|
|
|
|
|
setSelectedAgentId(null)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="min-h-screen bg-background text-foreground flex">
|
|
|
|
|
{/* Sidebar */}
|
|
|
|
|
<aside
|
|
|
|
|
className={cn(
|
|
|
|
|
'flex flex-col border-r border-border bg-card transition-all duration-200',
|
|
|
|
|
sidebarOpen ? 'w-56' : 'w-14',
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
{/* Logo */}
|
|
|
|
|
<div className="flex items-center gap-3 px-4 py-4 border-b border-border">
|
|
|
|
|
<div className="w-7 h-7 rounded-md bg-primary flex items-center justify-center text-primary-foreground font-bold text-sm flex-shrink-0">
|
|
|
|
|
N
|
|
|
|
|
</div>
|
|
|
|
|
{sidebarOpen && (
|
|
|
|
|
<span className="font-semibold text-foreground truncate">NexusRMM</span>
|
|
|
|
|
)}
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setSidebarOpen(!sidebarOpen)}
|
|
|
|
|
className="ml-auto text-muted-foreground hover:text-foreground"
|
|
|
|
|
>
|
|
|
|
|
{sidebarOpen ? <X size={16} /> : <Menu size={16} />}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Nav */}
|
|
|
|
|
<nav className="flex-1 py-3 px-2 flex flex-col gap-1">
|
|
|
|
|
{navItems.map((item) => (
|
|
|
|
|
<button
|
|
|
|
|
key={item.id}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setPage(item.id)
|
|
|
|
|
setSelectedAgentId(null)
|
|
|
|
|
}}
|
|
|
|
|
className={cn(
|
|
|
|
|
'flex items-center gap-3 px-2 py-2 rounded-md text-sm transition-colors w-full text-left',
|
|
|
|
|
page === item.id || (page === 'agent-detail' && item.id === 'dashboard')
|
|
|
|
|
? 'bg-primary/15 text-primary'
|
|
|
|
|
: 'text-muted-foreground hover:text-foreground hover:bg-accent',
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<span className="flex-shrink-0">{item.icon}</span>
|
|
|
|
|
{sidebarOpen && <span>{item.label}</span>}
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</nav>
|
|
|
|
|
|
feat: Phase 9 — Offline Detection, API Key Auth, Agent Self-Update
Offline Detection (9.1):
- AgentOfflineDetectorService: BackgroundService, prüft alle 60s
ob Agents seit >5 min kein Heartbeat hatten → Status=Offline
- IServiceScopeFactory für korrektes Scoped-DI im Singleton
- SignalR-Push AgentStatusChanged bei jeder Offline-Markierung
API Key Auth (9.2):
- ApiKeyMiddleware: prüft X-Api-Key Header gegen Security:ApiKey Config
- Deaktiviert wenn ApiKey leer (Dev-Modus), Swagger/hubs bypassed
- Frontend: getApiKey() aus localStorage, automatisch in allen Requests
- Settings-Modal in Sidebar: API-Key eingeben + maskiert anzeigen
Agent Self-Update (9.3):
- internal/updater/updater.go: CheckForUpdate() + Update()
Download, SHA256-Verify, Windows Batch-Neustart / Linux Shell-Neustart
- AgentReleasesController: GET /api/v1/agent/releases/latest,
GET /api/v1/agent/releases/download/{platform}
- AgentReleaseOptions: LatestVersion, ReleasePath, Checksum in appsettings
- executeCommand() erhält cfg *Config statt agentID string
(für ServerAddress-Ableitung im UpdateAgent-Case)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 15:41:24 +01:00
|
|
|
{/* Settings + Version */}
|
|
|
|
|
<div className="border-t border-border px-2 py-2 flex flex-col gap-1">
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setApiKeyInput(localStorage.getItem('nexusrmm_api_key') ?? '')
|
|
|
|
|
setSettingsOpen(true)
|
|
|
|
|
}}
|
|
|
|
|
className="flex items-center gap-3 px-2 py-2 rounded-md text-sm transition-colors w-full text-left text-muted-foreground hover:text-foreground hover:bg-accent"
|
|
|
|
|
>
|
|
|
|
|
<span className="flex-shrink-0"><Settings size={18} /></span>
|
|
|
|
|
{sidebarOpen && <span>Einstellungen</span>}
|
|
|
|
|
</button>
|
|
|
|
|
{sidebarOpen && (
|
|
|
|
|
<div className="px-2 py-1 text-xs text-muted-foreground">
|
|
|
|
|
NexusRMM v0.1.0
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2026-03-19 12:42:52 +01:00
|
|
|
</aside>
|
|
|
|
|
|
feat: Phase 9 — Offline Detection, API Key Auth, Agent Self-Update
Offline Detection (9.1):
- AgentOfflineDetectorService: BackgroundService, prüft alle 60s
ob Agents seit >5 min kein Heartbeat hatten → Status=Offline
- IServiceScopeFactory für korrektes Scoped-DI im Singleton
- SignalR-Push AgentStatusChanged bei jeder Offline-Markierung
API Key Auth (9.2):
- ApiKeyMiddleware: prüft X-Api-Key Header gegen Security:ApiKey Config
- Deaktiviert wenn ApiKey leer (Dev-Modus), Swagger/hubs bypassed
- Frontend: getApiKey() aus localStorage, automatisch in allen Requests
- Settings-Modal in Sidebar: API-Key eingeben + maskiert anzeigen
Agent Self-Update (9.3):
- internal/updater/updater.go: CheckForUpdate() + Update()
Download, SHA256-Verify, Windows Batch-Neustart / Linux Shell-Neustart
- AgentReleasesController: GET /api/v1/agent/releases/latest,
GET /api/v1/agent/releases/download/{platform}
- AgentReleaseOptions: LatestVersion, ReleasePath, Checksum in appsettings
- executeCommand() erhält cfg *Config statt agentID string
(für ServerAddress-Ableitung im UpdateAgent-Case)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 15:41:24 +01:00
|
|
|
{/* Settings Modal */}
|
|
|
|
|
{settingsOpen && (
|
|
|
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
|
|
|
|
|
<div className="bg-card border border-border rounded-lg shadow-lg w-96 p-6 flex flex-col gap-4">
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<h2 className="text-base font-semibold text-foreground">Einstellungen</h2>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setSettingsOpen(false)}
|
|
|
|
|
className="text-muted-foreground hover:text-foreground"
|
|
|
|
|
>
|
|
|
|
|
<X size={16} />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex flex-col gap-2">
|
|
|
|
|
<label className="text-sm font-medium text-foreground">API-Key</label>
|
|
|
|
|
<p className="text-xs text-muted-foreground">
|
|
|
|
|
Aktuell: <span className="font-mono">{maskedKey}</span>
|
|
|
|
|
</p>
|
|
|
|
|
<input
|
|
|
|
|
type="password"
|
|
|
|
|
value={apiKeyInput}
|
|
|
|
|
onChange={(e) => setApiKeyInput(e.target.value)}
|
|
|
|
|
placeholder="API-Key eingeben..."
|
|
|
|
|
className="w-full px-3 py-2 rounded-md border border-border bg-background text-foreground text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary"
|
|
|
|
|
/>
|
|
|
|
|
<p className="text-xs text-muted-foreground">
|
|
|
|
|
Leer lassen um Authentifizierung zu deaktivieren.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex justify-end gap-2">
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => setSettingsOpen(false)}
|
|
|
|
|
className="px-4 py-2 text-sm rounded-md border border-border text-muted-foreground hover:text-foreground hover:bg-accent transition-colors"
|
|
|
|
|
>
|
|
|
|
|
Abbrechen
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={handleSaveApiKey}
|
|
|
|
|
className="px-4 py-2 text-sm rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors"
|
|
|
|
|
>
|
|
|
|
|
Speichern
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2026-03-19 12:42:52 +01:00
|
|
|
{/* Main content */}
|
|
|
|
|
<main className="flex-1 overflow-auto">
|
|
|
|
|
{page === 'dashboard' && (
|
|
|
|
|
<DashboardPage onSelectAgent={handleSelectAgent} />
|
|
|
|
|
)}
|
|
|
|
|
{page === 'agent-detail' && selectedAgentId && (
|
|
|
|
|
<AgentDetailPage agentId={selectedAgentId} onBack={handleBack} />
|
|
|
|
|
)}
|
|
|
|
|
{page === 'tickets' && <TicketsPage />}
|
feat: implement Phase 5 — Alerting & Monitoring
Backend:
- AlertEvaluationService: evaluates metrics against AlertRules after each heartbeat
- Supports cpu_usage_percent and memory_usage_percent metric paths
- Operators: >, >=, <, <=, ==
- 15-minute dedup window to prevent alert spam
- AlertRulesController: full CRUD for alert rules (GET/POST/PUT/DELETE)
- AlertsController: list with acknowledged filter + POST acknowledge endpoint
- IRmmHubClient: added AlertTriggered push method
- Program.cs: AlertEvaluationService registered as Scoped
Frontend:
- AlertsPage: two-tab layout (active alerts + rules)
- Alerts tab: severity badges, acknowledge button, all/unack/ack filter
- Rules tab: condition display, enabled toggle, delete with confirm
- Create rule modal with MetricPath/Operator/Threshold/Severity selects
- api/types.ts: AlertRule, AlertItem, CreateAlertRuleRequest types
- api/client.ts: alertRulesApi and alertsApi
- useAgentSignalR: handles AlertTriggered → invalidates alerts query
- App.tsx: Alerts nav item with Bell icon
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 14:00:19 +01:00
|
|
|
{page === 'alerts' && <AlertsPage />}
|
feat: Phase 8 — Network Discovery + Windows Dev-Setup-Skripte
Network Discovery:
- Go Agent: internal/scanner/scanner.go mit TCP-Sweep (Port 445/80/22/443),
ARP-Tabellen-Parser (Windows: arp -a, Linux: /proc/net/arp), Reverse-DNS,
50 gleichzeitige Goroutines mit Semaphore
- Go Agent main.go: COMMAND_TYPE_NETWORK_SCAN Case → scanner.Scan() → JSON stdout
- Backend: NetworkDevice Model (Id, AgentId, IpAddress, MacAddress, Hostname,
Vendor, IsManaged, FirstSeen, LastSeen)
- Backend: EF Migration AddNetworkDevices + Index auf IpAddress + MacAddress
- Backend: NetworkDevicesController GET /api/v1/network-devices + DELETE /{id}
- Backend: AgentGrpcService.ProcessNetworkScanResultAsync — upsert via MAC,
IsManaged=true wenn IP einem bekannten Agent entspricht
- Frontend: NetworkPage.tsx mit Scan-Panel, Device-Tabelle, Filter, Delete
- Frontend: App.tsx — 'Netzwerk' Nav-Eintrag mit Network Icon
Windows Dev-Setup:
- dev-start.ps1 — Startet Docker/Postgres, EF-Migrationen, Backend+Frontend
in separaten PowerShell-Fenstern; Voraussetzungen-Check (docker/dotnet/node/go)
- dev-stop.ps1 — Stoppt alle NexusRMM-Prozesse + PostgreSQL Container
- build-agent.ps1 — Baut nexus-agent.exe (Windows) + optional nexus-agent-linux
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 14:53:35 +01:00
|
|
|
{page === 'network' && <NetworkPage />}
|
feat: implement Phase 6 — Software Deployment
Backend:
- SoftwarePackage model (Name, Version, OsType, PackageManager, PackageName, InstallerUrl, Checksum, SilentArgs)
- RmmDbContext: SoftwarePackages DbSet + unique index on (Name, Version, OsType)
- SoftwarePackagesController: full CRUD with OsType filter
- DeployController: POST /api/v1/deploy creates InstallSoftware/UninstallSoftware TaskItem
- EF Migration: AddSoftwarePackages (20260319130448)
Go Agent:
- internal/deployer/deployer.go: Install() and Uninstall() with:
- Chocolatey (Windows), apt/dnf (Linux), auto-detect
- Direct installer fallback: HTTP download + SHA256 verify + silent install
- Supports .msi, .exe (Windows) and .deb, .rpm (Linux)
- main.go: COMMAND_TYPE_INSTALL_SOFTWARE and COMMAND_TYPE_UNINSTALL_SOFTWARE routed to deployer
Frontend:
- SoftwarePage: Katalog tab (CRUD, OS filter, smart package manager select) + Deploy tab
- api/types.ts: SoftwarePackage, PackageManager, DeployRequest/Response types
- api/client.ts: softwarePackagesApi and deployApi
- App.tsx: Software nav item with Package icon
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 14:06:40 +01:00
|
|
|
{page === 'software' && <SoftwarePage />}
|
2026-03-19 12:42:52 +01:00
|
|
|
</main>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function App() {
|
|
|
|
|
return (
|
|
|
|
|
<QueryClientProvider client={queryClient}>
|
|
|
|
|
<AppContent />
|
|
|
|
|
</QueryClientProvider>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default App
|