diff --git a/server/backend/internal/httpapi/playerstatus.go b/server/backend/internal/httpapi/playerstatus.go index 365190f..e865a47 100644 --- a/server/backend/internal/httpapi/playerstatus.go +++ b/server/backend/internal/httpapi/playerstatus.go @@ -243,21 +243,45 @@ var ( errInvalidStale = errors.New("invalid stale") ) -func writeOverviewQueryError(w http.ResponseWriter, err error) { +// overviewQueryErrorCode returns the machine-readable error code for query +// validation errors. It is used by both the JSON and HTML error paths. +func overviewQueryErrorCode(err error) string { switch err { case errInvalidUpdatedSince: - writeError(w, http.StatusBadRequest, "invalid_updated_since", "updated_since ist kein gueltiger RFC3339-Zeitstempel", nil) + return "invalid_updated_since" case errInvalidLimit: - writeError(w, http.StatusBadRequest, "invalid_limit", "limit muss eine positive Ganzzahl sein", nil) + return "invalid_limit" case errInvalidServerConnectivity: - writeError(w, http.StatusBadRequest, "invalid_server_connectivity", "server_connectivity muss online, offline, degraded oder unknown sein", nil) + return "invalid_server_connectivity" case errInvalidStale: - writeError(w, http.StatusBadRequest, "invalid_stale", "stale muss true oder false sein", nil) + return "invalid_stale" default: - writeError(w, http.StatusBadRequest, "invalid_query", "ungueltige Query-Parameter", nil) + return "invalid_query" } } +// overviewQueryErrorMessage returns the human-readable message for query +// validation errors. It is used by both the JSON and HTML error paths so +// that the wording stays consistent regardless of response format. +func overviewQueryErrorMessage(err error) string { + switch err { + case errInvalidUpdatedSince: + return "updated_since ist kein gueltiger RFC3339-Zeitstempel" + case errInvalidLimit: + return "limit muss eine positive Ganzzahl sein" + case errInvalidServerConnectivity: + return "server_connectivity muss online, offline, degraded oder unknown sein" + case errInvalidStale: + return "stale muss true oder false sein" + default: + return "ungueltige Query-Parameter" + } +} + +func writeOverviewQueryError(w http.ResponseWriter, err error) { + writeError(w, http.StatusBadRequest, overviewQueryErrorCode(err), overviewQueryErrorMessage(err), nil) +} + func validateOptionalRFC3339(value string) error { if strings.TrimSpace(value) == "" { return nil diff --git a/server/backend/internal/httpapi/router_test.go b/server/backend/internal/httpapi/router_test.go index adaa783..7e70106 100644 --- a/server/backend/internal/httpapi/router_test.go +++ b/server/backend/internal/httpapi/router_test.go @@ -218,6 +218,7 @@ func TestRouterScreenDetailPageRoute(t *testing.T) { "← All screens", "Timing", "Endpoints", + "", } { if !strings.Contains(body, want) { t.Fatalf("body missing %q", want) diff --git a/server/backend/internal/httpapi/statuspage.go b/server/backend/internal/httpapi/statuspage.go index 39719d0..00d1757 100644 --- a/server/backend/internal/httpapi/statuspage.go +++ b/server/backend/internal/httpapi/statuspage.go @@ -34,6 +34,7 @@ type statusFilterLink struct { type screenDetailPageData struct { GeneratedAt string + RefreshSeconds int Record playerStatusRecord StatusPagePath string } @@ -606,6 +607,7 @@ var screenDetailTemplate = template.Must(template.New("screen-detail").Funcs(sta
+