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" ) // UserLobbyMembershipsHandlers groups the membership-lifecycle handlers // under `/api/v1/user/lobby/games/{game_id}/memberships/*`. The implementation // ships real implementations backed by `*lobby.Service`. type UserLobbyMembershipsHandlers struct { svc *lobby.Service logger *zap.Logger } // NewUserLobbyMembershipsHandlers constructs the handler set. svc may // be nil — in that case every handler returns 501 not_implemented. func NewUserLobbyMembershipsHandlers(svc *lobby.Service, logger *zap.Logger) *UserLobbyMembershipsHandlers { if logger == nil { logger = zap.NewNop() } return &UserLobbyMembershipsHandlers{svc: svc, logger: logger.Named("http.user.lobby.memberships")} } // List handles GET /api/v1/user/lobby/games/{game_id}/memberships. func (h *UserLobbyMembershipsHandlers) List() gin.HandlerFunc { if h.svc == nil { return handlers.NotImplemented("userLobbyMembershipsList") } return func(c *gin.Context) { gameID, ok := parseGameIDParam(c) if !ok { return } ctx := c.Request.Context() items, err := h.svc.ListMembershipsForGame(ctx, gameID) if err != nil { respondLobbyError(c, h.logger, "user lobby memberships list", ctx, err) return } out := lobbyMembershipListWire{Items: make([]lobbyMembershipDetailWire, 0, len(items))} for _, m := range items { out.Items = append(out.Items, lobbyMembershipDetailToWire(m)) } c.JSON(http.StatusOK, out) } } // Remove handles POST /api/v1/user/lobby/games/{game_id}/memberships/{membership_id}/remove. func (h *UserLobbyMembershipsHandlers) Remove() gin.HandlerFunc { if h.svc == nil { return handlers.NotImplemented("userLobbyMembershipsRemove") } 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 } membershipID, ok := parseMembershipIDParam(c) if !ok { return } ctx := c.Request.Context() caller := userID updated, err := h.svc.RemoveMembership(ctx, &caller, false, gameID, membershipID) if err != nil { respondLobbyError(c, h.logger, "user lobby memberships remove", ctx, err) return } c.JSON(http.StatusOK, lobbyMembershipDetailToWire(updated)) } } // Block handles POST /api/v1/user/lobby/games/{game_id}/memberships/{membership_id}/block. func (h *UserLobbyMembershipsHandlers) Block() gin.HandlerFunc { if h.svc == nil { return handlers.NotImplemented("userLobbyMembershipsBlock") } 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 } membershipID, ok := parseMembershipIDParam(c) if !ok { return } ctx := c.Request.Context() caller := userID updated, err := h.svc.BlockMembership(ctx, &caller, false, gameID, membershipID) if err != nil { respondLobbyError(c, h.logger, "user lobby memberships block", ctx, err) return } c.JSON(http.StatusOK, lobbyMembershipDetailToWire(updated)) } } // lobbyMembershipListWire mirrors `LobbyMembershipList`. type lobbyMembershipListWire struct { Items []lobbyMembershipDetailWire `json:"items"` }