Files
terdut-tui/internal/api/types.go
T
Niklas Ye e04cfcf433
Release / build (amd64, darwin) (push) Failing after 10s
Release / build (amd64, linux) (push) Failing after 9s
Release / build (arm64, darwin) (push) Failing after 11s
Release / build (arm64, linux) (push) Failing after 9s
Release / release (push) Has been skipped
feat: Last Seen column tracking Alertmanager re-send heartbeat
The alert list showed only Started, which comes from Prometheus and
never changes for the lifetime of an alert instance. A firing alert
that started 12 days ago looked identical whether Alertmanager
refreshed it 30 seconds ago or went silent a week ago.

terdut-server already tracks this: the webhook upsert sets
received_at on every accepted payload, including the periodic
re-sends issued at repeat_interval, and its archiver treats the
field as a liveness heartbeat. The field was already decoded into
api.Alert.ReceivedAt and simply never rendered.

Add a Last Seen column to the alert tables, rendered with the
existing humanAgo helper. The Alerts and Archived tabs share
alertColumns/alertRows, so both pick it up. The width budget is
re-derived for five columns; the slack constant now accounts for
all of bubbles' per-cell padding, so the table lands exactly on
the terminal width instead of overflowing by two columns as it
did with four.

The detail view gains a matching Last Seen line, with the timeline
labels widened to keep values aligned. Since received_at stops
advancing once an alert resolves, also pull through the server's
resolution_source and show it in the status header
(RESOLVED · alertmanager vs RESOLVED · expiry) so a frozen
timestamp is explained.
2026-07-30 08:48:53 +02:00

81 lines
2.4 KiB
Go

package api
import "time"
type Alert struct {
ID int64 `json:"id"`
Fingerprint string `json:"fingerprint"`
Name string `json:"name"`
Status string `json:"status"`
Labels map[string]string `json:"labels"`
Annotations map[string]string `json:"annotations"`
StartsAt time.Time `json:"starts_at"`
EndsAt *time.Time `json:"ends_at"`
GeneratorURL string `json:"generator_url"`
ReceivedAt time.Time `json:"received_at"`
AcknowledgedByID *int64 `json:"acknowledged_by_id"`
AcknowledgedBy string `json:"acknowledged_by"`
AcknowledgedAt *time.Time `json:"acknowledged_at"`
ArchivedAt *time.Time `json:"archived_at,omitempty"`
// ResolutionSource records why a resolved alert left the firing state:
// "alertmanager" for a real resolved webhook, "expiry" when the server
// inferred it after the alert stopped being refreshed.
ResolutionSource *string `json:"resolution_source,omitempty"`
}
type AlertStats struct {
Total int `json:"total"`
Firing int `json:"firing"`
Resolved int `json:"resolved"`
}
type Comment struct {
ID int64 `json:"id"`
AlertID int64 `json:"alert_id"`
UserID int64 `json:"user_id"`
Username string `json:"username"`
Content string `json:"content"`
CreatedAt time.Time `json:"created_at"`
}
type TopAlert struct {
Name string `json:"name"`
Count int `json:"count"`
}
type HourStat struct {
Hour int `json:"hour"`
Count int `json:"count"`
}
type DayStat struct {
Day int `json:"day"`
DayName string `json:"day_name"`
Count int `json:"count"`
}
type ScheduleEntry struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
Username string `json:"username"`
Date string `json:"date"` // YYYY-MM-DD
CreatedAt time.Time `json:"created_at"`
}
type User struct {
ID int64 `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
type APIKey struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
Name string `json:"name"`
Key string `json:"key,omitempty"` // only present in create response
CreatedAt time.Time `json:"created_at"`
LastUsedAt *time.Time `json:"last_used_at"`
}