Implement account management features in API, including endpoints for listing accounts and updating account roles. Introduce moderator role with associated permissions, and refactor access control checks to accommodate staff roles. Update database schema to support new role constraints and enhance frontend navigation for staff access.
This commit is contained in:
@@ -65,6 +65,8 @@ func New(service *application.Service, store application.Store, hub *realtime.Hu
|
||||
api.Get("/api/me", s.me)
|
||||
api.Patch("/api/me/player", s.updateProfile)
|
||||
api.Get("/api/players", s.players)
|
||||
api.Get("/api/accounts", s.accounts)
|
||||
api.Patch("/api/accounts/{accountID}/moderator", s.setModerator)
|
||||
api.Get("/api/events", s.events)
|
||||
api.Post("/api/events", s.createEvent)
|
||||
api.Get("/api/events/{eventID}", s.getEvent)
|
||||
@@ -84,10 +86,14 @@ func New(service *application.Service, store application.Store, hub *realtime.Hu
|
||||
api.Get("/api/events/{eventID}/roster", s.getRoster)
|
||||
api.Put("/api/events/{eventID}/roster", s.selectWorkflowBalance)
|
||||
api.Post("/api/events/{eventID}/roster/swap", s.swapRoster)
|
||||
api.Post("/api/events/{eventID}/roster/move", s.moveRosterPlayer)
|
||||
api.Post("/api/events/{eventID}/roster/place-reserve", s.placeReservePlayer)
|
||||
api.Post("/api/events/{eventID}/roster/remove", s.removeRosterPlayer)
|
||||
api.Post("/api/events/{eventID}/roster/substitute", s.substituteRoster)
|
||||
api.Post("/api/events/{eventID}/roster/emergency-substitute", s.emergencySubstitute)
|
||||
api.Put("/api/events/{eventID}/roster/captain", s.setRosterCaptain)
|
||||
api.Post("/api/events/{eventID}/roster/confirm", s.confirmRosters)
|
||||
api.Post("/api/events/{eventID}/workflow/back", s.revertWorkflowStage)
|
||||
api.Post("/api/events/{eventID}/start", s.startScrim)
|
||||
api.Put("/api/teams/{teamID}/captain", s.assignCaptain)
|
||||
api.Post("/api/rulesets", s.saveRuleset)
|
||||
@@ -220,8 +226,8 @@ func (s *Server) authenticate(next http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
func who(r *http.Request) identity { return r.Context().Value(identityKey{}).(identity) }
|
||||
func requireAdmin(r *http.Request) error {
|
||||
if !who(r).account.IsAdmin() {
|
||||
func requireStaff(r *http.Request) error {
|
||||
if !who(r).account.IsStaff() {
|
||||
return domain.ErrForbidden
|
||||
}
|
||||
return nil
|
||||
@@ -253,6 +259,22 @@ func (s *Server) players(w http.ResponseWriter, r *http.Request) {
|
||||
respond(w, out, err, 200)
|
||||
}
|
||||
|
||||
func (s *Server) accounts(w http.ResponseWriter, r *http.Request) {
|
||||
out, err := s.service.ListAccounts(r.Context(), who(r).account)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) setModerator(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
Moderator bool `json:"moderator"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
out, err := s.service.SetModerator(r.Context(), who(r).account, chi.URLParam(r, "accountID"), in.Moderator)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) events(w http.ResponseWriter, r *http.Request) {
|
||||
from := time.Unix(0, 0).UTC()
|
||||
if raw := r.URL.Query().Get("from"); raw != "" {
|
||||
@@ -358,7 +380,7 @@ func (s *Server) assignCaptain(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (s *Server) saveRuleset(w http.ResponseWriter, r *http.Request) {
|
||||
if err := requireAdmin(r); err != nil {
|
||||
if err := requireStaff(r); err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
@@ -410,7 +432,7 @@ func (s *Server) coinToss(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (s *Server) createMapDraft(w http.ResponseWriter, r *http.Request) {
|
||||
if err := requireAdmin(r); err != nil {
|
||||
if err := requireStaff(r); err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
@@ -478,7 +500,7 @@ func (s *Server) mapBan(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (s *Server) createHeroDraft(w http.ResponseWriter, r *http.Request) {
|
||||
if err := requireAdmin(r); err != nil {
|
||||
if err := requireStaff(r); err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
@@ -532,7 +554,7 @@ func (s *Server) heroBan(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func (s *Server) authorizeTeam(r *http.Request, eventID, teamID string) error {
|
||||
id := who(r)
|
||||
if id.account.IsAdmin() {
|
||||
if id.account.IsStaff() {
|
||||
return nil
|
||||
}
|
||||
teams, err := s.store.ListTeams(r.Context(), eventID)
|
||||
@@ -548,7 +570,7 @@ func (s *Server) authorizeTeam(r *http.Request, eventID, teamID string) error {
|
||||
}
|
||||
|
||||
func (s *Server) createTournament(w http.ResponseWriter, r *http.Request) {
|
||||
if err := requireAdmin(r); err != nil {
|
||||
if err := requireStaff(r); err != nil {
|
||||
writeError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -79,6 +79,48 @@ func (s *Server) swapRoster(w http.ResponseWriter, r *http.Request) {
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) moveRosterPlayer(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
FromTeamID string `json:"fromTeamId"`
|
||||
PlayerID string `json:"playerId"`
|
||||
ToTeamID string `json:"toTeamId"`
|
||||
Role domain.Role `json:"role"`
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
out, err := s.service.MoveRosterPlayer(r.Context(), who(r).account, chi.URLParam(r, "eventID"), in.FromTeamID, in.PlayerID, in.ToTeamID, in.Role, in.ExpectedVersion)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) placeReservePlayer(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
TeamID string `json:"teamId"`
|
||||
ReservePlayerID string `json:"reservePlayerId"`
|
||||
Role domain.Role `json:"role"`
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
out, err := s.service.PlaceReservePlayer(r.Context(), who(r).account, chi.URLParam(r, "eventID"), in.TeamID, in.ReservePlayerID, in.Role, in.ExpectedVersion)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) removeRosterPlayer(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
TeamID string `json:"teamId"`
|
||||
PlayerID string `json:"playerId"`
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
out, err := s.service.RemoveRosterPlayer(r.Context(), who(r).account, chi.URLParam(r, "eventID"), in.TeamID, in.PlayerID, in.ExpectedVersion)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) substituteRoster(w http.ResponseWriter, r *http.Request) {
|
||||
s.handleSubstitute(w, r, false)
|
||||
}
|
||||
@@ -126,6 +168,17 @@ func (s *Server) confirmRosters(w http.ResponseWriter, r *http.Request) {
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) revertWorkflowStage(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
}
|
||||
if !decode(w, r, &in) {
|
||||
return
|
||||
}
|
||||
out, err := s.service.RevertWorkflowStage(r.Context(), who(r).account, chi.URLParam(r, "eventID"), in.ExpectedVersion)
|
||||
respond(w, out, err, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) startScrim(w http.ResponseWriter, r *http.Request) {
|
||||
var in struct {
|
||||
ExpectedVersion int `json:"expectedVersion"`
|
||||
|
||||
Reference in New Issue
Block a user