2 Commits

Author SHA1 Message Date
urania 088880f584 fix: updates
build-and-release / release (push) Successful in 2m39s
2026-06-23 14:27:06 +02:00
urania 67d95475ee fix: added package update endpoint
build-and-release / release (push) Successful in 3m1s
2026-06-23 13:15:39 +02:00
2 changed files with 46 additions and 8 deletions
+2 -2
View File
@@ -278,11 +278,11 @@ func runServer() {
// is the follow-up (M5 partial).
w.Header().Set("Content-Security-Policy",
"default-src 'self'; "+
"script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; "+
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net; "+
"style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; "+
"img-src 'self' data: https:; "+
"connect-src 'self'; "+
"font-src 'self' data: https://cdn.jsdelivr.net")
"font-src 'self' data: https://cdn.jsdelivr.net https://fonts.scalar.com")
w.Header().Set("Content-Type", "text/html")
w.Write([]byte(`<!doctype html><html><head><title>API</title>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+44 -6
View File
@@ -65,6 +65,10 @@ type RemoveInput struct {
Name string `path:"name" example:"htop" doc:"Package to remove"`
}
type UpgradeOneInput struct {
Name string `path:"name" example:"htop" doc:"Package to upgrade"`
}
// SSE event types for streaming package operations.
type PkgOutputEvent struct {
Line string `json:"line" doc:"One line of the package manager's terminal output"`
@@ -162,6 +166,25 @@ func registerPackages(api huma.API, pm manager) {
bin, args := pm.upgradeArgs()
streamOp(ctx, send, bin, args)
})
sse.Register(api, huma.Operation{
OperationID: "packages-upgrade-one",
Method: "POST",
Path: "/api/packages/upgrade/{name}",
Summary: "Upgrade a single package (streamed)",
Description: "Upgrades the named package to its latest version, streaming the " +
"package manager's output live. apt uses `install --only-upgrade` so the " +
"package must already be installed; dnf/pacman handle this natively.",
Tags: []string{tagPackages},
Metadata: op("write"),
}, pkgEvents, func(ctx context.Context, in *UpgradeOneInput, send sse.Sender) {
if validateName(in.Name) != nil {
send.Data(PkgErrorEvent{Message: "invalid package name: " + in.Name})
return
}
bin, args := pm.upgradeOneArgs(in.Name)
streamOp(ctx, send, bin, args)
})
}
// streamOp runs a package write and streams its combined output to the client.
@@ -260,11 +283,11 @@ func result(pm manager, pkgs []Package) *ListOutput {
func (m manager) installArgs(name string) (string, []string) {
switch m.name {
case "dnf":
return "dnf", []string{"install", "-y", "--", name}
return "dnf", []string{"install", "-y", name}
case "apt":
return "apt-get", []string{"install", "-y", "--", name}
return "apt-get", []string{"install", "-y", name}
case "pacman":
return "pacman", []string{"-S", "--noconfirm", "--", name}
return "pacman", []string{"-S", "--noconfirm", name}
}
return "", nil
}
@@ -272,11 +295,11 @@ func (m manager) installArgs(name string) (string, []string) {
func (m manager) removeArgs(name string) (string, []string) {
switch m.name {
case "dnf":
return "dnf", []string{"remove", "-y", "--", name}
return "dnf", []string{"remove", "-y", name}
case "apt":
return "apt-get", []string{"remove", "-y", "--", name}
return "apt-get", []string{"remove", "-y", name}
case "pacman":
return "pacman", []string{"-R", "--noconfirm", "--", name}
return "pacman", []string{"-R", "--noconfirm", name}
}
return "", nil
}
@@ -293,6 +316,21 @@ func (m manager) upgradeArgs() (string, []string) {
return "", nil
}
// upgradeOneArgs upgrades a single package to its latest version. apt's
// `install --only-upgrade` is the safe variant (won't install if absent);
// pacman -S re-syncs to latest; dnf upgrade is naturally scoped by name.
func (m manager) upgradeOneArgs(name string) (string, []string) {
switch m.name {
case "dnf":
return "dnf", []string{"upgrade", "-y", name}
case "apt":
return "apt-get", []string{"install", "--only-upgrade", "-y", name}
case "pacman":
return "pacman", []string{"-S", "--noconfirm", name}
}
return "", nil
}
// --- parsers (pure, tested) --------------------------------------------------
// parseTabbed reads "name\tversion" lines (dpkg-query / rpm output).