aboutsummaryrefslogtreecommitdiff
path: root/internal/admin/api_rooms.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/admin/api_rooms.go')
-rw-r--r--internal/admin/api_rooms.go108
1 files changed, 108 insertions, 0 deletions
diff --git a/internal/admin/api_rooms.go b/internal/admin/api_rooms.go
index cf23761..d1193f0 100644
--- a/internal/admin/api_rooms.go
+++ b/internal/admin/api_rooms.go
@@ -11,6 +11,7 @@ import (
"strconv"
"strings"
+ "thehouseoficarus/internal/behavior"
"thehouseoficarus/internal/world"
)
@@ -736,3 +737,110 @@ func (s *AdminServer) handleRoomRename(w http.ResponseWriter, r *http.Request) {
})
writeJSON(w, map[string]any{"ok": true})
}
+
+func (s *AdminServer) handleDuplicateRooms(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ writeJSON(w, map[string]any{"error": "method not allowed"})
+ return
+ }
+ dups := behavior.CheckDuplicateIDs(filepath.Join(s.dataDir, "rooms"))
+ type DupEntry struct {
+ ID string `json:"id"`
+ Paths []string `json:"paths"`
+ }
+ result := make([]DupEntry, len(dups))
+ for i, d := range dups {
+ result[i] = DupEntry{ID: d.ID, Paths: d.Paths}
+ }
+ writeJSON(w, map[string]any{"duplicates": result})
+}
+
+func (s *AdminServer) handleResolveDuplicate(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ writeJSON(w, map[string]any{"error": "method not allowed"})
+ return
+ }
+ var body struct {
+ Path string `json:"path"`
+ Action string `json:"action"`
+ NewID int `json:"new_id"`
+ }
+ if err := readJSON(r, &body); err != nil || body.Path == "" || body.Action == "" {
+ writeJSON(w, map[string]any{"error": "invalid body"})
+ return
+ }
+
+ roomsDir := filepath.Join(s.dataDir, "rooms")
+ absRooms, _ := filepath.Abs(roomsDir)
+ absPath, err := filepath.Abs(body.Path)
+ if err != nil || !strings.HasPrefix(absPath, absRooms+string(filepath.Separator)) {
+ writeJSON(w, map[string]any{"error": "invalid path"})
+ return
+ }
+
+ switch body.Action {
+ case "delete":
+ old, _ := snapshotFile(absPath)
+ if err := os.Remove(absPath); err != nil {
+ writeJSON(w, map[string]any{"error": "delete failed: " + err.Error()})
+ return
+ }
+ s.world.RebuildRoomIndex(s.dataDir)
+ s.undoStack.Push(ChangeDesc{
+ Description: fmt.Sprintf("delete duplicate room file %s", body.Path),
+ FilePath: absPath,
+ OldContent: old,
+ IsDelete: true,
+ })
+ writeJSON(w, map[string]any{"ok": true})
+
+ case "rename":
+ if body.NewID <= 0 {
+ writeJSON(w, map[string]any{"error": "new_id must be positive"})
+ return
+ }
+ if _, ok := s.world.GetRoomPath(body.NewID); ok {
+ writeJSON(w, map[string]any{"error": "room ID already exists"})
+ return
+ }
+ newRel := filepath.Join(filepath.Dir(absPath), strconv.Itoa(body.NewID)+".yaml")
+ if _, err := os.Stat(newRel); err == nil {
+ writeJSON(w, map[string]any{"error": "target file already exists"})
+ return
+ }
+
+ oldContent, err := os.ReadFile(absPath)
+ if err != nil {
+ writeJSON(w, map[string]any{"error": "read failed: " + err.Error()})
+ return
+ }
+ m, err := yamlToMap(oldContent)
+ if err != nil {
+ writeJSON(w, map[string]any{"error": "yaml parse failed: " + err.Error()})
+ return
+ }
+ m["id"] = body.NewID
+
+ newContent, err := writeMapAsYAML(newRel, m)
+ if err != nil {
+ writeJSON(w, map[string]any{"error": "write failed: " + err.Error()})
+ return
+ }
+ if err := os.Remove(absPath); err != nil {
+ writeJSON(w, map[string]any{"error": "remove old failed: " + err.Error()})
+ return
+ }
+ s.world.RebuildRoomIndex(s.dataDir)
+ s.undoStack.Push(ChangeDesc{
+ Description: fmt.Sprintf("rename duplicate room %s to ID %d", body.Path, body.NewID),
+ FilePath: absPath,
+ NewFilePath: newRel,
+ OldContent: oldContent,
+ NewContent: newContent,
+ })
+ writeJSON(w, map[string]any{"ok": true})
+
+ default:
+ writeJSON(w, map[string]any{"error": "unknown action: " + body.Action})
+ }
+}