diff options
Diffstat (limited to 'internal/admin/api_rooms.go')
| -rw-r--r-- | internal/admin/api_rooms.go | 108 |
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}) + } +} |
