Add recent API request recorder with admin tab and /api/recent endpoint
build / build (push) Successful in 2m31s

Record the last 10 /v1/ API requests (including errors) in an in-memory
ring buffer. Each entry captures request headers (Authorization masked),
request body, response status, and response body — all up to 1 MB.

- recorder.go: RecentRecorder ring buffer, recordingResponseWriter,
  withRecording middleware, getRecent handler
- server.go: add recorder to Server, wrap /v1/ routes, add /api/recent
- admin.html: new 最近请求 tab with lazy-load and expandable cards
- recorder_test.go: 5 tests (ring buffer, ordering, capture, errors, API)
This commit is contained in:
2026-08-23 14:52:33 +08:00
parent f988c47fec
commit 22d78c4576
4 changed files with 411 additions and 4 deletions
+6 -4
View File
@@ -33,10 +33,11 @@ type Server struct {
loginSession string
st *store.Store
statsEnabled bool
recorder *RecentRecorder
}
func New(cfg config.Config, st *store.Store) http.Handler {
s := &Server{cfg: cfg, mux: http.NewServeMux(), st: st, statsEnabled: !cfg.StatsDisabled}
s := &Server{cfg: cfg, mux: http.NewServeMux(), st: st, statsEnabled: !cfg.StatsDisabled, recorder: NewRecentRecorder()}
if cfg.LoginPassword != "" {
s.loginSession = randomSessionToken()
}
@@ -62,11 +63,12 @@ func (s *Server) routes() {
s.mux.HandleFunc("POST /api/sso/exchange", s.withLoginSession(s.exchangeSSOCode))
s.mux.HandleFunc("GET /api/stats", s.withLoginSession(s.getStats))
s.mux.HandleFunc("POST /api/stats/reset", s.withLoginSession(s.resetStats))
s.mux.HandleFunc("GET /api/recent", s.withLoginSession(s.getRecent))
s.mux.HandleFunc("GET /api/models", s.withLoginSession(s.getModels))
s.mux.HandleFunc("POST /api/models/test", s.withLoginSession(s.testModel))
s.mux.HandleFunc("GET /v1/models", s.withAPIKey(s.models))
s.mux.HandleFunc("POST /v1/chat/completions", s.withAPIKey(s.chatCompletions))
s.mux.HandleFunc("POST /v1/responses", s.withAPIKey(s.responses))
s.mux.HandleFunc("GET /v1/models", s.withRecording(s.withAPIKey(s.models)))
s.mux.HandleFunc("POST /v1/chat/completions", s.withRecording(s.withAPIKey(s.chatCompletions)))
s.mux.HandleFunc("POST /v1/responses", s.withRecording(s.withAPIKey(s.responses)))
}
func (s *Server) healthz(w http.ResponseWriter, r *http.Request) {