sojorn/go-backend/internal/handlers/appeal_handler.go
Patrick Britton da5984d67c refactor: rename Go module from github.com/patbritton to gitlab.com/patrickbritton3
- Rename module path from github.com/patbritton/sojorn-backend to gitlab.com/patrickbritton3/sojorn/go-backend
- Updated 78 references across 41 files
- Matches new GitLab repository structure
2026-02-16 23:58:39 -06:00

246 lines
6.7 KiB
Go

package handlers
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"gitlab.com/patrickbritton3/sojorn/go-backend/internal/models"
"gitlab.com/patrickbritton3/sojorn/go-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,
})
}