Add API Server (Go/PocketBase), Web App (Go BFF + Vue), Fly App (Flutter/DJI MSDK), Adobe Plugin, and Docker/Docker AIO deployment configs. Design assets and build artifacts are gitignored. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
66 lines
2.0 KiB
Go
66 lines
2.0 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// svcHealth is the health of one upstream service, as shown on the panel.
|
|
type svcHealth struct {
|
|
Status string `json:"status"` // "ok" | "down"
|
|
LatencyMs int64 `json:"latencyMs,omitempty"`
|
|
HTTPStatus int `json:"httpStatus,omitempty"`
|
|
URL string `json:"url,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
// healthClient is a short-timeout client for probing upstreams so a hung
|
|
// dependency can't stall the status endpoint.
|
|
var healthClient = &http.Client{Timeout: 4 * time.Second}
|
|
|
|
// probe does a GET against url and classifies the result.
|
|
func probe(ctx context.Context, url string) svcHealth {
|
|
start := time.Now()
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
if err != nil {
|
|
return svcHealth{Status: "down", URL: url, Error: err.Error()}
|
|
}
|
|
resp, err := healthClient.Do(req)
|
|
lat := time.Since(start).Milliseconds()
|
|
if err != nil {
|
|
return svcHealth{Status: "down", URL: url, LatencyMs: lat, Error: err.Error()}
|
|
}
|
|
defer resp.Body.Close()
|
|
_, _ = io.Copy(io.Discard, resp.Body)
|
|
status := "ok"
|
|
if resp.StatusCode >= 400 {
|
|
status = "down"
|
|
}
|
|
return svcHealth{Status: status, LatencyMs: lat, HTTPStatus: resp.StatusCode, URL: url}
|
|
}
|
|
|
|
// GET /api/status — aggregate health of the API Server and its neighbours
|
|
// (PocketBase and the Web App), probed server-side. The panel polls this so the
|
|
// browser never has to reach PocketBase or the Web App directly.
|
|
func (s *Server) handleStatus(w http.ResponseWriter, r *http.Request) {
|
|
var pb, web svcHealth
|
|
var wg sync.WaitGroup
|
|
wg.Add(2)
|
|
go func() { defer wg.Done(); pb = probe(r.Context(), s.pbURL()+"/api/health") }()
|
|
go func() { defer wg.Done(); web = probe(r.Context(), s.cfg.WebAppURL+"/healthz") }()
|
|
wg.Wait()
|
|
|
|
writeJSON(w, http.StatusOK, map[string]any{
|
|
"apiServer": map[string]any{
|
|
"status": "ok",
|
|
"devices": s.hub.OnlineCount(),
|
|
"known": len(s.hub.Snapshot()),
|
|
},
|
|
"pocketBase": pb,
|
|
"webApp": web,
|
|
})
|
|
}
|