feat(go-services): tracker-go — complete the Phase 1 read API
Adds the rest of the read-side endpoints to the Go tracker, all parity-checked
against the live Python service:
- DB reads: /stats/{c}, /portals, /spawns/heatmap, /server-health,
/character-stats/{c} (stats_data JSONB merged to top level),
/combat-stats[/{c}], /inventories, /inventory/{c}/search.
- 5-minute totals cache + /total-rares, /total-kills.
- Ingest-only state returned as Python's empty/default shapes (/quest-status,
/vital-sharing/peers, /equipment-cantrip-state/{c}); /issues (flat file),
/me (401 until cookie verification lands).
- Streaming reverse proxy to inventory-service (/inventory/{c},
/inventory-characters, /search/*, /sets/list, /inv/{path...} incl. the SSE
suitbuilder stream).
- compare/compare_endpoints.py: structural parity for all read endpoints +
exact-match check for /character-stats and /combat-stats on OFFLINE chars
(online chars legitimately differ — Python serves a richer live overlay that
Phase-1 Go lacks until ingest).
Verified live: 14/14 endpoints structural-match, 8/8 rich offline chars
exact-match on /character-stats.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
1af47520c0
commit
c4e8190656
9 changed files with 908 additions and 10 deletions
70
go-services/tracker-go/memstate.go
Normal file
70
go-services/tracker-go/memstate.go
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// These endpoints are backed by ingest-only in-memory state in the Python
|
||||
// service (populated from /ws/position events). Phase 1 has no ingest, so they
|
||||
// return the same empty/default shapes the Python service emits when no data is
|
||||
// present — preserving the API contract for the frontend.
|
||||
|
||||
// GET /quest-status (main.py:1940)
|
||||
func (s *Server) handleQuestStatus(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"quest_data": map[string]any{},
|
||||
"tracked_quests": []string{
|
||||
"Stipend Collection Timer",
|
||||
"Blank Augmentation Gem Pickup Timer",
|
||||
"Insatiable Eater Jaw",
|
||||
},
|
||||
"player_count": 0,
|
||||
})
|
||||
}
|
||||
|
||||
// GET /vital-sharing/peers (main.py:1800)
|
||||
func (s *Server) handleVitalSharingPeers(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"peers": []any{},
|
||||
"subscriber_count": 0,
|
||||
})
|
||||
}
|
||||
|
||||
// GET /equipment-cantrip-state/{name} (main.py:4167)
|
||||
func (s *Server) handleEquipmentCantrip(w http.ResponseWriter, r *http.Request) {
|
||||
name := r.PathValue("name")
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"type": "equipment_cantrip_state",
|
||||
"character_name": name,
|
||||
"items": []any{},
|
||||
})
|
||||
}
|
||||
|
||||
// GET /issues — flat-file issue board. (main.py:1709)
|
||||
func (s *Server) handleIssues(w http.ResponseWriter, r *http.Request) {
|
||||
issues := s.loadIssues()
|
||||
writeJSON(w, http.StatusOK, map[string]any{"issues": issues})
|
||||
}
|
||||
|
||||
func (s *Server) loadIssues() []any {
|
||||
empty := []any{}
|
||||
b, err := os.ReadFile(filepath.Join(s.staticDir, "openissues.json"))
|
||||
if err != nil {
|
||||
return empty
|
||||
}
|
||||
var v []any
|
||||
if json.Unmarshal(b, &v) != nil {
|
||||
return empty
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// GET /me — current user. Phase 1 has no session-cookie verification yet, so
|
||||
// (like the Python service for an unauthenticated request) this is 401. The
|
||||
// loopback internal-trust path carries no user identity. (main.py:1455)
|
||||
func (s *Server) handleMe(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusUnauthorized, map[string]any{"detail": "Not authenticated"})
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue