191 lines
5.8 KiB
Go
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,
|
|
}
|
|
}
|