Files
nadir-agent/internal/meta/meta.go
T
urania 2bf11dda91
build-and-release / release (push) Failing after 17m7s
feat: first release
2026-06-22 16:51:18 +02:00

59 lines
2.0 KiB
Go

package meta
import (
"cmp"
"context"
"slices"
"nadir/internal/module"
"github.com/danielgtaylor/huma/v2"
)
// ModuleInfo describes a registered module: its stable ID, display name, and
// the permissions it exposes. The frontend uses this to drive navigation and
// render the role/permission matrix.
type ModuleInfo struct {
ID string `json:"id" example:"system" doc:"Stable module identifier"`
Name string `json:"name" example:"System" doc:"Human-readable module name"`
Permissions []string `json:"permissions" doc:"Permissions this module exposes (never includes the \"*\" wildcard)"`
}
type ModulesOutput struct {
Body struct {
Modules []ModuleInfo `json:"modules" doc:"Registered modules, sorted by ID"`
}
}
// Register adds the read-only module-discovery endpoint. It is intentionally
// public: it exposes only the API's static shape (module IDs and permission
// vocabulary), the same information already served by /openapi.json. The module
// list is fixed at startup, so the response is computed once here.
func Register(api huma.API, mods []module.Module) {
infos := make([]ModuleInfo, 0, len(mods))
for _, m := range mods {
perms := m.Permissions()
ps := make([]string, len(perms))
for i, p := range perms {
ps[i] = string(p)
}
infos = append(infos, ModuleInfo{ID: m.ID(), Name: module.Title(m.ID()), Permissions: ps})
}
slices.SortFunc(infos, func(a, b ModuleInfo) int { return cmp.Compare(a.ID, b.ID) })
huma.Register(api, huma.Operation{
OperationID: "list-modules",
Method: "GET",
Path: "/api/_modules",
Summary: "List registered modules",
Description: "Returns every registered module with its ID, display name, " +
"and exported permissions. Public (same static shape as /openapi.json); " +
"used by the frontend for navigation and the role/permission matrix.",
Tags: []string{"Meta"},
}, func(ctx context.Context, _ *struct{}) (*ModulesOutput, error) {
out := &ModulesOutput{}
out.Body.Modules = infos
return out, nil
})
}