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

@@ -10,6 +10,10 @@ import type {
AlertItem,
CreateAlertRuleRequest,
UpdateAlertRuleRequest,
SoftwarePackage,
CreateSoftwarePackageRequest,
DeployRequest,
DeployResponse,
} from './types'
const BASE_URL = '/api/v1'
@@ -73,3 +77,23 @@ export const alertsApi = {
acknowledge: (id: number) =>
request<{ id: number; acknowledged: boolean }>(`/alerts/${id}/acknowledge`, { method: 'POST' }),
}
// Software Packages
export const softwarePackagesApi = {
list: (osType?: string) => {
const param = osType ? `?osType=${osType}` : ''
return request<SoftwarePackage[]>(`/software-packages${param}`)
},
create: (data: CreateSoftwarePackageRequest) =>
request<SoftwarePackage>('/software-packages', { method: 'POST', body: JSON.stringify(data) }),
update: (id: number, data: CreateSoftwarePackageRequest) =>
request<SoftwarePackage>(`/software-packages/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
delete: (id: number) =>
request<void>(`/software-packages/${id}`, { method: 'DELETE' }),
}
// Deploy
export const deployApi = {
deploy: (data: DeployRequest) =>
request<DeployResponse>('/deploy', { method: 'POST', body: JSON.stringify(data) }),
}

View File

@@ -132,3 +132,44 @@ export interface UpdateAlertRuleRequest {
severity?: AlertSeverity
enabled?: boolean
}
export type PackageManager = 'choco' | 'apt' | 'dnf' | 'direct'
export interface SoftwarePackage {
id: number
name: string
version: string
osType: OsType
packageManager: PackageManager
packageName: string
installerUrl: string | null
checksum: string | null
silentArgs: string | null
createdAt: string
}
export interface CreateSoftwarePackageRequest {
name: string
version: string
osType: OsType
packageManager: PackageManager
packageName: string
installerUrl?: string
checksum?: string
silentArgs?: string
}
export interface DeployRequest {
agentId: string
packageId: number
action: 'install' | 'uninstall'
}
export interface DeployResponse {
id: string
agentId: string
type: string
status: string
packageName: string
version: string
}