package web import ( "net/http" "strings" "ymhut-box/server/unified-management/internal/config" "ymhut-box/server/unified-management/internal/health" "ymhut-box/server/unified-management/internal/notices" ) func (r *router) handleClientBootstrap(w http.ResponseWriter, req *http.Request) { release := r.releases.Manifest(req) sourceCatalog, _ := r.sources.Catalog(false) writeJSON(w, http.StatusOK, map[string]any{ "ok": true, "serviceVersion": config.Version, "baseUrl": requestBaseURL(req, r.cfg.BaseURL), "capabilities": map[string]bool{ "dynamicSources": true, "sourceHealth": true, "feedbackStatus": true, "releaseManifest": true, "endpointCalls": true, "legacyJson": true, }, "endpoints": map[string]string{ "releases": "/api/client/releases", "sources": "/api/client/sources", "clientEndpoints": "/api/client/endpoints", "endpointCalls": "/api/client/endpoint-calls", "notices": "/api/client/notices", "feedback": "/", }, "cache": map[string]int{ "bootstrapSeconds": 300, "releasesSeconds": 300, "sourcesSeconds": 600, "healthSeconds": 300, }, "legacyRoutes": []string{"/update-info.json", "/update-info", "/api/update-info", "/api/releases", "/tool-status.json", "/media-types.json", "/modules.json", "/downloads/:filename"}, "release": release, "sources": sourceCatalog, "feedback": map[string]any{"submit": "/", "status": "/?api=status&code=:code"}, "branding": config.SafeBranding(r.effectiveBranding()), "health": health.Snapshot(r.cfg, r.store), }) } func (r *router) handleClientSources(w http.ResponseWriter, req *http.Request) { catalog, err := r.sources.Catalog(false) if err != nil { writeError(w, http.StatusInternalServerError, "SOURCES_FAILED", err) return } writeJSON(w, http.StatusOK, catalog) } func (r *router) handleClientEndpoints(w http.ResponseWriter, req *http.Request) { items, err := r.sources.Endpoints(false) if err != nil { writeError(w, http.StatusInternalServerError, "ENDPOINTS_FAILED", err) return } writeJSON(w, http.StatusOK, map[string]any{"ok": true, "items": items}) } func (r *router) handleClientNotices(w http.ResponseWriter, req *http.Request) { if r.notices == nil { writeJSON(w, http.StatusOK, map[string]any{"ok": true, "items": []any{}}) return } path := cleanPath(req.URL.Path) if path == "/api/client/notices" { items, err := r.notices.List(100) if err != nil { writeError(w, http.StatusInternalServerError, "NOTICES_FAILED", err) return } writeJSON(w, http.StatusOK, map[string]any{"ok": true, "items": notices.PublicList(items)}) return } version := strings.TrimPrefix(path, "/api/client/notices/") if version == "" { http.NotFound(w, req) return } doc, err := r.notices.Get(version) if err != nil { writeError(w, http.StatusNotFound, "NOTICE_NOT_FOUND", err) return } writeJSON(w, http.StatusOK, map[string]any{"ok": true, "notice": notices.PublicNotice(doc.Notice), "raw": doc.Parsed}) }