246 lines
6.7 KiB
Go
246 lines
6.7 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
"github.com/patbritton/sojorn-backend/internal/models"
|
|
"github.com/patbritton/sojorn-backend/internal/services"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
type AppealHandler struct {
|
|
appealService *services.AppealService
|
|
}
|
|
|
|
func NewAppealHandler(appealService *services.AppealService) *AppealHandler {
|
|
return &AppealHandler{
|
|
appealService: appealService,
|
|
}
|
|
}
|
|
|
|
// GetUserViolations returns the current user's violation history
|
|
func (h *AppealHandler) GetUserViolations(c *gin.Context) {
|
|
userIDStr, _ := c.Get("user_id")
|
|
userID, err := uuid.Parse(userIDStr.(string))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
|
|
return
|
|
}
|
|
|
|
// Parse pagination
|
|
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))
|
|
offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0"))
|
|
|
|
if limit > 100 {
|
|
limit = 100
|
|
}
|
|
|
|
violations, err := h.appealService.GetUserViolations(c.Request.Context(), userID, limit, offset)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Failed to get user violations")
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get violations"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"violations": violations,
|
|
"limit": limit,
|
|
"offset": offset,
|
|
})
|
|
}
|
|
|
|
// GetUserViolationSummary returns a summary of user's violation status
|
|
func (h *AppealHandler) GetUserViolationSummary(c *gin.Context) {
|
|
userIDStr, _ := c.Get("user_id")
|
|
userID, err := uuid.Parse(userIDStr.(string))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
|
|
return
|
|
}
|
|
|
|
summary, err := h.appealService.GetUserViolationSummary(c.Request.Context(), userID)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Failed to get user violation summary")
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get violation summary"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, summary)
|
|
}
|
|
|
|
// CreateAppeal creates an appeal for a violation
|
|
func (h *AppealHandler) CreateAppeal(c *gin.Context) {
|
|
userIDStr, _ := c.Get("user_id")
|
|
userID, err := uuid.Parse(userIDStr.(string))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
|
|
return
|
|
}
|
|
|
|
var req models.UserAppealRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Validate appeal reason length
|
|
if len(req.AppealReason) < 10 {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Appeal reason must be at least 10 characters"})
|
|
return
|
|
}
|
|
|
|
if len(req.AppealReason) > 1000 {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Appeal reason must be less than 1000 characters"})
|
|
return
|
|
}
|
|
|
|
// Create appeal
|
|
appeal, err := h.appealService.CreateAppeal(c.Request.Context(), userID, &req)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Failed to create appeal")
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, gin.H{"appeal": appeal})
|
|
}
|
|
|
|
// GetAppeal returns details of a specific appeal
|
|
func (h *AppealHandler) GetAppeal(c *gin.Context) {
|
|
userIDStr, _ := c.Get("user_id")
|
|
userID, err := uuid.Parse(userIDStr.(string))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
|
|
return
|
|
}
|
|
|
|
// Get violation first to check if user owns it
|
|
violationID, err := uuid.Parse(c.Query("violation_id"))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid violation ID"})
|
|
return
|
|
}
|
|
|
|
violations, err := h.appealService.GetUserViolations(c.Request.Context(), userID, 1, 0)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Failed to get user violations")
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get violations"})
|
|
return
|
|
}
|
|
|
|
// Check if user has access to this violation
|
|
hasAccess := false
|
|
for _, violation := range violations {
|
|
if violation.ID == violationID {
|
|
hasAccess = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !hasAccess {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Access denied"})
|
|
return
|
|
}
|
|
|
|
appeal, err := h.appealService.GetAppealForViolation(c.Request.Context(), violationID)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Failed to get appeal")
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Appeal not found"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"appeal": appeal})
|
|
}
|
|
|
|
// Admin: GetPendingAppeals returns all pending appeals for admin review
|
|
func (h *AppealHandler) GetPendingAppeals(c *gin.Context) {
|
|
// Check if user is admin
|
|
isAdmin, _ := c.Get("is_admin")
|
|
if !isAdmin.(bool) {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Admin access required"})
|
|
return
|
|
}
|
|
|
|
// Parse pagination
|
|
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
|
|
offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0"))
|
|
|
|
if limit > 100 {
|
|
limit = 100
|
|
}
|
|
|
|
// This would need to be implemented in the service
|
|
// For now, return a placeholder
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"appeals": []interface{}{},
|
|
"limit": limit,
|
|
"offset": offset,
|
|
"total": 0,
|
|
})
|
|
}
|
|
|
|
// Admin: ReviewAppeal allows admin to review and decide on an appeal
|
|
func (h *AppealHandler) ReviewAppeal(c *gin.Context) {
|
|
// Check if user is admin
|
|
userIDStr, _ := c.Get("user_id")
|
|
adminID, err := uuid.Parse(userIDStr.(string))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid admin ID"})
|
|
return
|
|
}
|
|
|
|
isAdmin, _ := c.Get("is_admin")
|
|
if !isAdmin.(bool) {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Admin access required"})
|
|
return
|
|
}
|
|
|
|
appealID, err := uuid.Parse(c.Param("id"))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid appeal ID"})
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Decision string `json:"decision" binding:"required,oneof=approved rejected"`
|
|
ReviewDecision string `json:"review_decision" binding:"required,min=10,max=1000"`
|
|
}
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
err = h.appealService.ReviewAppeal(c.Request.Context(), appealID, adminID, req.Decision, req.ReviewDecision)
|
|
if err != nil {
|
|
log.Error().Err(err).Msg("Failed to review appeal")
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to review appeal"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "Appeal reviewed successfully"})
|
|
}
|
|
|
|
// Admin: GetAppealStats returns statistics about appeals and violations
|
|
func (h *AppealHandler) GetAppealStats(c *gin.Context) {
|
|
// Check if user is admin
|
|
isAdmin, _ := c.Get("is_admin")
|
|
if !isAdmin.(bool) {
|
|
c.JSON(http.StatusForbidden, gin.H{"error": "Admin access required"})
|
|
return
|
|
}
|
|
|
|
// This would need to be implemented in the service
|
|
// For now, return placeholder stats
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"total_violations": 0,
|
|
"pending_appeals": 0,
|
|
"approved_appeals": 0,
|
|
"rejected_appeals": 0,
|
|
"banned_users": 0,
|
|
})
|
|
}
|