Initial commit: Driver Booster Pro - Go + WebView2 desktop app
Windows system utility with driver scanning, Windows Update integration, and system info collection. Beautiful dark-themed UI via embedded WebView2. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
208
internal/winupdate/winupdate.go
Normal file
208
internal/winupdate/winupdate.go
Normal file
@@ -0,0 +1,208 @@
|
||||
package winupdate
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os/exec"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Update struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
KBArticle string `json:"kbArticle"`
|
||||
Category string `json:"category"`
|
||||
Size string `json:"size"`
|
||||
IsInstalled bool `json:"isInstalled"`
|
||||
IsDownloaded bool `json:"isDownloaded"`
|
||||
IsMandatory bool `json:"isMandatory"`
|
||||
Severity string `json:"severity"`
|
||||
}
|
||||
|
||||
type CheckResult struct {
|
||||
Updates []Update `json:"updates"`
|
||||
PendingCount int `json:"pendingCount"`
|
||||
InstalledCount int `json:"installedCount"`
|
||||
CheckTime string `json:"checkTime"`
|
||||
CheckedAt time.Time `json:"checkedAt"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type InstallResult struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type Checker struct{}
|
||||
|
||||
func NewChecker() *Checker {
|
||||
return &Checker{}
|
||||
}
|
||||
|
||||
func (c *Checker) Check() CheckResult {
|
||||
start := time.Now()
|
||||
result := CheckResult{
|
||||
Updates: []Update{},
|
||||
}
|
||||
|
||||
// Use PowerShell COM object for Windows Update Agent
|
||||
psScript := `
|
||||
$ErrorActionPreference = 'SilentlyContinue'
|
||||
$Session = New-Object -ComObject Microsoft.Update.Session
|
||||
$Searcher = $Session.CreateUpdateSearcher()
|
||||
|
||||
# Get installed update history (recent 50)
|
||||
$HistoryCount = $Searcher.GetTotalHistoryCount()
|
||||
$History = $Searcher.QueryHistory(0, [Math]::Min($HistoryCount, 50))
|
||||
|
||||
$installed = @()
|
||||
foreach ($entry in $History) {
|
||||
if ($entry.Title) {
|
||||
$installed += @{
|
||||
Title = $entry.Title
|
||||
Description = $entry.Description
|
||||
Date = $entry.Date.ToString("yyyy-MM-dd")
|
||||
ResultCode = $entry.ResultCode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Search for pending updates
|
||||
try {
|
||||
$SearchResult = $Searcher.Search("IsInstalled=0")
|
||||
$pending = @()
|
||||
foreach ($update in $SearchResult.Updates) {
|
||||
$kb = ""
|
||||
if ($update.KBArticleIDs.Count -gt 0) { $kb = "KB" + $update.KBArticleIDs.Item(0) }
|
||||
$cat = ""
|
||||
if ($update.Categories.Count -gt 0) { $cat = $update.Categories.Item(0).Name }
|
||||
$sev = if ($update.MsrcSeverity) { $update.MsrcSeverity } else { "Unspecified" }
|
||||
$sz = ""
|
||||
if ($update.MaxDownloadSize -gt 0) {
|
||||
$mb = [Math]::Round($update.MaxDownloadSize / 1MB, 1)
|
||||
$sz = "$mb MB"
|
||||
}
|
||||
$pending += @{
|
||||
Title = $update.Title
|
||||
Description = $update.Description
|
||||
KBArticle = $kb
|
||||
Category = $cat
|
||||
Size = $sz
|
||||
IsDownloaded = $update.IsDownloaded
|
||||
IsMandatory = $update.IsMandatory
|
||||
Severity = $sev
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
$pending = @()
|
||||
}
|
||||
|
||||
@{
|
||||
Installed = $installed
|
||||
Pending = $pending
|
||||
} | ConvertTo-Json -Depth 3 -Compress
|
||||
`
|
||||
out, err := exec.Command("powershell", "-NoProfile", "-Command", psScript).Output()
|
||||
if err != nil {
|
||||
result.Error = "Failed to check updates: " + err.Error()
|
||||
result.CheckTime = time.Since(start).Round(time.Millisecond).String()
|
||||
result.CheckedAt = time.Now()
|
||||
return result
|
||||
}
|
||||
|
||||
var raw struct {
|
||||
Installed []struct {
|
||||
Title string `json:"Title"`
|
||||
Description string `json:"Description"`
|
||||
Date string `json:"Date"`
|
||||
ResultCode int `json:"ResultCode"`
|
||||
} `json:"Installed"`
|
||||
Pending []struct {
|
||||
Title string `json:"Title"`
|
||||
Description string `json:"Description"`
|
||||
KBArticle string `json:"KBArticle"`
|
||||
Category string `json:"Category"`
|
||||
Size string `json:"Size"`
|
||||
IsDownloaded bool `json:"IsDownloaded"`
|
||||
IsMandatory bool `json:"IsMandatory"`
|
||||
Severity string `json:"Severity"`
|
||||
} `json:"Pending"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(out, &raw); err != nil {
|
||||
result.Error = "Failed to parse update data"
|
||||
result.CheckTime = time.Since(start).Round(time.Millisecond).String()
|
||||
result.CheckedAt = time.Now()
|
||||
return result
|
||||
}
|
||||
|
||||
for _, p := range raw.Pending {
|
||||
result.Updates = append(result.Updates, Update{
|
||||
Title: p.Title,
|
||||
Description: p.Description,
|
||||
KBArticle: p.KBArticle,
|
||||
Category: p.Category,
|
||||
Size: p.Size,
|
||||
IsInstalled: false,
|
||||
IsDownloaded: p.IsDownloaded,
|
||||
IsMandatory: p.IsMandatory,
|
||||
Severity: p.Severity,
|
||||
})
|
||||
result.PendingCount++
|
||||
}
|
||||
|
||||
for _, i := range raw.Installed {
|
||||
result.Updates = append(result.Updates, Update{
|
||||
Title: i.Title,
|
||||
Description: i.Description,
|
||||
IsInstalled: true,
|
||||
Category: "Installed",
|
||||
})
|
||||
result.InstalledCount++
|
||||
}
|
||||
|
||||
result.CheckTime = time.Since(start).Round(time.Millisecond).String()
|
||||
result.CheckedAt = time.Now()
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *Checker) Install(titles []string) InstallResult {
|
||||
// Trigger Windows Update installation via PowerShell
|
||||
psScript := `
|
||||
$Session = New-Object -ComObject Microsoft.Update.Session
|
||||
$Searcher = $Session.CreateUpdateSearcher()
|
||||
$SearchResult = $Searcher.Search("IsInstalled=0")
|
||||
$ToInstall = New-Object -ComObject Microsoft.Update.UpdateColl
|
||||
|
||||
foreach ($update in $SearchResult.Updates) {
|
||||
if ($update.IsDownloaded -eq $false) {
|
||||
$Downloader = $Session.CreateUpdateDownloader()
|
||||
$dl = New-Object -ComObject Microsoft.Update.UpdateColl
|
||||
$dl.Add($update) | Out-Null
|
||||
$Downloader.Updates = $dl
|
||||
$Downloader.Download() | Out-Null
|
||||
}
|
||||
$ToInstall.Add($update) | Out-Null
|
||||
}
|
||||
|
||||
if ($ToInstall.Count -gt 0) {
|
||||
$Installer = $Session.CreateUpdateInstaller()
|
||||
$Installer.Updates = $ToInstall
|
||||
$Result = $Installer.Install()
|
||||
Write-Output "Installed $($ToInstall.Count) updates. Result: $($Result.ResultCode)"
|
||||
} else {
|
||||
Write-Output "No updates to install."
|
||||
}
|
||||
`
|
||||
out, err := exec.Command("powershell", "-NoProfile", "-Command", psScript).Output()
|
||||
if err != nil {
|
||||
return InstallResult{
|
||||
Success: false,
|
||||
Message: "Installation failed: " + err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
return InstallResult{
|
||||
Success: true,
|
||||
Message: string(out),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user