package admin import ( "net/http" "os" "path/filepath" "strings" ) func (s *AdminServer) findTechFile(id string) ([]byte, string, error) { data, path, err := readYAMLFile(s.dataDir, "techs", id) if err == nil { return data, path, nil } return findYAMLFileInSubdirs(s.dataDir, "techs", id) } func (s *AdminServer) handleTechs(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: tree, err := listYAMLTree(s.dataDir, "techs") if err != nil { writeJSON(w, map[string]any{"error": err.Error()}) return } if tree == nil { tree = &YAMLTree{Root: []string{}, Dirs: map[string][]string{}} } writeJSON(w, tree) case http.MethodPost: var m map[string]any if err := readJSON(r, &m); err != nil { writeJSON(w, map[string]any{"error": "invalid json"}) return } id, ok := m["id"].(string) if !ok || strings.TrimSpace(id) == "" { writeJSON(w, map[string]any{"error": "missing id"}) return } delete(m, "id") if _, _, err := s.findTechFile(id); err == nil { writeJSON(w, map[string]any{"error": "tech already exists"}) return } path := filepath.Join(s.dataDir, "techs", id+".yaml") newContent, err := writeMapAsYAML(path, m) if err != nil { writeJSON(w, map[string]any{"error": "write error: " + err.Error()}) return } s.undoStack.Push(ChangeDesc{ Description: "Created tech " + id, FilePath: path, NewContent: newContent, IsCreate: true, }) writeJSON(w, map[string]any{"ok": true, "id": id}) default: http.Error(w, "method not allowed", http.StatusMethodNotAllowed) } } func (s *AdminServer) handleTechByID(w http.ResponseWriter, r *http.Request) { id := strings.TrimPrefix(r.URL.Path, "/api/techs/") if id == "" { http.Error(w, `{"error":"missing id"}`, http.StatusBadRequest) return } switch r.Method { case http.MethodGet: data, path, err := s.findTechFile(id) if err != nil { writeJSON(w, map[string]any{"error": "not found: " + id}) return } raw, err := yamlToMap(data) if err != nil { writeJSON(w, map[string]any{"error": "parse error: " + err.Error()}) return } raw["id"] = id raw["_path"] = path raw["_raw"] = string(data) writeJSON(w, raw) case http.MethodPost, http.MethodPut: var body map[string]any if err := readJSON(r, &body); err != nil { writeJSON(w, map[string]any{"error": "invalid JSON: " + err.Error()}) return } delete(body, "id") delete(body, "_path") delete(body, "_raw") _, path, findErr := s.findTechFile(id) isCreate := findErr != nil if isCreate { path = filepath.Join(s.dataDir, "techs", id+".yaml") } var oldContent []byte if !isCreate { if existing, err := snapshotFile(path); err == nil { oldContent = existing } } newContent, err := writeMapAsYAML(path, body) if err != nil { writeJSON(w, map[string]any{"error": "write error: " + err.Error()}) return } desc := "Updated tech " + id if isCreate { desc = "Created tech " + id } s.undoStack.Push(ChangeDesc{ Description: desc, FilePath: path, OldContent: oldContent, NewContent: newContent, IsCreate: isCreate, }) writeJSON(w, map[string]any{"ok": true, "id": id}) case http.MethodDelete: _, path, err := s.findTechFile(id) if err != nil { writeJSON(w, map[string]any{"error": "not found: " + id}) return } oldContent, _ := snapshotFile(path) if err := os.Remove(path); err != nil { writeJSON(w, map[string]any{"error": "delete error: " + err.Error()}) return } s.undoStack.Push(ChangeDesc{ Description: "Deleted tech " + id, FilePath: path, OldContent: oldContent, IsDelete: true, }) writeJSON(w, map[string]any{"ok": true, "id": id}) default: http.Error(w, "method not allowed", http.StatusMethodNotAllowed) } }