package admin import ( "net/http" "os" "path/filepath" "strings" "gopkg.in/yaml.v3" "thehouseoficarus/internal/player" ) func (s *AdminServer) handlePlayers(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } charsDir := filepath.Join(s.dataDir, "players", "characters") entries, err := os.ReadDir(charsDir) if err != nil { writeJSON(w, map[string]any{"error": err.Error()}) return } charToAccount := s.buildCharToAccountMap() type playerSummary struct { Name string `json:"name"` Level int `json:"level"` TotalLevel int `json:"total_level"` Room int `json:"room"` Account string `json:"account"` } var players []playerSummary for _, e := range entries { if e.IsDir() || filepath.Ext(e.Name()) != ".yaml" { continue } rawName := strings.TrimSuffix(e.Name(), ".yaml") p, err := s.accountStore.LoadCharacter(rawName) if err != nil { continue } players = append(players, playerSummary{ Name: rawName, Level: p.CombatLevel(), TotalLevel: p.TotalLevel(), Room: p.RoomID, Account: charToAccount[rawName], }) } writeJSON(w, players) } func (s *AdminServer) buildCharToAccountMap() map[string]string { m := make(map[string]string) names, err := s.accountStore.ListAccountNames() if err != nil { return m } for _, name := range names { acc, err := s.accountStore.LoadAccount(name) if err != nil { continue } for _, charName := range acc.Characters { m[charName] = acc.Name } } return m } func (s *AdminServer) handlePlayerByID(w http.ResponseWriter, r *http.Request) { path := strings.TrimPrefix(r.URL.Path, "/api/players/") if path == "" { http.Error(w, `{"error":"missing player id"}`, http.StatusBadRequest) return } if r.Method != http.MethodGet { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } if strings.HasPrefix(path, "account/") { s.getAccount(w, r, strings.TrimPrefix(path, "account/")) return } s.getCharacter(w, r, path) } func (s *AdminServer) getCharacter(w http.ResponseWriter, r *http.Request, name string) { canonical, err := s.accountStore.FindCharacter(name) if err != nil { writeJSONError(w, "character not found: "+name, http.StatusNotFound) return } charPath := s.accountStore.CharPath(canonical) data, err := os.ReadFile(charPath) if err != nil { writeJSONError(w, "read error: "+err.Error(), http.StatusInternalServerError) return } m, err := yamlToMap(data) if err != nil { writeJSONError(w, "parse error: "+err.Error(), http.StatusInternalServerError) return } var p player.Player if err := yaml.Unmarshal(data, &p); err == nil { m["combat_level"] = p.CombatLevel() m["total_level"] = p.TotalLevel() m["max_hp"] = p.MaxHP() skillLevels := make(map[string]int) for _, skill := range player.AllSkills { skillLevels[string(skill)] = p.Level(skill) } m["skill_levels"] = skillLevels charToAccount := s.buildCharToAccountMap() if acc, ok := charToAccount[canonical]; ok { m["account"] = acc } } m["_path"] = charPath m["_raw"] = string(data) writeJSON(w, m) } func (s *AdminServer) getAccount(w http.ResponseWriter, r *http.Request, name string) { canonical, err := s.accountStore.FindAccount(name) if err != nil { writeJSONError(w, "account not found: "+name, http.StatusNotFound) return } accPath := s.accountStore.AccountPath(canonical) data, err := os.ReadFile(accPath) if err != nil { writeJSONError(w, "read error: "+err.Error(), http.StatusInternalServerError) return } m, err := yamlToMap(data) if err != nil { writeJSONError(w, "parse error: "+err.Error(), http.StatusInternalServerError) return } delete(m, "password_hash") m["_path"] = accPath m["_raw"] = string(data) writeJSON(w, m) }