feat(modules): public GET /api/modules discovery endpoint

Add HandleGetModules returning module state, metadata, and
public config (auth providers, Create URI scheme). No auth
required — clients call this pre-login.

Register at /api/modules before the auth middleware.

Ref #97
This commit is contained in:
Forbes
2026-02-14 14:02:11 -06:00
parent b8abd8859d
commit 690ad73161
2 changed files with 49 additions and 0 deletions

View File

@@ -173,6 +173,54 @@ func (s *Server) HandleReady(w http.ResponseWriter, r *http.Request) {
})
}
// HandleGetModules returns the public module discovery response.
// No authentication required — clients call this pre-login.
func (s *Server) HandleGetModules(w http.ResponseWriter, r *http.Request) {
mods := make(map[string]any, 10)
for _, m := range s.modules.All() {
entry := map[string]any{
"enabled": s.modules.IsEnabled(m.ID),
"required": m.Required,
"name": m.Name,
}
if m.Version != "" {
entry["version"] = m.Version
}
if len(m.DependsOn) > 0 {
entry["depends_on"] = m.DependsOn
}
// Public config (non-secret) for specific modules.
switch m.ID {
case "auth":
if s.cfg != nil {
entry["config"] = map[string]any{
"local_enabled": s.cfg.Auth.Local.Enabled,
"ldap_enabled": s.cfg.Auth.LDAP.Enabled,
"oidc_enabled": s.cfg.Auth.OIDC.Enabled,
"oidc_issuer_url": s.cfg.Auth.OIDC.IssuerURL,
}
}
case "freecad":
if s.cfg != nil {
entry["config"] = map[string]any{
"uri_scheme": s.cfg.FreeCAD.URIScheme,
}
}
}
mods[m.ID] = entry
}
writeJSON(w, http.StatusOK, map[string]any{
"modules": mods,
"server": map[string]any{
"version": "0.2",
"read_only": s.serverState.IsReadOnly(),
},
})
}
// Schema handlers
// SchemaResponse represents a schema in API responses.

View File

@@ -58,6 +58,7 @@ func NewRouter(server *Server, logger zerolog.Logger) http.Handler {
r.Get("/auth/callback", server.HandleOIDCCallback)
// Public API endpoints (no auth required)
r.Get("/api/modules", server.HandleGetModules)
r.Get("/api/auth/config", server.HandleAuthConfig)
// API routes (require auth, no CSRF — token auth instead)