package admin import ( "encoding/json" "fmt" "io" "net/http" "os" "thehouseoficarus/internal/world" ) type bulkDeleteRequest struct { IDs []int `json:"ids"` } func (s *AdminServer) handleBulkDelete(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } body, readErr := io.ReadAll(r.Body) r.Body.Close() if readErr != nil { writeJSON(w, map[string]any{"error": fmt.Sprintf("read body: %v", readErr)}) return } var req bulkDeleteRequest if err := json.Unmarshal(body, &req); err != nil { writeJSON(w, map[string]any{"error": fmt.Sprintf("invalid json: %v", err)}) return } if len(req.IDs) == 0 { writeJSON(w, map[string]any{"error": "no room ids"}) return } type roomSnapshot struct { id int path string oldContent []byte } snapshots := make([]roomSnapshot, 0, len(req.IDs)) for _, id := range req.IDs { path, ok := s.world.GetRoomPath(id) if !ok { writeJSON(w, map[string]any{"error": fmt.Sprintf("room %d not found", id)}) return } oldContent, snapErr := snapshotFile(path) if snapErr != nil { writeJSON(w, map[string]any{"error": fmt.Sprintf("snapshot room %d: %v", id, snapErr)}) return } snapshots = append(snapshots, roomSnapshot{id: id, path: path, oldContent: oldContent}) } deletedSet := make(map[int]bool, len(req.IDs)) for _, snap := range snapshots { s.detachRoomFromCourse(snap.id) if err := os.Remove(snap.path); err != nil { writeJSON(w, map[string]any{"error": fmt.Sprintf("delete room %d: %v", snap.id, err)}) return } deletedSet[snap.id] = true } s.world.RebuildRoomIndex(s.dataDir) for _, snap := range snapshots { s.world.ClearRoomState(snap.id) } allIDs, _ := listRoomIDs(s.dataDir) var extraFiles []ExtraFile var exitCleaned int for _, otherID := range allIDs { if deletedSet[otherID] { continue } otherRoom, loadErr := s.world.LoadRoom(otherID) if loadErr != nil { continue } changed := false for _, dir := range world.ExitOrder { exit, exists := otherRoom.Exits[dir] if !exists || !deletedSet[exit.Room] { continue } delete(otherRoom.Exits, dir) changed = true } if changed { otherPath, pathOk := s.world.GetRoomPath(otherID) if pathOk { oldExtra, _ := snapshotFile(otherPath) newContent, writeErr := writeYAMLFile(otherPath, otherRoom) if writeErr != nil { continue } extraFiles = append(extraFiles, ExtraFile{ FilePath: otherPath, OldContent: oldExtra, NewContent: newContent, }) exitCleaned++ } } } for i := 1; i < len(snapshots); i++ { extraFiles = append(extraFiles, ExtraFile{ FilePath: snapshots[i].path, OldContent: snapshots[i].oldContent, IsDelete: true, }) } s.undoStack.Push(ChangeDesc{ Description: fmt.Sprintf("delete %d rooms (cleaned %d exits)", len(req.IDs), exitCleaned), FilePath: snapshots[0].path, OldContent: snapshots[0].oldContent, IsDelete: true, ExtraFiles: extraFiles, }) writeJSON(w, map[string]any{"ok": true, "count": len(req.IDs)}) }