cynfo3000 54ddc0b902 WAU Compliance: agent collector, backend endpoints, frontend badge + panel
- Agent: WAU registry config check (include/exclude URLs), pending updates count
- Backend: /wau/compliance per customer, /wau/compliance/summary all customers
- Frontend: compliance badge in customer list, compliance panel in WAU tab
- config.js: no longer gitignored (secrets-free, reads from env/window.__RMM_CONFIG__)
- vite.config.js: reads backend URL from .env instead of hardcoded value
- .env.example added for clean setup
2026-03-10 17:38:58 +01:00

101 lines
2.7 KiB
Go

//go:build windows
package collector
import (
"os/exec"
"strings"
"golang.org/x/sys/windows/registry"
)
// WAUInfo enthält den Status von Winget-AutoUpdate auf diesem Gerät
type WAUInfo struct {
Installed bool `json:"installed"`
Version string `json:"version,omitempty"`
IncludeURL string `json:"include_url,omitempty"`
ExcludeURL string `json:"exclude_url,omitempty"`
PendingCount int `json:"pending_updates"`
}
// getWAUInfo liest WAU-Installations- und Konfigurationsstatus
func getWAUInfo(software []SoftwareInfo) WAUInfo {
info := WAUInfo{}
// WAU in installierter Software suchen
for _, s := range software {
lower := strings.ToLower(s.Name)
if strings.Contains(lower, "winget-autoupdate") || lower == "wau" {
info.Installed = true
info.Version = s.Version
break
}
}
// WAU-Konfiguration aus Registry lesen
// WAU speichert seine Einstellungen unter HKLM\SOFTWARE\Winget-AutoUpdate
regPaths := []string{
`SOFTWARE\Winget-AutoUpdate`,
`SOFTWARE\Policies\Microsoft\Windows\Winget-AutoUpdate`,
}
for _, path := range regPaths {
k, err := registry.OpenKey(registry.LOCAL_MACHINE, path, registry.QUERY_VALUE)
if err != nil {
continue
}
if url, _, err := k.GetStringValue("WAU_IncludeListURL"); err == nil && url != "" {
info.IncludeURL = url
}
if url, _, err := k.GetStringValue("WAU_ExcludeListURL"); err == nil && url != "" {
info.ExcludeURL = url
}
k.Close()
if info.IncludeURL != "" || info.ExcludeURL != "" {
info.Installed = true
break
}
}
// Pending updates via winget upgrade --list zählen
wp := findWingetExe()
if wp != "" {
out, err := runPS(`& "` + wp + `" upgrade --list --accept-source-agreements 2>&1`)
if err == nil {
info.PendingCount = countWingetUpgradeLines(out)
}
}
return info
}
// findWingetExe sucht winget.exe im System
func findWingetExe() string {
if path, err := exec.LookPath("winget"); err == nil {
return path
}
script := `Get-ChildItem "C:\Program Files\WindowsApps" -Filter winget.exe -Recurse -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty FullName`
out, err := runPS(script)
if err == nil && strings.TrimSpace(out) != "" {
return strings.TrimSpace(out)
}
return ""
}
// countWingetUpgradeLines zählt Pakete mit Updates aus "winget upgrade --list" Output
func countWingetUpgradeLines(output string) int {
lines := strings.Split(output, "\n")
count := 0
pastSeparator := false
for _, line := range lines {
trimmed := strings.TrimSpace(line)
if strings.Contains(trimmed, "---") {
pastSeparator = true
continue
}
if pastSeparator && trimmed != "" {
count++
}
}
return count
}