package server import ( "net/http" "galaxy/backend/internal/lobby" "galaxy/backend/internal/server/handlers" "galaxy/backend/internal/server/httperr" "galaxy/backend/internal/server/middleware/userid" "github.com/gin-gonic/gin" "go.uber.org/zap" ) // UserLobbyApplicationsHandlers groups the application-lifecycle handlers // under `/api/v1/user/lobby/games/{game_id}/applications/*`. The implementation // ships real implementations backed by `*lobby.Service`. type UserLobbyApplicationsHandlers struct { svc *lobby.Service logger *zap.Logger } // NewUserLobbyApplicationsHandlers constructs the handler set. svc may // be nil — in that case every handler returns 501 not_implemented. func NewUserLobbyApplicationsHandlers(svc *lobby.Service, logger *zap.Logger) *UserLobbyApplicationsHandlers { if logger == nil { logger = zap.NewNop() } return &UserLobbyApplicationsHandlers{svc: svc, logger: logger.Named("http.user.lobby.applications")} } // Submit handles POST /api/v1/user/lobby/games/{game_id}/applications. func (h *UserLobbyApplicationsHandlers) Submit() gin.HandlerFunc { if h.svc == nil { return handlers.NotImplemented("userLobbyApplicationsSubmit") } return func(c *gin.Context) { userID, ok := userid.FromContext(c.Request.Context()) if !ok { httperr.Abort(c, http.StatusBadRequest, httperr.CodeInvalidRequest, "X-User-ID header is required") return } gameID, ok := parseGameIDParam(c) if !ok { return } var req lobbyApplicationSubmitRequestWire if err := c.ShouldBindJSON(&req); err != nil { httperr.Abort(c, http.StatusBadRequest, httperr.CodeInvalidRequest, "request body must be valid JSON") return } ctx := c.Request.Context() app, err := h.svc.SubmitApplication(ctx, lobby.SubmitApplicationInput{ GameID: gameID, ApplicantUserID: userID, RaceName: req.RaceName, }) if err != nil { respondLobbyError(c, h.logger, "user lobby applications submit", ctx, err) return } c.JSON(http.StatusCreated, lobbyApplicationDetailToWire(app)) } } // Approve handles POST /api/v1/user/lobby/games/{game_id}/applications/{application_id}/approve. func (h *UserLobbyApplicationsHandlers) Approve() gin.HandlerFunc { if h.svc == nil { return handlers.NotImplemented("userLobbyApplicationsApprove") } return func(c *gin.Context) { userID, ok := userid.FromContext(c.Request.Context()) if !ok { httperr.Abort(c, http.StatusBadRequest, httperr.CodeInvalidRequest, "X-User-ID header is required") return } gameID, ok := parseGameIDParam(c) if !ok { return } applicationID, ok := parseApplicationIDParam(c) if !ok { return } ctx := c.Request.Context() caller := userID updated, err := h.svc.ApproveApplication(ctx, &caller, false, gameID, applicationID) if err != nil { respondLobbyError(c, h.logger, "user lobby applications approve", ctx, err) return } c.JSON(http.StatusOK, lobbyApplicationDetailToWire(updated)) } } // Reject handles POST /api/v1/user/lobby/games/{game_id}/applications/{application_id}/reject. func (h *UserLobbyApplicationsHandlers) Reject() gin.HandlerFunc { if h.svc == nil { return handlers.NotImplemented("userLobbyApplicationsReject") } return func(c *gin.Context) { userID, ok := userid.FromContext(c.Request.Context()) if !ok { httperr.Abort(c, http.StatusBadRequest, httperr.CodeInvalidRequest, "X-User-ID header is required") return } gameID, ok := parseGameIDParam(c) if !ok { return } applicationID, ok := parseApplicationIDParam(c) if !ok { return } ctx := c.Request.Context() caller := userID updated, err := h.svc.RejectApplication(ctx, &caller, false, gameID, applicationID) if err != nil { respondLobbyError(c, h.logger, "user lobby applications reject", ctx, err) return } c.JSON(http.StatusOK, lobbyApplicationDetailToWire(updated)) } } // lobbyApplicationSubmitRequestWire mirrors `LobbyApplicationSubmitRequest`. type lobbyApplicationSubmitRequestWire struct { RaceName string `json:"race_name"` }