Files
nadir-agent/internal/modules/system/power.go
T
2026-06-22 16:06:57 +02:00

74 lines
2.2 KiB
Go

package system
import (
"context"
"regexp"
"strings"
"nadir/internal/oscmd"
"github.com/danielgtaylor/huma/v2"
)
// whenRe matches the shutdown(8) TIME forms we accept: "now", "+<minutes>", or
// "hh:mm". Validating before exec keeps a value like "-r" or "--help" from
// being read as a flag (shutdown does not honor a "--" separator), per the
// repo's input-validation rule.
var whenRe = regexp.MustCompile(`^(now|\+\d+|([01]?\d|2[0-3]):[0-5]\d)$`)
type PowerInput struct {
Body struct {
When string `json:"when,omitempty" example:"+1" doc:"When to act, as a shutdown(8) TIME (e.g. \"+5\" minutes, \"23:00\"). Empty or 'now' = immediate."`
}
}
const powerDescription = "Requires the `root` permission. The response is sent once `shutdown` " +
"accepts the request; for the immediate form it returns before the machine " +
"actually goes down, so a 200 does not guarantee a clean shutdown completed."
func registerPower(api huma.API) {
huma.Register(api, huma.Operation{
OperationID: "system-reboot",
Method: "POST",
Path: "/api/system/reboot",
Summary: "Reboot the system",
Description: powerDescription,
Tags: []string{tagSystem},
Metadata: op("root"),
Errors: []int{400, 401, 403, 500},
}, func(ctx context.Context, in *PowerInput) (*oscmd.StatusOutput, error) {
return schedulePower("reboot", in.Body.When)
})
huma.Register(api, huma.Operation{
OperationID: "system-poweroff",
Method: "POST",
Path: "/api/system/poweroff",
Summary: "Power off the system",
Description: powerDescription,
Tags: []string{tagSystem},
Metadata: op("root"),
Errors: []int{400, 401, 403, 500},
}, func(ctx context.Context, in *PowerInput) (*oscmd.StatusOutput, error) {
return schedulePower("poweroff", in.Body.When)
})
}
func schedulePower(action, when string) (*oscmd.StatusOutput, error) {
w := strings.TrimSpace(when)
if w == "" {
w = "now"
}
if !whenRe.MatchString(w) {
return nil, huma.Error400BadRequest("invalid when: " + w)
}
flag := "-h"
if action == "reboot" {
flag = "-r"
}
if _, err := oscmd.Run("shutdown", flag, w); err != nil {
return nil, huma.Error500InternalServerError("shutdown failed", err)
}
return oscmd.OK(), nil
}