package admin import ( "io" "net/http" "os" "path/filepath" "strings" ) func (s *AdminServer) handleFiles(w http.ResponseWriter, r *http.Request) { relPath := r.URL.Query().Get("path") relPath = strings.TrimPrefix(relPath, "/") relPath = strings.TrimPrefix(relPath, "data/") switch r.Method { case http.MethodGet: if relPath == "" { entries, err := os.ReadDir(s.dataDir) if err != nil { writeJSON(w, map[string]any{"error": err.Error()}) return } var files []map[string]any for _, e := range entries { files = append(files, map[string]any{ "name": e.Name(), "dir": e.IsDir(), }) } writeJSON(w, files) return } fullPath := filepath.Join(s.dataDir, relPath) info, err := os.Stat(fullPath) if err != nil { writeJSON(w, map[string]any{"error": "not found: " + relPath}) return } if info.IsDir() { entries, err := os.ReadDir(fullPath) if err != nil { writeJSON(w, map[string]any{"error": err.Error()}) return } var files []map[string]any for _, e := range entries { files = append(files, map[string]any{ "name": e.Name(), "dir": e.IsDir(), }) } writeJSON(w, files) return } data, err := os.ReadFile(fullPath) if err != nil { writeJSON(w, map[string]any{"error": "read error: " + err.Error()}) return } writeJSON(w, map[string]any{ "name": info.Name(), "content": string(data), "path": relPath, }) case http.MethodPut, http.MethodPost: if relPath == "" { writeJSON(w, map[string]any{"error": "missing path"}) return } fullPath := filepath.Join(s.dataDir, relPath) body, err := io.ReadAll(r.Body) if err != nil { writeJSON(w, map[string]any{"error": "read body: " + err.Error()}) return } var oldContent []byte if existing, err := snapshotFile(fullPath); err == nil { oldContent = existing } dir := filepath.Dir(fullPath) if err := os.MkdirAll(dir, 0755); err != nil { writeJSON(w, map[string]any{"error": "mkdir: " + err.Error()}) return } if err := os.WriteFile(fullPath, body, 0644); err != nil { writeJSON(w, map[string]any{"error": "write error: " + err.Error()}) return } isCreate := oldContent == nil desc := "Updated file " + relPath if isCreate { desc = "Created file " + relPath } s.undoStack.Push(ChangeDesc{ Description: desc, FilePath: fullPath, OldContent: oldContent, NewContent: body, IsCreate: isCreate, }) writeJSON(w, map[string]any{"ok": true, "path": relPath}) case http.MethodDelete: if relPath == "" { writeJSON(w, map[string]any{"error": "missing path"}) return } fullPath := filepath.Join(s.dataDir, relPath) oldContent, _ := snapshotFile(fullPath) if err := os.Remove(fullPath); err != nil { writeJSON(w, map[string]any{"error": "delete error: " + err.Error()}) return } s.undoStack.Push(ChangeDesc{ Description: "Deleted file " + relPath, FilePath: fullPath, OldContent: oldContent, IsDelete: true, }) writeJSON(w, map[string]any{"ok": true, "path": relPath}) default: http.Error(w, "method not allowed", http.StatusMethodNotAllowed) } }