The UI had no way to add a device. Adds an admin-only Add device dialog
(IPv6/IPv4 addresses, SNMP v1/v2c/v3, synchronous discovery, reattach 409
flow) and a device-page Actions menu (Edit with parents and SNMP
replacement, Rediscover now, Retire). Purge now requires typing the name.
API: POST /api/v1/devices/{slug}/retire (admin, audited, 409 if already
retired). OpenAPI change is additive.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lx9FNLG2CqeBbVGGd9bFsP
42 lines
1.3 KiB
Go
42 lines
1.3 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
|
|
"github.com/danielgtaylor/huma/v2"
|
|
)
|
|
|
|
func (s *server) registerRetire(api huma.API) {
|
|
huma.Register(api, huma.Operation{
|
|
OperationID: "retire-device", Method: http.MethodPost, Path: "/api/v1/devices/{slug}/retire", Tags: []string{"devices"},
|
|
Summary: "Retire a device",
|
|
Description: "Admin only. Soft-deletes the device and its interfaces: polling stops, history is kept, and adding the " +
|
|
"same address again offers to restore it. Retiring a retired device is a 409.",
|
|
Errors: []int{http.StatusNotFound, http.StatusConflict},
|
|
}, s.retireDevice)
|
|
}
|
|
|
|
func (s *server) retireDevice(ctx context.Context, in *struct {
|
|
Slug string `path:"slug" doc:"Device slug."`
|
|
}) (*deviceOut, error) {
|
|
d, err := s.Store.GetDeviceBySlug(ctx, in.Slug)
|
|
if err != nil {
|
|
return nil, s.storeErr(err)
|
|
}
|
|
if d.PurgedAt != nil {
|
|
return nil, huma.Error404NotFound("not found")
|
|
}
|
|
if d.RetiredAt != nil {
|
|
return nil, huma.Error409Conflict("device is already retired")
|
|
}
|
|
if err := s.Store.RetireDevice(ctx, d.ID); err != nil {
|
|
return nil, s.storeErr(err)
|
|
}
|
|
if err := s.Store.AppendAudit(ctx, actorOf(ctx), "device.retire", d.Slug, nil, map[string]any{"name": d.Name}); err != nil {
|
|
s.Logger.Warn("audit device.retire", "err", err)
|
|
}
|
|
s.inventoryChanged(ctx)
|
|
return s.view(ctx, d.Slug)
|
|
}
|