feat: M11b — ownership tracking, agent groups, interactive renewal approval

Ownership: owners/teams GUI pages, notification email resolution via
resolveRecipient (owner_id → owner.email lookup). Agent groups: dynamic
device grouping by OS/arch/IP CIDR/version with manual include/exclude
membership, migration 000004, full CRUD stack (domain → repo → service →
handler → frontend). Interactive approval: AwaitingApproval job state,
approve/reject API endpoints with reason tracking. Tests: 12 agent group
handler tests, 8 approve/reject job handler tests, integration tests
updated for 13-param RegisterHandlers. Docs updated across architecture,
concepts, and seed data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Shankar
2026-03-20 21:02:35 -04:00
parent 1ef16984eb
commit e445cbef22
27 changed files with 1774 additions and 21 deletions
+11
View File
@@ -54,6 +54,7 @@ func (r *Router) RegisterHandlers(
profiles handler.ProfileHandler,
teams handler.TeamHandler,
owners handler.OwnerHandler,
agentGroups handler.AgentGroupHandler,
audit handler.AuditHandler,
notifications handler.NotificationHandler,
health handler.HealthHandler,
@@ -117,6 +118,8 @@ func (r *Router) RegisterHandlers(
r.Register("GET /api/v1/jobs", http.HandlerFunc(jobs.ListJobs))
r.Register("GET /api/v1/jobs/{id}", http.HandlerFunc(jobs.GetJob))
r.Register("POST /api/v1/jobs/{id}/cancel", http.HandlerFunc(jobs.CancelJob))
r.Register("POST /api/v1/jobs/{id}/approve", http.HandlerFunc(jobs.ApproveJob))
r.Register("POST /api/v1/jobs/{id}/reject", http.HandlerFunc(jobs.RejectJob))
// Policies routes: /api/v1/policies
r.Register("GET /api/v1/policies", http.HandlerFunc(policies.ListPolicies))
@@ -147,6 +150,14 @@ func (r *Router) RegisterHandlers(
r.Register("PUT /api/v1/owners/{id}", http.HandlerFunc(owners.UpdateOwner))
r.Register("DELETE /api/v1/owners/{id}", http.HandlerFunc(owners.DeleteOwner))
// Agent Groups routes: /api/v1/agent-groups
r.Register("GET /api/v1/agent-groups", http.HandlerFunc(agentGroups.ListAgentGroups))
r.Register("POST /api/v1/agent-groups", http.HandlerFunc(agentGroups.CreateAgentGroup))
r.Register("GET /api/v1/agent-groups/{id}", http.HandlerFunc(agentGroups.GetAgentGroup))
r.Register("PUT /api/v1/agent-groups/{id}", http.HandlerFunc(agentGroups.UpdateAgentGroup))
r.Register("DELETE /api/v1/agent-groups/{id}", http.HandlerFunc(agentGroups.DeleteAgentGroup))
r.Register("GET /api/v1/agent-groups/{id}/members", http.HandlerFunc(agentGroups.ListAgentGroupMembers))
// Audit routes: /api/v1/audit
r.Register("GET /api/v1/audit", http.HandlerFunc(audit.ListAuditEvents))
r.Register("GET /api/v1/audit/{id}", http.HandlerFunc(audit.GetAuditEvent))