227 lines
5.8 KiB
Go
227 lines
5.8 KiB
Go
// Package handlers contains HTTP request handlers
|
|
package handlers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/omixlab/mosis-portal/internal/database"
|
|
"github.com/omixlab/mosis-portal/internal/review"
|
|
"github.com/omixlab/mosis-portal/internal/storage"
|
|
)
|
|
|
|
// AdminHandler handles admin operations
|
|
type AdminHandler struct {
|
|
db *database.DB
|
|
store *storage.Storage
|
|
review *review.Service
|
|
}
|
|
|
|
// NewAdminHandler creates a new admin handler
|
|
func NewAdminHandler(db *database.DB, store *storage.Storage) *AdminHandler {
|
|
return &AdminHandler{
|
|
db: db,
|
|
store: store,
|
|
review: review.New(db),
|
|
}
|
|
}
|
|
|
|
// Dashboard returns the admin dashboard with stats
|
|
func (h *AdminHandler) Dashboard(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
|
|
pending, approved, rejected, err := h.db.GetReviewStats(ctx)
|
|
if err != nil {
|
|
Error(w, http.StatusInternalServerError, "database_error", err.Error())
|
|
return
|
|
}
|
|
|
|
JSON(w, http.StatusOK, map[string]interface{}{
|
|
"stats": map[string]int{
|
|
"pending": pending,
|
|
"approved": approved,
|
|
"rejected": rejected,
|
|
},
|
|
})
|
|
}
|
|
|
|
// ReviewQueue lists versions pending review
|
|
func (h *AdminHandler) ReviewQueue(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
|
|
// Parse pagination
|
|
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
|
|
if limit < 1 || limit > 100 {
|
|
limit = 20
|
|
}
|
|
offset := (page - 1) * limit
|
|
|
|
versions, total, err := h.db.GetVersionsInReview(ctx, limit, offset)
|
|
if err != nil {
|
|
Error(w, http.StatusInternalServerError, "database_error", err.Error())
|
|
return
|
|
}
|
|
|
|
JSON(w, http.StatusOK, map[string]interface{}{
|
|
"items": versions,
|
|
"pagination": map[string]int{
|
|
"page": page,
|
|
"limit": limit,
|
|
"total": total,
|
|
"total_pages": (total + limit - 1) / limit,
|
|
},
|
|
})
|
|
}
|
|
|
|
// ReviewDetail returns details for a specific version under review
|
|
func (h *AdminHandler) ReviewDetail(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
versionID := chi.URLParam(r, "versionID")
|
|
|
|
versionWithApp, err := h.db.GetVersionWithApp(ctx, versionID)
|
|
if err != nil {
|
|
Error(w, http.StatusNotFound, "not_found", "Version not found")
|
|
return
|
|
}
|
|
|
|
// If package exists, run validation to get flags
|
|
var validationResult *review.FullValidationResult
|
|
if versionWithApp.Version.PackageURL != "" {
|
|
packagePath := h.store.GetPackagePath(versionWithApp.Version.PackageURL)
|
|
result, err := h.review.ValidatePackage(packagePath)
|
|
if err == nil {
|
|
validationResult = result
|
|
}
|
|
}
|
|
|
|
JSON(w, http.StatusOK, map[string]interface{}{
|
|
"version": versionWithApp.Version,
|
|
"app": versionWithApp.App,
|
|
"developer": map[string]string{
|
|
"name": versionWithApp.DeveloperName,
|
|
"email": versionWithApp.DeveloperEmail,
|
|
},
|
|
"validation": validationResult,
|
|
})
|
|
}
|
|
|
|
// ApproveVersion approves a version
|
|
func (h *AdminHandler) ApproveVersion(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
versionID := chi.URLParam(r, "versionID")
|
|
|
|
// Parse request body
|
|
var req struct {
|
|
Notes string `json:"notes"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
// Notes are optional, continue with empty
|
|
}
|
|
|
|
// Verify version exists and is in review
|
|
version, err := h.db.GetVersion(ctx, versionID)
|
|
if err != nil {
|
|
Error(w, http.StatusNotFound, "not_found", "Version not found")
|
|
return
|
|
}
|
|
|
|
if version.Status != "in_review" {
|
|
Error(w, http.StatusBadRequest, "invalid_status", "Version is not in review")
|
|
return
|
|
}
|
|
|
|
// Approve the version
|
|
if err := h.review.ApproveVersion(ctx, versionID, req.Notes); err != nil {
|
|
Error(w, http.StatusInternalServerError, "database_error", err.Error())
|
|
return
|
|
}
|
|
|
|
JSON(w, http.StatusOK, map[string]interface{}{
|
|
"success": true,
|
|
"message": "Version approved and published",
|
|
})
|
|
}
|
|
|
|
// RejectVersion rejects a version
|
|
func (h *AdminHandler) RejectVersion(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
versionID := chi.URLParam(r, "versionID")
|
|
|
|
// Parse request body
|
|
var req struct {
|
|
Reason string `json:"reason"`
|
|
Message string `json:"message"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
Error(w, http.StatusBadRequest, "invalid_body", "Invalid request body")
|
|
return
|
|
}
|
|
|
|
if req.Reason == "" {
|
|
Error(w, http.StatusBadRequest, "missing_reason", "Rejection reason is required")
|
|
return
|
|
}
|
|
|
|
// Verify version exists and is in review
|
|
version, err := h.db.GetVersion(ctx, versionID)
|
|
if err != nil {
|
|
Error(w, http.StatusNotFound, "not_found", "Version not found")
|
|
return
|
|
}
|
|
|
|
if version.Status != "in_review" {
|
|
Error(w, http.StatusBadRequest, "invalid_status", "Version is not in review")
|
|
return
|
|
}
|
|
|
|
// Reject the version
|
|
feedback := &review.RejectionFeedback{
|
|
Reason: req.Reason,
|
|
Message: req.Message,
|
|
CanResubmit: true,
|
|
}
|
|
if err := h.review.RejectVersion(ctx, versionID, feedback); err != nil {
|
|
Error(w, http.StatusInternalServerError, "database_error", err.Error())
|
|
return
|
|
}
|
|
|
|
JSON(w, http.StatusOK, map[string]interface{}{
|
|
"success": true,
|
|
"message": "Version rejected",
|
|
})
|
|
}
|
|
|
|
// ValidatePackage runs validation on a package and returns results
|
|
func (h *AdminHandler) ValidatePackage(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
versionID := chi.URLParam(r, "versionID")
|
|
|
|
// Get version with package info
|
|
version, err := h.db.GetVersion(ctx, versionID)
|
|
if err != nil {
|
|
Error(w, http.StatusNotFound, "not_found", "Version not found")
|
|
return
|
|
}
|
|
|
|
if version.PackageURL == "" {
|
|
Error(w, http.StatusBadRequest, "no_package", "Version has no uploaded package")
|
|
return
|
|
}
|
|
|
|
// Run validation
|
|
packagePath := h.store.GetPackagePath(version.PackageURL)
|
|
result, err := h.review.ValidatePackage(packagePath)
|
|
if err != nil {
|
|
Error(w, http.StatusInternalServerError, "validation_error", err.Error())
|
|
return
|
|
}
|
|
|
|
JSON(w, http.StatusOK, result)
|
|
}
|