clonepack/internal/api/sync_handler.go
GnomeZworc e1aff8d16b
add logging
Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
2026-04-25 20:00:00 +02:00

191 lines
5.8 KiB
Go

package api
import (
"context"
"encoding/json"
"errors"
"log/slog"
"net/http"
"strconv"
"github.com/go-chi/chi/v5"
"github.com/syonad/clonepack/internal/core"
"github.com/syonad/clonepack/internal/store"
)
type SyncHandler struct {
svc *core.SyncService
}
func NewSyncHandler(svc *core.SyncService) *SyncHandler {
return &SyncHandler{svc: svc}
}
func (h *SyncHandler) Trigger(w http.ResponseWriter, r *http.Request) {
repoID, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
if err != nil {
Error(w, http.StatusBadRequest, "invalid id")
return
}
// Validate repo exists before returning.
if err := h.svc.ValidateRepo(r.Context(), repoID); errors.Is(err, store.ErrNotFound) {
Error(w, http.StatusNotFound, "repo not found")
return
} else if err != nil {
Error(w, http.StatusInternalServerError, err.Error())
return
}
go func() {
if err := h.svc.ScanRepo(context.Background(), repoID); err != nil {
slog.Warn("scan repo failed", "repo_id", repoID, "error", err)
}
}()
JSON(w, http.StatusAccepted, map[string]string{"status": "scan started"})
}
func (h *SyncHandler) ListPending(w http.ResponseWriter, r *http.Request) {
repoID, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
if err != nil {
Error(w, http.StatusBadRequest, "invalid id")
return
}
pkgs, err := h.svc.ListPending(r.Context(), repoID)
if errors.Is(err, store.ErrNotFound) {
Error(w, http.StatusNotFound, "repo not found")
return
} else if err != nil {
Error(w, http.StatusInternalServerError, err.Error())
return
}
items := make([]PendingPackageResponse, len(pkgs))
for i, p := range pkgs {
items[i] = pendingToResponse(p)
}
JSON(w, http.StatusOK, ListPendingResponse{Items: items, Total: len(items)})
}
func (h *SyncHandler) Approve(w http.ResponseWriter, r *http.Request) {
repoID, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
if err != nil {
Error(w, http.StatusBadRequest, "invalid id")
return
}
var req SyncSelectionRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil || len(req.IDs) == 0 {
Error(w, http.StatusBadRequest, "body must contain non-empty ids array")
return
}
if err := h.svc.ValidateRepo(r.Context(), repoID); errors.Is(err, store.ErrNotFound) {
Error(w, http.StatusNotFound, "repo not found")
return
} else if err != nil {
Error(w, http.StatusInternalServerError, err.Error())
return
}
ids := req.IDs
go func() {
if err := h.svc.ApprovePending(context.Background(), repoID, ids); err != nil {
slog.Warn("approve pending failed", "repo_id", repoID, "error", err)
}
}()
JSON(w, http.StatusAccepted, map[string]string{"status": "approval started"})
}
func (h *SyncHandler) Reject(w http.ResponseWriter, r *http.Request) {
repoID, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
if err != nil {
Error(w, http.StatusBadRequest, "invalid id")
return
}
var req SyncSelectionRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil || len(req.IDs) == 0 {
Error(w, http.StatusBadRequest, "body must contain non-empty ids array")
return
}
if err := h.svc.RejectPending(r.Context(), repoID, req.IDs); 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 (h *SyncHandler) Block(w http.ResponseWriter, r *http.Request) {
repoID, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
if err != nil {
Error(w, http.StatusBadRequest, "invalid id")
return
}
var req SyncSelectionRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil || len(req.IDs) == 0 {
Error(w, http.StatusBadRequest, "body must contain non-empty ids array")
return
}
if err := h.svc.BlockPackages(r.Context(), repoID, req.IDs); 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 (h *SyncHandler) Unblock(w http.ResponseWriter, r *http.Request) {
repoID, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
if err != nil {
Error(w, http.StatusBadRequest, "invalid id")
return
}
var req SyncSelectionRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil || len(req.IDs) == 0 {
Error(w, http.StatusBadRequest, "body must contain non-empty ids array")
return
}
if err := h.svc.UnblockPackages(r.Context(), repoID, req.IDs); 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 (h *SyncHandler) ListBlocked(w http.ResponseWriter, r *http.Request) {
repoID, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
if err != nil {
Error(w, http.StatusBadRequest, "invalid id")
return
}
pkgs, err := h.svc.ListBlocked(r.Context(), repoID)
if errors.Is(err, store.ErrNotFound) {
Error(w, http.StatusNotFound, "repo not found")
return
} else if err != nil {
Error(w, http.StatusInternalServerError, err.Error())
return
}
items := make([]BlockedPackageResponse, len(pkgs))
for i, p := range pkgs {
items[i] = BlockedPackageResponse{ID: p.ID, RepoID: p.RepoID, Name: p.Name, Location: p.Location, CreatedAt: p.CreatedAt}
}
JSON(w, http.StatusOK, ListBlockedResponse{Items: items, Total: len(items)})
}
func pendingToResponse(p store.PendingPackage) PendingPackageResponse {
return PendingPackageResponse{
ID: p.ID,
RepoID: p.RepoID,
Name: p.Name,
Version: p.Version,
Arch: p.Arch,
Location: p.Location,
Checksum: p.Checksum,
ChecksumType: p.ChecksumType,
Size: p.Size,
CreatedAt: p.CreatedAt,
}
}