package api import ( "encoding/json" "errors" "net/http" "strconv" "github.com/go-chi/chi/v5" aptclone "github.com/syonad/clonepack/internal/clone/apt" "github.com/syonad/clonepack/internal/core" "github.com/syonad/clonepack/internal/store" ) type RepoHandler struct { svc *core.RepoService } func NewRepoHandler(svc *core.RepoService) *RepoHandler { return &RepoHandler{svc: svc} } func (h *RepoHandler) Create(w http.ResponseWriter, r *http.Request) { var req CreateRepoRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { Error(w, http.StatusBadRequest, "invalid JSON") return } repo, err := h.svc.Create(r.Context(), core.CreateRepoInput{ Name: req.Name, Type: req.Type, SourceURL: req.SourceURL, SyncMode: req.SyncMode, AptSuite: req.AptSuite, AptComponents: req.AptComponents, AptArchitectures: req.AptArchitectures, }) if err != nil { Error(w, http.StatusUnprocessableEntity, err.Error()) return } JSON(w, http.StatusCreated, repoToResponse(repo)) } func (h *RepoHandler) List(w http.ResponseWriter, r *http.Request) { repos, err := h.svc.List(r.Context()) if err != nil { Error(w, http.StatusInternalServerError, err.Error()) return } items := make([]RepoResponse, len(repos)) for i, repo := range repos { items[i] = repoToResponse(&repo) } JSON(w, http.StatusOK, ListReposResponse{Items: items, Total: len(items)}) } func (h *RepoHandler) Get(w http.ResponseWriter, r *http.Request) { id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) if err != nil { Error(w, http.StatusBadRequest, "invalid id") return } repo, err := h.svc.Get(r.Context(), id) if errors.Is(err, store.ErrNotFound) { Error(w, http.StatusNotFound, "repo not found") return } if err != nil { Error(w, http.StatusInternalServerError, err.Error()) return } JSON(w, http.StatusOK, repoToResponse(repo)) } func (h *RepoHandler) Update(w http.ResponseWriter, r *http.Request) { id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) if err != nil { Error(w, http.StatusBadRequest, "invalid id") return } var req UpdateRepoRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { Error(w, http.StatusBadRequest, "invalid JSON") return } repo, err := h.svc.Update(r.Context(), id, core.UpdateRepoInput{ SourceURL: req.SourceURL, SyncMode: req.SyncMode, AptSuite: req.AptSuite, AptComponents: req.AptComponents, AptArchitectures: req.AptArchitectures, }) if errors.Is(err, store.ErrNotFound) { Error(w, http.StatusNotFound, "repo not found") return } if err != nil { Error(w, http.StatusUnprocessableEntity, err.Error()) return } JSON(w, http.StatusOK, repoToResponse(repo)) } func (h *RepoHandler) Delete(w http.ResponseWriter, r *http.Request) { id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) if err != nil { Error(w, http.StatusBadRequest, "invalid id") return } if err := h.svc.Delete(r.Context(), id); errors.Is(err, store.ErrNotFound) { Error(w, http.StatusNotFound, "repo not found") return } else if err != nil { Error(w, http.StatusInternalServerError, err.Error()) return } w.WriteHeader(http.StatusNoContent) } func repoToResponse(r *store.Repo) RepoResponse { resp := RepoResponse{ ID: r.ID, Name: r.Name, Type: r.Type, SourceURL: r.SourceURL, Frozen: r.Frozen, SyncMode: r.SyncMode, CreatedAt: r.CreatedAt, } if r.Type == "apt" && r.Config != "" { var cfg aptclone.Config if err := json.Unmarshal([]byte(r.Config), &cfg); err == nil { resp.AptSuite = cfg.Suite resp.AptComponents = cfg.Components resp.AptArchitectures = cfg.Architectures } } return resp }