aboutsummaryrefslogtreecommitdiff
path: root/internal/admin
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-10-06 12:57:23 -0400
committerhistoria <[not public]>2026-10-06 12:57:23 -0400
commit9666676766bf21364f4fc4d0d1d336e72f2a91b4 (patch)
tree0cade34091d7602790dc38cd44053f0b74bbadf2 /internal/admin
parent4bc1daaf259e0c19c5fe4a12ec8bc7f6e5a735c1 (diff)
downloadthehouseoficarus-9666676766bf21364f4fc4d0d1d336e72f2a91b4.tar.gz
refactor: split casino.go, dedup helpers, remove audit docs
Diffstat (limited to 'internal/admin')
-rw-r--r--internal/admin/api_entity.go9
-rw-r--r--internal/admin/api_rooms.go46
-rw-r--r--internal/admin/api_triggers.go19
-rw-r--r--internal/admin/server.go2
-rw-r--r--internal/admin/undo.go12
-rw-r--r--internal/admin/yaml_util.go10
6 files changed, 46 insertions, 52 deletions
diff --git a/internal/admin/api_entity.go b/internal/admin/api_entity.go
index 04b9dca..1049b96 100644
--- a/internal/admin/api_entity.go
+++ b/internal/admin/api_entity.go
@@ -322,7 +322,14 @@ func (s *AdminServer) entityDelete(w http.ResponseWriter, id string, c entityCRU
c.fail(w, http.StatusNotFound, c.notFound(id))
return
}
- oldContent := backupFile(path)
+ oldContent, exists, snapErr := snapshotBefore(path)
+ if snapErr != nil {
+ c.fail(w, http.StatusInternalServerError, c.removeErr(snapErr))
+ return
+ }
+ if !exists {
+ oldContent = nil
+ }
if err := os.Remove(path); err != nil {
c.fail(w, http.StatusInternalServerError, c.removeErr(err))
return
diff --git a/internal/admin/api_rooms.go b/internal/admin/api_rooms.go
index 06e736c..72f4a2c 100644
--- a/internal/admin/api_rooms.go
+++ b/internal/admin/api_rooms.go
@@ -349,26 +349,9 @@ func (s *AdminServer) handleRoomByID(w http.ResponseWriter, r *http.Request) {
return
}
- courseID, _ := s.detachRoomFromCourse(id)
-
- if err := os.Remove(path); err != nil {
- failJSON(w, fmt.Sprintf("delete room: %v", err))
- return
- }
-
- s.world.RebuildRoomIndex(s.dataDir)
- s.world.ClearRoomState(id)
- // Drop the room's live mob instances too, or they keep wandering and
- // are returned by MobsInRoom for a room that no longer exists.
- if s.mobStore != nil {
- s.mobStore.RemoveMobsInRoom(id)
- }
- // Drop any cached in-room games (tables/machines) and refund their
- // outstanding wagers — same eviction semantics as bulk delete.
- if s.evictRoom != nil {
- s.evictRoom(id)
- }
-
+ // Clean up references and exits in every OTHER room BEFORE deleting this
+ // one: a failed write then aborts with the world untouched, instead of
+ // leaving dangling exits while reporting success.
allIDs, _ := listRoomIDs(s.dataDir)
var extraFiles []ExtraFile
var exitCleaned int
@@ -395,7 +378,8 @@ func (s *AdminServer) handleRoomByID(w http.ResponseWriter, r *http.Request) {
oldExtra, _ := snapshotFile(otherPath)
newContent, writeErr := writeYAMLFile(otherPath, otherRoom)
if writeErr != nil {
- continue
+ failJSON(w, fmt.Sprintf("clean exits in room %d: %v", otherID, writeErr))
+ return
}
extraFiles = append(extraFiles, ExtraFile{
FilePath: otherPath,
@@ -407,6 +391,26 @@ func (s *AdminServer) handleRoomByID(w http.ResponseWriter, r *http.Request) {
}
}
+ courseID, _ := s.detachRoomFromCourse(id)
+
+ if err := os.Remove(path); err != nil {
+ failJSON(w, fmt.Sprintf("delete room: %v", err))
+ return
+ }
+
+ s.world.RebuildRoomIndex(s.dataDir)
+ s.world.ClearRoomState(id)
+ // Drop the room's live mob instances too, or they keep wandering and
+ // are returned by MobsInRoom for a room that no longer exists.
+ if s.mobStore != nil {
+ s.mobStore.RemoveMobsInRoom(id)
+ }
+ // Drop any cached in-room games (tables/machines) and refund their
+ // outstanding wagers — same eviction semantics as bulk delete.
+ if s.evictRoom != nil {
+ s.evictRoom(id)
+ }
+
s.undoStack.Push(ChangeDesc{
Description: fmt.Sprintf("delete room %d (cleaned %d exits)", id, exitCleaned),
FilePath: path,
diff --git a/internal/admin/api_triggers.go b/internal/admin/api_triggers.go
index ff3e7d6..b392317 100644
--- a/internal/admin/api_triggers.go
+++ b/internal/admin/api_triggers.go
@@ -120,6 +120,7 @@ func (s *AdminServer) handleTriggerByID(w http.ResponseWriter, r *http.Request)
delete(body, "id")
delete(body, "_path")
delete(body, "_raw")
+ delete(body, "_kind")
oldPath, oldDir, findErr := findTriggerFile(s.dataDir, id)
newKind := triggerKind(body)
@@ -131,10 +132,10 @@ func (s *AdminServer) handleTriggerByID(w http.ResponseWriter, r *http.Request)
oldContent = existing
}
- // If the kind changed, ensure the target directory exists and delete
- // the old path after writing the new one.
- if findErr == nil && newSubdir != oldDir {
- os.MkdirAll(filepath.Dir(newPath), 0755)
+ // Ensure the target subdirectory exists even for PUT-as-create.
+ if err := os.MkdirAll(filepath.Dir(newPath), 0755); err != nil {
+ failJSON(w, "mkdir error: "+err.Error())
+ return
}
triggerData, err := triggerMapToYAML(body)
@@ -170,7 +171,6 @@ func (s *AdminServer) handleTriggerByID(w http.ResponseWriter, r *http.Request)
desc.NewFilePath = newPath
}
s.undoStack.Push(desc)
- s.rebuildTriggerIndex()
writeJSON(w, map[string]any{"ok": true, "id": id, "kind": newKind})
case http.MethodDelete:
@@ -196,7 +196,6 @@ func (s *AdminServer) handleTriggerByID(w http.ResponseWriter, r *http.Request)
// new-content image over the file.
IsDelete: true,
})
- s.rebuildTriggerIndex()
writeJSON(w, map[string]any{"ok": true})
default:
@@ -285,6 +284,12 @@ func triggerMapToYAML(m map[string]any) ([]byte, error) {
}
// Handle simple fields.
+ if v, ok := m["item_id"]; ok {
+ if s, ok := v.(string); ok {
+ t.ItemID = s
+ }
+ delete(m, "item_id")
+ }
if v, ok := m["on_player_flag"]; ok {
if s, ok := v.(string); ok {
t.OnPlayerFlag = s
@@ -311,5 +316,3 @@ func triggerMapToYAML(m map[string]any) ([]byte, error) {
}
return yamlBytes, nil
}
-
-func (s *AdminServer) rebuildTriggerIndex() {}
diff --git a/internal/admin/server.go b/internal/admin/server.go
index 9819924..a68eeda 100644
--- a/internal/admin/server.go
+++ b/internal/admin/server.go
@@ -946,6 +946,7 @@ func (s *AdminServer) handleEntityDirs(entityType string) http.HandlerFunc {
for _, ch := range changes {
s.undoStack.Push(ch)
}
+ s.invalidateEntityCaches(entityType)
writeJSON(w, map[string]any{"ok": true, "moved": len(changes) - 1})
case "rename_dir":
if err := renameEntityDir(s.dataDir, entityType, body.Dir, body.NewName); err != nil {
@@ -959,6 +960,7 @@ func (s *AdminServer) handleEntityDirs(entityType string) http.HandlerFunc {
newPrefix := filepath.Join(s.dataDir, entityType, body.NewName)
s.undoStack.RewritePaths(oldPrefix, newPrefix)
}
+ s.invalidateEntityCaches(entityType)
writeJSON(w, map[string]any{"ok": true, "name": body.NewName})
default:
failJSON(w, "unknown action")
diff --git a/internal/admin/undo.go b/internal/admin/undo.go
index d9ef263..d352100 100644
--- a/internal/admin/undo.go
+++ b/internal/admin/undo.go
@@ -835,18 +835,6 @@ func (us *UndoStack) load() {
us.redo = toChanges(sd.Redo)
}
-func (us *UndoStack) Clear() {
- us.mu.Lock()
- defer us.mu.Unlock()
- us.history = nil
- us.redo = nil
- us.lastErr = nil
- os.Remove(us.filePath)
- log.Printf("undo: cleared history")
-}
-
-// LastError returns the error from the most recent failed Undo/Redo, or nil.
-// A nil change plus a nil LastError means the history/redo stack was empty.
func (us *UndoStack) LastError() error {
us.mu.Lock()
defer us.mu.Unlock()
diff --git a/internal/admin/yaml_util.go b/internal/admin/yaml_util.go
index 2ab7ec4..050a3f6 100644
--- a/internal/admin/yaml_util.go
+++ b/internal/admin/yaml_util.go
@@ -2,7 +2,6 @@ package admin
import (
"fmt"
- "log"
"os"
"path/filepath"
"strings"
@@ -133,15 +132,6 @@ func listYAMLTree(dataDir, subdir string) (*YAMLTree, error) {
return tree, nil
}
-func backupFile(path string) []byte {
- data, err := os.ReadFile(path)
- if err != nil {
- log.Printf("backup: WARNING failed to read %s: %v (undo will not be able to restore this file)", path, err)
- return nil
- }
- return data
-}
-
func snapshotFile(path string) ([]byte, error) {
return os.ReadFile(path)
}