package api import ( "net/http" "strings" "reanimator/internal/repository/registry" "reanimator/internal/repository/timeline" ) type AssetComponentDependencies struct { Assets *registry.AssetRepository Components *registry.ComponentRepository Installations *registry.InstallationRepository Timeline *timeline.EventRepository } type assetComponentHandlers struct { deps AssetComponentDependencies } func RegisterAssetComponentRoutes(mux *http.ServeMux, deps AssetComponentDependencies) { h := assetComponentHandlers{deps: deps} mux.HandleFunc("/assets/", h.handleAsset) mux.HandleFunc("/components/", h.handleComponent) } func (h assetComponentHandlers) handleAsset(w http.ResponseWriter, r *http.Request) { if strings.HasSuffix(r.URL.Path, "/timeline") { if r.Method != http.MethodGet { w.WriteHeader(http.StatusMethodNotAllowed) return } if h.deps.Timeline == nil { writeError(w, http.StatusInternalServerError, "timeline unavailable") return } id := parseTimelineID(r.URL.Path, "/assets/") if id == "" { writeError(w, http.StatusNotFound, "asset timeline not found") return } writeTimelineResponse(w, r, h.deps.Timeline, "asset", id) return } if strings.HasSuffix(r.URL.Path, "/components") { if r.Method != http.MethodGet { w.WriteHeader(http.StatusMethodNotAllowed) return } if h.deps.Installations == nil { writeError(w, http.StatusInternalServerError, "installations unavailable") return } id, ok := parseSubresourceID(r.URL.Path, "/assets/", "/components") if !ok { writeError(w, http.StatusNotFound, "asset components not found") return } items, err := h.deps.Installations.ListCurrentComponentsByAsset(r.Context(), id) if err != nil { writeError(w, http.StatusInternalServerError, "list components failed") return } writeJSON(w, http.StatusOK, map[string]any{"items": items}) return } if r.Method != http.MethodGet { w.WriteHeader(http.StatusMethodNotAllowed) return } id, ok := parseID(r.URL.Path, "/assets/") if !ok { writeError(w, http.StatusNotFound, "asset not found") return } item, err := h.deps.Assets.Get(r.Context(), id) if err != nil { switch err { case registry.ErrNotFound: writeError(w, http.StatusNotFound, "asset not found") default: writeError(w, http.StatusInternalServerError, "get asset failed") } return } writeJSON(w, http.StatusOK, item) } func (h assetComponentHandlers) handleComponent(w http.ResponseWriter, r *http.Request) { if strings.HasSuffix(r.URL.Path, "/timeline") { if r.Method != http.MethodGet { w.WriteHeader(http.StatusMethodNotAllowed) return } if h.deps.Timeline == nil { writeError(w, http.StatusInternalServerError, "timeline unavailable") return } id := parseTimelineID(r.URL.Path, "/components/") if id == "" { writeError(w, http.StatusNotFound, "component timeline not found") return } writeTimelineResponse(w, r, h.deps.Timeline, "component", id) return } if r.Method != http.MethodGet { w.WriteHeader(http.StatusMethodNotAllowed) return } id, ok := parseID(r.URL.Path, "/components/") if !ok { writeError(w, http.StatusNotFound, "component not found") return } item, err := h.deps.Components.Get(r.Context(), id) if err != nil { switch err { case registry.ErrNotFound: writeError(w, http.StatusNotFound, "component not found") default: writeError(w, http.StatusInternalServerError, "get component failed") } return } writeJSON(w, http.StatusOK, item) }