Ergaenze Auto-Refresh auf Detailseite und bereinige Fehlermeldungs-Duplikat

Auto-Refresh auf GET /status/{screenId}:
  screenDetailPageData bekommt RefreshSeconds (wie statusPageData). Das
  Detail-Template rendert das Meta-Tag analog zur Uebersichtsseite mit
  denselben 15 Sekunden, damit ein Screen seinen Zustand (z.B. fresh ->
  stale, connectivity-Wechsel) auch ohne manuellen Reload sichtbar macht.
  Test: meta-refresh-Tag jetzt in TestRouterScreenDetailPageRoute geprueft.

DRY-Refactor: Fehlermeldungen vereinheitlicht:
  overviewQueryErrorMessage und overviewQueryErrorCode sind jetzt in
  playerstatus.go definiert -- dort wo auch die Validierungslogik lebt.
  writeOverviewQueryError delegiert vollstaendig an beide Helper statt
  die Meldungen selbst zu duplizieren. Die vorherige Kopie in statuspage.go
  mit abweichenden Satzendezeichen und Grossschreibung wurde entfernt.
  Beide Fehlerpfade (JSON und HTML) nutzen jetzt exakt dieselben
  Meldungstexte.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jesko Anschütz 2026-03-22 20:16:22 +01:00
parent cfab277dc4
commit 57e0cdb43c
3 changed files with 34 additions and 24 deletions

View file

@ -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

View file

@ -218,6 +218,7 @@ func TestRouterScreenDetailPageRoute(t *testing.T) {
"← All screens",
"Timing",
"Endpoints",
"<meta http-equiv=\"refresh\" content=\"15\">",
} {
if !strings.Contains(body, want) {
t.Fatalf("body missing %q", want)

View file

@ -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
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="refresh" content="{{.RefreshSeconds}}">
<title>{{.Record.ScreenID}} Screen Status</title>
` + statusPageCSSBlock + `
</head>
@ -768,6 +770,7 @@ func handleScreenDetailPage(store playerStatusStore) http.HandlerFunc {
data := screenDetailPageData{
GeneratedAt: store.Now().Format(time.RFC3339),
RefreshSeconds: 15,
Record: record,
StatusPagePath: "/status",
}
@ -875,24 +878,6 @@ func writeStatusPageQueryError(w http.ResponseWriter, queryErr error) {
}
}
// overviewQueryErrorMessage returns a human-readable message for the given
// overview query validation error. It is shared between the HTML and JSON
// error paths to keep messages consistent.
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 statusClass(value string) string {
trimmed := strings.TrimSpace(value)
if trimmed == "" {