veza/veza-backend-api/internal/handlers/queue_session_handler.go
2026-03-05 23:03:43 +01:00

162 lines
5.1 KiB
Go

package handlers
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"go.uber.org/zap"
apperrors "veza-backend-api/internal/errors"
"veza-backend-api/internal/services"
)
// QueueSessionHandler handles collaborative queue session HTTP requests (v0.203 Lot D1)
type QueueSessionHandler struct {
svc *services.QueueSessionService
logger *zap.Logger
}
// NewQueueSessionHandler creates a new QueueSessionHandler
func NewQueueSessionHandler(svc *services.QueueSessionService, logger *zap.Logger) *QueueSessionHandler {
return &QueueSessionHandler{svc: svc, logger: logger}
}
// CreateSession creates a new shared queue session
func (h *QueueSessionHandler) CreateSession(c *gin.Context) {
userID, ok := GetUserIDUUID(c)
if !ok {
return
}
session, err := h.svc.CreateSession(c.Request.Context(), userID)
if err != nil {
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to create session", err))
return
}
RespondSuccess(c, http.StatusCreated, gin.H{
"session": session,
"share_token": session.ShareToken,
"share_url": "/queue?session=" + session.ShareToken,
})
}
// GetSession returns a session's queue (auth optional for joining via link)
func (h *QueueSessionHandler) GetSession(c *gin.Context) {
token := c.Param("token")
if token == "" {
RespondWithAppError(c, apperrors.NewValidationError("token is required"))
return
}
session, items, err := h.svc.GetSessionByToken(c.Request.Context(), token)
if err != nil {
if errors.Is(err, services.ErrQueueSessionNotFound) {
RespondWithAppError(c, apperrors.NewNotFoundError("session not found"))
return
}
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to get session", err))
return
}
// Map items to response with tracks
type itemResp struct {
ID string `json:"id"`
Position int `json:"position"`
AddedAt string `json:"added_at"`
Track interface{} `json:"track,omitempty"`
}
respItems := make([]itemResp, 0, len(items))
for _, it := range items {
r := itemResp{
ID: it.ID.String(),
Position: it.Position,
AddedAt: it.AddedAt.Format("2006-01-02T15:04:05Z07:00"),
}
if it.Track.ID != uuid.Nil {
r.Track = it.Track
}
respItems = append(respItems, r)
}
RespondSuccess(c, http.StatusOK, gin.H{
"session": session,
"items": respItems,
})
}
// DeleteSession deletes a session (creator only)
func (h *QueueSessionHandler) DeleteSession(c *gin.Context) {
userID, ok := GetUserIDUUID(c)
if !ok {
return
}
token := c.Param("token")
if token == "" {
RespondWithAppError(c, apperrors.NewValidationError("token is required"))
return
}
if err := h.svc.DeleteSession(c.Request.Context(), token, userID); err != nil {
if errors.Is(err, services.ErrQueueSessionNotFound) {
RespondWithAppError(c, apperrors.NewNotFoundError("session not found"))
return
}
if errors.Is(err, services.ErrQueueSessionForbidden) {
RespondWithAppError(c, apperrors.NewForbiddenError("only the creator can delete this session"))
return
}
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to delete session", err))
return
}
RespondSuccess(c, http.StatusOK, gin.H{"message": "session deleted"})
}
// AddToSessionRequest represents the request body
type AddToSessionRequest struct {
TrackID uuid.UUID `json:"track_id" binding:"required"`
}
// AddToSession adds a track to a session's queue
func (h *QueueSessionHandler) AddToSession(c *gin.Context) {
token := c.Param("token")
if token == "" {
RespondWithAppError(c, apperrors.NewValidationError("token is required"))
return
}
var req AddToSessionRequest
if err := c.ShouldBindJSON(&req); err != nil {
RespondWithAppError(c, apperrors.NewValidationError("track_id is required"))
return
}
if err := h.svc.AddToSession(c.Request.Context(), token, req.TrackID); err != nil {
if errors.Is(err, services.ErrQueueSessionNotFound) {
RespondWithAppError(c, apperrors.NewNotFoundError("session not found"))
return
}
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to add to session", err))
return
}
// Return updated session
session, items, _ := h.svc.GetSessionByToken(c.Request.Context(), token)
RespondSuccess(c, http.StatusCreated, gin.H{"session": session, "items": items})
}
// RemoveFromSession removes an item from a session's queue
func (h *QueueSessionHandler) RemoveFromSession(c *gin.Context) {
token := c.Param("token")
if token == "" {
RespondWithAppError(c, apperrors.NewValidationError("token is required"))
return
}
itemID, err := uuid.Parse(c.Param("id"))
if err != nil {
RespondWithAppError(c, apperrors.NewValidationError("invalid item ID"))
return
}
if err := h.svc.RemoveFromSession(c.Request.Context(), token, itemID); err != nil {
if errors.Is(err, services.ErrQueueSessionNotFound) {
RespondWithAppError(c, apperrors.NewNotFoundError("session or item not found"))
return
}
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to remove from session", err))
return
}
RespondSuccess(c, http.StatusOK, gin.H{"message": "item removed"})
}