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>
This commit is contained in:
Claude Agent
2026-03-19 14:06:40 +01:00
parent eb114f68e2
commit 84629dfbcf
13 changed files with 1491 additions and 2 deletions

View File

@@ -1,10 +1,11 @@
import { useState } from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { LayoutDashboard, Ticket, Bell, Menu, X } from 'lucide-react'
import { LayoutDashboard, Ticket, Bell, Package, Menu, X } from 'lucide-react'
import { DashboardPage } from './pages/DashboardPage'
import { AgentDetailPage } from './pages/AgentDetailPage'
import TicketsPage from './pages/TicketsPage'
import AlertsPage from './pages/AlertsPage'
import SoftwarePage from './pages/SoftwarePage'
import { cn } from './lib/utils'
const queryClient = new QueryClient({
@@ -16,7 +17,7 @@ const queryClient = new QueryClient({
},
})
type Page = 'dashboard' | 'agent-detail' | 'tickets' | 'alerts'
type Page = 'dashboard' | 'agent-detail' | 'tickets' | 'alerts' | 'software'
interface NavItem {
id: Page
@@ -28,6 +29,7 @@ const navItems: NavItem[] = [
{ id: 'dashboard', label: 'Dashboard', icon: <LayoutDashboard size={18} /> },
{ id: 'tickets', label: 'Tickets', icon: <Ticket size={18} /> },
{ id: 'alerts', label: 'Alerts', icon: <Bell size={18} /> },
{ id: 'software', label: 'Software', icon: <Package size={18} /> },
]
function AppContent() {
@@ -110,6 +112,7 @@ function AppContent() {
)}
{page === 'tickets' && <TicketsPage />}
{page === 'alerts' && <AlertsPage />}
{page === 'software' && <SoftwarePage />}
</main>
</div>
)