fix(allowlist): 唤醒名单可查看、可取消或恢复唤醒权限,换主人时清空
This commit is contained in:
23 files changed
+1190
-32
No files matched your search
@@ -0,0 +1,96 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"felis.lolicon.best/internal/naming"
|
||||
)
|
||||
|
||||
// The wake allowlist (server_allowlist) decides who may wake a sleeping server
|
||||
// whose autostartPolicy is allowlist. A player lands on it by joining the server
|
||||
// once (RecordJoin); these two routes let the owner, or an admin, see who is on it
|
||||
// and take a player's wake right away or give it back. It is Felis's own record in
|
||||
// Postgres, so it answers whether the server is running or not, unlike the
|
||||
// Minecraft whitelist under /access, which is the game server's and needs RCON.
|
||||
|
||||
// allowlistWakeRequest is the PUT /servers/{name}/allowlist/{uuid} body.
|
||||
type allowlistWakeRequest struct {
|
||||
CanWake *bool `json:"can_wake"`
|
||||
}
|
||||
|
||||
// allowlistServer resolves {name} for the allowlist routes and applies their
|
||||
// gate: 400 for a malformed name, 404 for a server that does not exist, 403 for a
|
||||
// caller who neither owns it nor is an admin.
|
||||
func (a *API) allowlistServer(w http.ResponseWriter, r *http.Request) (string, bool) {
|
||||
name := r.PathValue("name")
|
||||
if err := naming.ValidateServerName(name); err != nil {
|
||||
writeError(w, r, newError(http.StatusBadRequest, "bad_name", "invalid server name: %v", err))
|
||||
return "", false
|
||||
}
|
||||
rec, err := a.Repo.ServerByName(r.Context(), name)
|
||||
if err != nil {
|
||||
a.writeLookupError(w, r, err)
|
||||
return "", false
|
||||
}
|
||||
if !a.isOwnerOrAdmin(principalFromContext(r.Context()), rec) {
|
||||
writeError(w, r, errForbidden)
|
||||
return "", false
|
||||
}
|
||||
return name, true
|
||||
}
|
||||
|
||||
// handleAllowlistList returns the server's wake allowlist, newest first,
|
||||
// including the players whose wake right was taken away.
|
||||
func (a *API) handleAllowlistList(w http.ResponseWriter, r *http.Request) {
|
||||
name, ok := a.allowlistServer(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
entries, err := a.Repo.ServerAllowlist(r.Context(), name)
|
||||
if err != nil {
|
||||
writeError(w, r, err)
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]any{"server": name, "entries": entries})
|
||||
}
|
||||
|
||||
// handleAllowlistSetWake takes a player's wake right away (can_wake false) or
|
||||
// gives it back (true). The entry stays on the list either way, so a revoked
|
||||
// player's next join does not quietly undo the owner's choice.
|
||||
func (a *API) handleAllowlistSetWake(w http.ResponseWriter, r *http.Request) {
|
||||
name, ok := a.allowlistServer(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
id, err := uuid.Parse(r.PathValue("uuid"))
|
||||
if err != nil {
|
||||
writeError(w, r, newError(http.StatusBadRequest, "bad_request", "invalid Minecraft UUID"))
|
||||
return
|
||||
}
|
||||
var req allowlistWakeRequest
|
||||
if err := decodeJSON(w, r, &req); err != nil {
|
||||
writeError(w, r, err)
|
||||
return
|
||||
}
|
||||
if req.CanWake == nil {
|
||||
writeError(w, r, newError(http.StatusBadRequest, "bad_request", "can_wake is required"))
|
||||
return
|
||||
}
|
||||
if err := a.Repo.SetAllowlistWake(r.Context(), name, id.String(), *req.CanWake); err != nil {
|
||||
a.writeLookupError(w, r, err)
|
||||
return
|
||||
}
|
||||
p := principalFromContext(r.Context())
|
||||
e := AuditEntry{Actor: auditActor(p), Action: "allowlist.revoke", ServerName: name}
|
||||
if *req.CanWake {
|
||||
e.Action = "allowlist.restore"
|
||||
}
|
||||
if p != nil {
|
||||
e.ActorUserID = p.UserID
|
||||
}
|
||||
e.Payload = auditPayload(map[string]any{"mc_uuid": id.String()})
|
||||
a.auditEntry(r, e)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
Reference in new issue
Block a user