// Package health probes an ori server's GET /healthz endpoint. It is pure Go // (no GUI dependency) so the connection logic of the desktop client can be // unit-tested against an httptest server. package health import ( "context" "encoding/json" "fmt" "io" "net/http" "time" ) // DefaultTimeout bounds a single probe; a desktop connection screen must fail // fast when the server is down. const DefaultTimeout = 3 * time.Second // Report is the outcome of one probe, shaped to cross the Wails bridge as a // plain JSON object (no error type, so the JavaScript side never has to // distinguish a rejected promise from a negative answer). type Report struct { // OK is true when /healthz answered with a 2xx status. OK bool `json:"ok"` // URL is the exact URL that was probed. URL string `json:"url"` // StatusCode is the HTTP status received, or 0 when no response arrived. StatusCode int `json:"statusCode"` // Detail is a short human-readable explanation ("ori is reachable", // the transport error, or the unexpected status). Detail string `json:"detail"` } // Check performs GET /healthz. baseURL must already be normalised // (scheme + host, no trailing slash). A nil client uses http.DefaultTransport // with DefaultTimeout. func Check(ctx context.Context, client *http.Client, baseURL string) Report { if client == nil { client = &http.Client{Timeout: DefaultTimeout} } target := baseURL + "/healthz" rep := Report{URL: target} req, err := http.NewRequestWithContext(ctx, http.MethodGet, target, nil) if err != nil { rep.Detail = fmt.Sprintf("invalid URL: %v", err) return rep } resp, err := client.Do(req) if err != nil { rep.Detail = fmt.Sprintf("cannot reach ori: %v", err) return rep } defer resp.Body.Close() rep.StatusCode = resp.StatusCode body, _ := io.ReadAll(io.LimitReader(resp.Body, 4096)) if resp.StatusCode < 200 || resp.StatusCode > 299 { rep.Detail = fmt.Sprintf("unexpected status %s from /healthz (is this really an ori server?)", resp.Status) return rep } rep.OK = true rep.Detail = describe(body) return rep } // describe turns ori's `{"status":"ok"}` body into a one-liner, tolerating // any other 2xx body so a future ori version cannot break the client. func describe(body []byte) string { var payload struct { Status string `json:"status"` } if err := json.Unmarshal(body, &payload); err == nil && payload.Status != "" { return "ori is reachable (status: " + payload.Status + ")" } return "ori is reachable" }