Transportiere Server-Connectivity im Statuspfad

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
Jesko Anschütz 2026-03-22 18:27:44 +01:00
parent a69135c0b9
commit 8f0f06ae25
8 changed files with 32 additions and 0 deletions

View file

@ -200,6 +200,7 @@ Ergaenzt seit dem ersten Geruest:
- strukturierte Agent-Logs mit internem Health-Snapshot und signalgesteuertem Shutdown
- erster periodischer HTTP-Status-Reporter im Agent
- Server-Connectivity-Zustand im Agent (`unknown`, `online`, `degraded`, `offline`) auf Basis der Report-Ergebnisse
- der HTTP-Statuspfad transportiert jetzt neben `status` auch `server_connectivity`
- lokales Compose-Grundgeruest fuer PostgreSQL und Mosquitto
## Arbeitsweise

View file

@ -26,6 +26,7 @@ Mindestens enthalten:
Aktuell zusaetzlich enthalten:
- `server_connectivity`
- `server_url`
- `mqtt_broker`
- `heartbeat_every_seconds`
@ -39,6 +40,7 @@ Aktuell zusaetzlich enthalten:
"screen_id": "info01-dev",
"ts": "2026-03-22T16:00:00Z",
"status": "running",
"server_connectivity": "online",
"server_url": "http://127.0.0.1:8080",
"mqtt_broker": "tcp://127.0.0.1:1883",
"heartbeat_every_seconds": 30,
@ -68,6 +70,8 @@ Zusätzlich zur Write-Route gibt es in dieser Stufe:
Dieser Endpunkt liefert den zuletzt akzeptierten Status fuer einen Screen zurueck.
Wenn fuer den Screen noch kein Status vorliegt, liefert das Backend `404` mit dem gemeinsamen Fehlerumschlag.
Der aktuell zurueckgelieferte Datensatz enthaelt damit sowohl den Lifecycle-Status (`status`) als auch den vom Agenten lokal abgeleiteten Reachability-Zustand (`server_connectivity`).
## Abgrenzung
Noch nicht Teil dieser Stufe:

View file

@ -176,6 +176,7 @@ func (a *App) reportStatus(ctx context.Context) {
err := a.reporter.Send(ctx, statusreporter.Snapshot{
Status: string(snapshot.Status),
ServerConnectivity: string(snapshot.ServerConnectivity),
ScreenID: snapshot.ScreenID,
ServerBaseURL: snapshot.ServerBaseURL,
MQTTBroker: snapshot.MQTTBroker,

View file

@ -12,6 +12,7 @@ import (
type Snapshot struct {
Status string
ServerConnectivity string
ScreenID string
ServerBaseURL string
MQTTBroker string
@ -24,6 +25,7 @@ type statusPayload struct {
ScreenID string `json:"screen_id"`
Timestamp string `json:"ts"`
Status string `json:"status"`
ServerConnectivity string `json:"server_connectivity"`
ServerURL string `json:"server_url"`
MQTTBroker string `json:"mqtt_broker"`
HeartbeatEverySeconds int `json:"heartbeat_every_seconds"`
@ -88,6 +90,7 @@ func buildPayload(snapshot Snapshot, now time.Time) statusPayload {
ScreenID: snapshot.ScreenID,
Timestamp: now.Format(time.RFC3339),
Status: snapshot.Status,
ServerConnectivity: snapshot.ServerConnectivity,
ServerURL: snapshot.ServerBaseURL,
MQTTBroker: snapshot.MQTTBroker,
HeartbeatEverySeconds: snapshot.HeartbeatEverySeconds,

View file

@ -14,6 +14,7 @@ func TestBuildPayloadFromSnapshot(t *testing.T) {
lastHeartbeatAt := time.Date(2026, 3, 22, 16, 0, 0, 0, time.UTC)
snapshot := Snapshot{
Status: "running",
ServerConnectivity: "online",
ScreenID: "info01-dev",
ServerBaseURL: "http://127.0.0.1:8080",
MQTTBroker: "tcp://127.0.0.1:1883",
@ -36,6 +37,10 @@ func TestBuildPayloadFromSnapshot(t *testing.T) {
t.Fatalf("StartedAt = %q, want %q", got, want)
}
if got, want := payload.ServerConnectivity, "online"; got != want {
t.Fatalf("ServerConnectivity = %q, want %q", got, want)
}
if got, want := payload.LastHeartbeatAt, lastHeartbeatAt.Format(time.RFC3339); got != want {
t.Fatalf("LastHeartbeatAt = %q, want %q", got, want)
}
@ -67,6 +72,7 @@ func TestReporterSendStatus(t *testing.T) {
err := reporter.Send(context.Background(), Snapshot{
Status: "running",
ServerConnectivity: "online",
ScreenID: "info01-dev",
ServerBaseURL: "http://127.0.0.1:8080",
MQTTBroker: "tcp://127.0.0.1:1883",
@ -81,4 +87,8 @@ func TestReporterSendStatus(t *testing.T) {
if got, want := received.ScreenID, "info01-dev"; got != want {
t.Fatalf("received.ScreenID = %q, want %q", got, want)
}
if got, want := received.ServerConnectivity, "online"; got != want {
t.Fatalf("received.ServerConnectivity = %q, want %q", got, want)
}
}

View file

@ -10,6 +10,7 @@ type playerStatusRequest struct {
ScreenID string `json:"screen_id"`
Timestamp string `json:"ts"`
Status string `json:"status"`
ServerConnectivity string `json:"server_connectivity"`
ServerURL string `json:"server_url"`
MQTTBroker string `json:"mqtt_broker"`
HeartbeatEverySeconds int `json:"heartbeat_every_seconds"`
@ -59,6 +60,7 @@ func handlePlayerStatus(store playerStatusStore) http.HandlerFunc {
ScreenID: request.ScreenID,
Timestamp: request.Timestamp,
Status: request.Status,
ServerConnectivity: request.ServerConnectivity,
ServerURL: request.ServerURL,
MQTTBroker: request.MQTTBroker,
HeartbeatEverySeconds: request.HeartbeatEverySeconds,

View file

@ -6,6 +6,7 @@ type playerStatusRecord struct {
ScreenID string `json:"screen_id"`
Timestamp string `json:"ts"`
Status string `json:"status"`
ServerConnectivity string `json:"server_connectivity,omitempty"`
ServerURL string `json:"server_url,omitempty"`
MQTTBroker string `json:"mqtt_broker,omitempty"`
HeartbeatEverySeconds int `json:"heartbeat_every_seconds,omitempty"`

View file

@ -14,6 +14,7 @@ func TestHandlePlayerStatusAccepted(t *testing.T) {
"screen_id": "info01-dev",
"ts": "2026-03-22T16:00:00Z",
"status": "running",
"server_connectivity": "online",
"server_url": "http://127.0.0.1:8080",
"mqtt_broker": "tcp://127.0.0.1:1883",
"heartbeat_every_seconds": 30,
@ -50,6 +51,10 @@ func TestHandlePlayerStatusAccepted(t *testing.T) {
if got, want := stored.ScreenID, "info01-dev"; got != want {
t.Fatalf("stored.ScreenID = %q, want %q", got, want)
}
if got, want := stored.ServerConnectivity, "online"; got != want {
t.Fatalf("stored.ServerConnectivity = %q, want %q", got, want)
}
}
func TestHandlePlayerStatusRejectsInvalidJSON(t *testing.T) {
@ -171,6 +176,7 @@ func TestHandleGetLatestPlayerStatus(t *testing.T) {
ScreenID: "info01-dev",
Timestamp: "2026-03-22T16:00:00Z",
Status: "running",
ServerConnectivity: "degraded",
ServerURL: "http://127.0.0.1:8080",
MQTTBroker: "tcp://127.0.0.1:1883",
HeartbeatEverySeconds: 30,
@ -196,6 +202,10 @@ func TestHandleGetLatestPlayerStatus(t *testing.T) {
if got, want := response.ScreenID, "info01-dev"; got != want {
t.Fatalf("response.ScreenID = %q, want %q", got, want)
}
if got, want := response.ServerConnectivity, "degraded"; got != want {
t.Fatalf("response.ServerConnectivity = %q, want %q", got, want)
}
}
func TestHandleGetLatestPlayerStatusNotFound(t *testing.T) {