Console panel, collapsible panels, Add on the Members panel

- Server settings > Console sends a raw console command for staff with @css/rcon
  (POST /api/admin/rcon). It doesn't wait for the server; the output is only logged.
- Staff & ranks and Server settings panels fold up by clicking their heads. All but
  Ranks and Server start folded; a folded Ranks panel shows the selected rank.
- The Members panel has an Add button that opens Add staff with that rank picked.
- The public rank log shows a rank being given as Promoted and taken away as Demoted.
This commit is contained in:
Astra 2026-10-03 12:09:43 +01:00
parent 31901495de
commit b26360e8f3
8 changed files with 123 additions and 24 deletions

View file

@ -353,6 +353,43 @@ func (s *Server) restartRound(w http.ResponseWriter, r *http.Request, id *Identi
s.console(w, id, "@css/generic", "css_rr", "Restarting the match.")
}
// rcon runs a raw console command for staff with @css/rcon, the same as css_rcon in game. It doesn't
// wait for the server: the command is sent in the background and what it prints is only logged.
func (s *Server) rcon(w http.ResponseWriter, r *http.Request, id *Identity, x *staffIndex) {
if !id.Has("@css/rcon") {
fail(w, http.StatusForbidden, "Your rank can't do that.")
return
}
var req struct {
Command string `json:"command"`
}
if err := readJSON(r, &req); err != nil {
fail(w, http.StatusBadRequest, err.Error())
return
}
// Newlines would send several commands; ';' still does, as it does in the console.
cmd := strings.TrimSpace(strings.NewReplacer("\r", " ", "\n", " ").Replace(req.Command))
if cmd == "" {
fail(w, http.StatusBadRequest, "Enter a command first.")
return
}
if len(cmd) > 512 {
fail(w, http.StatusBadRequest, "That command is too long.")
return
}
slog.Info("rcon", "admin", id.SteamID, "cmd", cmd)
go func() {
out, err := s.rc.Exec(cmd)
if err != nil {
slog.Warn("rcon failed", "admin", id.SteamID, "cmd", cmd, "err", err)
return
}
slog.Info("rcon output", "admin", id.SteamID, "out", strings.TrimSpace(out))
s.poll.Refresh()
}()
done(w, "Sent.")
}
func (s *Server) reloadAdmins(w http.ResponseWriter, r *http.Request, id *Identity, x *staffIndex) {
s.staff.invalidate()
s.console(w, id, "@css/root", "css_reloadadmins", "Reloaded admins.")