// Package handlers contains HTTP request handlers package handlers import ( "encoding/json" "net/http" "strconv" "github.com/go-chi/chi/v5" "omixlab.com/mosis-portal/internal/database" "omixlab.com/mosis-portal/internal/review" "omixlab.com/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) }