Commit Graph

202 Commits

Author SHA1 Message Date
lerko 65a83368bf fix(store): cascade delete related rows when removing a site
CI / test (pull_request) Successful in 2m32s
CI / lint (pull_request) Successful in 56s
CI / vulncheck (pull_request) Successful in 51s
DeleteSite now removes maintenance_windows, check_history, and
state_changes for the site within a transaction before deleting
the site itself. Prevents orphaned rows.

Closes #71
2026-06-05 12:55:43 -04:00
lerko 965a864343 chore: streamline issue templates, redirect GitHub issues to Gitea
CI / test (pull_request) Successful in 2m35s
CI / lint (pull_request) Successful in 56s
CI / vulncheck (pull_request) Successful in 50s
Simplify bug report (5 fields → 3 + search checkbox) and feature
request (4 fields → 2). Add GitHub ISSUE_TEMPLATE config that
disables blank issues and redirects to Gitea.
2026-06-05 11:01:18 -04:00
lerko 96eb660b29 chore: add bug report and feature request issue templates
CI / test (pull_request) Successful in 2m29s
CI / lint (pull_request) Successful in 51s
CI / vulncheck (pull_request) Successful in 51s
2026-06-05 14:46:59 +00:00
lerko c471a72ff5 fix(monitor): inject time into ComputeDailyBreakdown for testability
CI / test (pull_request) Successful in 2m30s
CI / lint (pull_request) Successful in 56s
CI / vulncheck (pull_request) Successful in 51s
Test failed near midnight when outage events fell in previous day's
bucket. Accept a now parameter instead of calling time.Now() internally.
2026-06-04 21:29:03 -04:00
lerko 7bff79b09c fix(tui): check fmt.Sscanf return value (errcheck lint)
CI / test (pull_request) Successful in 2m37s
CI / lint (pull_request) Successful in 57s
CI / vulncheck (pull_request) Successful in 51s
2026-06-04 19:56:05 -04:00
lerko 986681ef8a feat(tui): overhaul latency sparkline scaling, color, and layout
CI / test (pull_request) Successful in 2m39s
CI / lint (pull_request) Failing after 56s
CI / vulncheck (pull_request) Successful in 51s
Replace misleading relative-only sparkline with dual-channel design:
bar height uses relative scaling (shows stability and anomalies),
color+brightness uses absolute thresholds (shows fast vs slow).

- Add brightness gradient within color bands (dim→bright as latency
  increases toward the next threshold)
- Pass row background through sparkline rendering so zebra stripes
  and selection highlights carry through ANSI sequences
- Cap sparkline width to 60 (matches maxHistoryLen) and column
  width to 62 to eliminate trailing dead space
- Quiet group sparkline: subtle dots for healthy, bold red for down
- Add braille subpixel canvas (ported from meridian) for future
  multi-row graph use
2026-06-04 19:40:34 -04:00
lerko 00fa381a7c fix(monitor): log STALE recovery in push heartbeat handler 2026-06-04 17:41:39 -04:00
lerko a38a397d3a docs: update alert provider count to 10 (add Opsgenie) 2026-06-04 17:33:35 -04:00
lerko fb709b34c5 refactor(tui): status icons, clean STATUS column, relative time
CI / test (pull_request) Successful in 2m30s
CI / lint (pull_request) Successful in 56s
CI / vulncheck (pull_request) Successful in 51s
- STATUS column shows icon + clean state only (▲ UP, ▼ DOWN, ◆ LATE,
  ◆ STALE, ◇ PAUSED, ◼ MAINT, ○ PENDING). Error classification
  (DNS/TLS/TMO) removed from STATUS — stays in NAME inline hint.
- Detail panel Last Check shows relative time ("12s ago") instead of
  absolute timestamp.
- Extract shared fmtTimeAgo() to format.go, consolidate duplicate
  formatters in tab_alerts.go and tab_nodes.go.
2026-06-04 17:03:04 -04:00
lerko 33a3ff9bcb fix(tui): expand log viewport to fill content area
CI / test (pull_request) Successful in 2m36s
CI / lint (pull_request) Successful in 1m6s
CI / vulncheck (pull_request) Successful in 51s
Previous + 3 over-restricted viewport height, leaving blank
lines at the bottom of the logs tab.
2026-06-04 16:13:40 -04:00
lerko d099740f33 fix(tui): remove extra blank lines above footer
CI / test (pull_request) Successful in 2m37s
CI / lint (pull_request) Successful in 57s
CI / vulncheck (pull_request) Successful in 51s
JoinVertical adds no gap lines between sections. The - 2
subtraction was over-reserving space, leaving 2 blank lines
between content and footer.
2026-06-04 16:12:14 -04:00
lerko cdb8c356e9 fix(tui): clip overflowing content to keep footer pinned
CI / test (pull_request) Successful in 2m38s
CI / lint (pull_request) Successful in 56s
CI / vulncheck (pull_request) Successful in 51s
Sites table with many rows exceeded the fixed content height,
pushing footer down. MaxHeight now clips content that overflows
while Height still pads shorter content upward.
2026-06-04 16:07:44 -04:00
lerko d4a2e9dd53 fix(tui): normalize content whitespace for consistent footer position
CI / test (pull_request) Successful in 2m43s
CI / lint (pull_request) Successful in 1m1s
CI / vulncheck (pull_request) Successful in 51s
Each tab returned different leading newlines (Sites/tables: 1,
Logs: 3, empty states: varies). TrimSpace content before layout
so JoinVertical controls all spacing. Remove leading \n from
footer since JoinVertical handles gaps.
2026-06-04 16:03:57 -04:00
lerko aae6e6e65e fix(tui): pin footer to bottom of terminal
CI / test (pull_request) Successful in 2m45s
CI / lint (pull_request) Successful in 56s
CI / vulncheck (pull_request) Successful in 51s
Replace string concatenation layout with lipgloss.JoinVertical
and fixed-height content area. Footer now stays at the same
vertical position regardless of tab content height. Uses
lipgloss.Height() to dynamically measure header/footer and
fill remaining space.
2026-06-04 15:59:32 -04:00
lerko e0f189efe9 fix(tui): logs tab use viewport for scrollable content
CI / test (pull_request) Successful in 2m39s
CI / lint (pull_request) Successful in 56s
CI / vulncheck (pull_request) Successful in 51s
Logs were dumping all lines directly, pushing the dashboard
footer off screen. Now uses logViewport with proper height
accounting so footer stays visible and scrolling works.
2026-06-04 15:36:21 -04:00
lerko ba75be194d refactor(tui): consistent chrome across all views
CI / test (pull_request) Successful in 2m36s
CI / lint (pull_request) Successful in 56s
CI / vulncheck (pull_request) Successful in 51s
- Extract divider() and emptyState() helpers to format.go
- All empty states now use bordered box with accent color
- Detail and alert detail panels get header/section dividers
- SLA label width 14→16 to match detail/alert panels
- Logs key hints moved from content to dashboard footer
- History/SLA panels use shared divider helper
2026-06-04 19:23:12 +00:00
lerko e0cb0adebd fix(tui): quick wins batch — version footer, column widths, zebra, sparkline
CI / test (pull_request) Successful in 2m34s
CI / lint (pull_request) Successful in 57s
CI / vulncheck (pull_request) Successful in 51s
- Show version in dashboard footer (wired from goreleaser ldflags)
- Cap name column at 35, raise sparkline minimum to 15 chars
- Preserve zebra background on group rows (was lost by style override)
- Group sparkline uses bullet • instead of heavy circle ●
2026-06-04 14:56:01 -04:00
lerko 60592ef810 feat(tui): add SLA reporting view
CI / test (pull_request) Successful in 2m35s
CI / lint (pull_request) Successful in 56s
CI / vulncheck (pull_request) Successful in 41s
Full-screen SLA report accessible via [s] from detail panel.
Computes uptime%, downtime, outage count, longest outage, MTTR,
and MTBF from state_changes table. Includes daily breakdown with
bar chart, switchable time periods (24h/7d/30d/90d), and
scrollable viewport. LATE/STALE treated as UP for SLA purposes.
2026-06-04 14:24:39 -04:00
lerko b2e92e8a2a fix(monitor): propagate STALE/LATE child status to group
checkGroup only checked for DOWN/SSL EXP and PENDING. Groups
now reflect STALE and LATE children with proper priority:
DOWN > STALE > LATE > PENDING > UP.
2026-06-04 18:23:57 +00:00
lerko 66b1c662c9 fix(tui): show correct push heartbeat curl command in detail panel 2026-06-04 18:23:57 +00:00
lerko 10c6ec348e fix(tui): show push token and URL in detail panel
Push monitors were missing token/endpoint info in the detail
view, making it impossible to know where to send heartbeats.
2026-06-04 18:23:57 +00:00
lerko ca43621c44 feat(monitor): add STALE state for push monitors
New intermediate state between LATE and DOWN at the midpoint of
the grace period. Gives operators earlier warning that a push
monitor has gone quiet. Includes dedicated orange theme color
across all 5 themes and proper styling in dashboard, detail
panel, and history view.
2026-06-04 18:23:57 +00:00
lerko f23014ab12 feat(alert): add Opsgenie provider
CI / test (pull_request) Successful in 2m37s
CI / lint (pull_request) Successful in 57s
CI / vulncheck (pull_request) Successful in 51s
Support Opsgenie Alert API v2 with US/EU endpoint selection,
configurable priority (P1-P5), and GenieKey auth. TUI form
includes API key, priority picker, and EU instance toggle.
2026-06-04 13:32:14 -04:00
lerko 50ee878097 ci: drop redundant push-to-main trigger
PR already runs test+lint+vulncheck on the same commit.
Running identical jobs again on merge wastes ~10min of runner.
2026-06-04 13:29:40 -04:00
lerko 9e15b369d3 fix(tui): wire up [e] edit key in detail panel
CI / test (pull_request) Successful in 2m38s
CI / lint (pull_request) Successful in 56s
CI / vulncheck (pull_request) Successful in 46s
CI / test (push) Successful in 2m40s
CI / lint (push) Successful in 56s
CI / vulncheck (push) Successful in 51s
The detail panel footer showed [e] Edit but handleDetailKey had no
case for it. Route to handleEditItem() like the dashboard does.
2026-06-04 12:36:24 -04:00
lerko 5b39be8eb2 fix(tui): broken tick chain after form/dialog + retries off-by-one
CI / test (pull_request) Successful in 2m43s
CI / lint (pull_request) Successful in 56s
CI / vulncheck (pull_request) Successful in 51s
Update() routed form and confirm-delete states before handling
time.Time ticks, so those handlers swallowed the tick command and
permanently broke the refresh loop. After opening any form or
delete dialog, the TUI stopped auto-refreshing until restarted.

Move time.Time and WindowSizeMsg handling before state dispatch
so ticks always fire regardless of view state.

Also fix fmtRetries off-by-one: FailureCount=1 displayed as 0/N
instead of 1/N due to an erroneous subtract-one.
2026-06-04 12:34:10 -04:00
lerko eb9546c97e fix(monitor): trigger immediate recheck after site config edit
CI / test (pull_request) Successful in 2m32s
CI / lint (pull_request) Successful in 1m1s
CI / vulncheck (pull_request) Successful in 51s
Monitor goroutine slept for the full check interval after a config
edit, so hostname/URL changes wouldn't take effect until the next
scheduled check. Added per-site recheck channel that wakes the
goroutine immediately when UpdateSiteConfig is called.
2026-06-04 12:23:04 -04:00
lerko 1d1f5d0ee4 fix(tui): resolve staticcheck lint errors in history view
CI / test (pull_request) Successful in 2m39s
CI / lint (pull_request) Successful in 51s
CI / vulncheck (pull_request) Successful in 51s
- Replace deprecated LineUp/LineDown/HalfViewUp/HalfViewDown with
  ScrollUp/ScrollDown/HalfPageUp/HalfPageDown
- Use tagged switch for mouse button dispatch
- Use fmt.Fprintf instead of WriteString(Sprintf)
2026-06-03 20:22:41 -04:00
lerko bc661f5207 feat(tui): add state change history view with outage duration
CI / test (pull_request) Successful in 2m30s
CI / lint (pull_request) Failing after 51s
CI / vulncheck (pull_request) Successful in 46s
Full-screen scrollable history view accessible via [h] from detail
panel. Shows all state transitions with computed outage durations,
event density sparkline for flapping detection, and summary stats.

- Detail panel STATE CHANGES now shows outage duration per recovery
- Event density sparkline highlights flapping periods
- Summary footer: event count, outage count, avg outage duration
- Vim-style navigation (j/k/g/G) + mouse scroll in history view
2026-06-03 19:49:10 -04:00
lerko c0ad51af9c fix(tui): classify safedial "failed to connect" as TCP
CI / test (pull_request) Successful in 2m29s
CI / lint (pull_request) Successful in 56s
CI / vulncheck (pull_request) Successful in 51s
CI / test (push) Successful in 2m41s
CI / lint (push) Successful in 56s
CI / vulncheck (push) Successful in 51s
Error from safedial.go fell through to ErrCatUnknown, showing plain
DOWN instead of DOWN:TCP.
2026-06-03 17:24:31 -04:00
lerko c25614c098 fix(tui): remove error truncation from detail panel
CI / test (pull_request) Successful in 2m29s
CI / lint (pull_request) Successful in 56s
CI / vulncheck (pull_request) Successful in 51s
Error row now word-wraps to terminal width instead of hard-truncating.
Probe results and state change errors show full text.
2026-06-03 16:57:27 -04:00
lerko 3d7ab5a49e feat(tui): classify error reasons on DOWN monitors
CI / test (pull_request) Successful in 2m30s
CI / lint (pull_request) Successful in 1m7s
CI / vulncheck (pull_request) Successful in 46s
Categorize raw error strings into DNS/TCP/TLS/HTTP/ICMP/TMO/PRIV
so users get instant triage from the monitor list without opening
the detail panel.

- Status column shows DOWN:DNS, DOWN:TLS, DOWN:HTTP, etc.
- Inline NAME column errors prefixed with category tag [DNS], [TLS]
- Detail panel shows connection chain checklist for HTTP monitors
  (✓ DNS → ✓ TCP → ✗ TLS → · HTTP) pinpointing failure layer
- All display-side only — no database or model changes
2026-06-03 16:33:12 -04:00
lerko 5d362fdbe6 refactor(tui): decompose god files into single-concern modules
CI / test (pull_request) Successful in 2m34s
CI / lint (pull_request) Successful in 56s
CI / vulncheck (pull_request) Successful in 51s
CI / test (push) Successful in 2m32s
CI / lint (push) Successful in 56s
CI / vulncheck (push) Successful in 51s
tui.go (1032→164) and tab_sites.go (993→482) violated "small functions"
and "testable in isolation" standards. Extracted 6 new files by concern:

- format.go: pure formatting functions (fmtLatency, fmtUptime, etc.)
- sparkline.go: sparkline rendering (latency, heartbeat, group)
- update.go: Update method decomposed into 15 named handlers
- view_dashboard.go: View, dashboard composition, tab bar, footer
- view_detail.go: site detail panel
- data.go: data refresh with extracted sortSitesForDisplay/filterSites

Added 17 unit tests for the newly-testable pure functions covering
format, sparkline, sort ordering, and filter logic. No behavioral
changes — strict move-and-extract refactor.
2026-06-02 21:06:30 -04:00
lerko 89f3ff27e3 test: verify GPG merge signing
CI / test (pull_request) Successful in 2m31s
CI / lint (pull_request) Successful in 51s
CI / vulncheck (pull_request) Successful in 56s
CI / test (push) Successful in 2m34s
CI / lint (push) Successful in 1m6s
CI / vulncheck (push) Successful in 51s
2026-06-02 20:38:10 -04:00
lerko cc9f04aa2f Merge pull request 'docs: public readiness — env reference, clustering guide, README improvements' (#50) from chore/public-readiness into main
CI / test (push) Successful in 2m31s
CI / lint (push) Successful in 56s
CI / vulncheck (push) Successful in 51s
Reviewed-on: #50
2026-06-02 23:53:41 +00:00
lerko 771721abb4 fix(security): bump Go 1.26.3 → 1.26.4
CI / test (pull_request) Successful in 2m31s
CI / lint (pull_request) Successful in 55s
CI / vulncheck (pull_request) Successful in 51s
Fixes GO-2026-5039 (net/textproto) and GO-2026-5037 (crypto/x509).
2026-06-02 18:43:11 -04:00
lerko d316c5cf1b docs: fix PEER_URL description to include probe nodes
CI / test (pull_request) Successful in 2m25s
CI / lint (pull_request) Successful in 46s
CI / vulncheck (pull_request) Failing after 40s
2026-06-02 17:30:37 -04:00
lerko 99c0ad8072 chore: add .env and .github/ to .dockerignore
Prevent secrets from leaking into Docker build context.
GitHub workflows don't belong in the image.
2026-06-02 17:30:37 -04:00
lerko 048b245d25 docs: add env reference, clustering guide, and README improvements
- .env.example: complete env var reference (21 vars, grouped, commented)
- docs/clustering.md: leader/follower/probe setup, aggregation, security
- README: encryption section, clustering summary, upgrading note,
  ALLOW_PRIVATE_TARGETS + ENCRYPTION_KEY in env table, link to .env.example
- .gitignore: add .env to prevent credential leaks
2026-06-02 17:30:37 -04:00
lerko 21cbaa9ff9 docs: point release downloads to GitHub mirror
GitHub is the public storefront. Binary releases link to GitHub
where the relay workflow publishes them. Clarify Linux amd64 only
for binary downloads.
2026-06-02 17:30:37 -04:00
lerko 056cf9c3f7 docs: add missing changelog entries for 2026.05.6 and 2026.06.2
Both tags were infrastructure-only (CI/CD pipeline work).
Marked as (infrastructure) to distinguish from user-facing releases.
2026-06-02 17:30:37 -04:00
lerko 737ada2f5e Merge pull request 'feat(ci): add GitHub release relay workflow' (#49) from feat/github-release-relay into main
CI / test (push) Successful in 2m20s
CI / lint (push) Successful in 45s
CI / vulncheck (push) Successful in 41s
Release Binaries / release (push) Successful in 2m6s
Release Docker / docker (push) Successful in 21m5s
Reviewed-on: #49
2026-06-02 18:33:21 +00:00
lerko f9e7a4d473 feat(ci): add GitHub release relay workflow
CI / test (pull_request) Successful in 2m21s
CI / lint (pull_request) Successful in 46s
CI / vulncheck (pull_request) Successful in 41s
Mirror pushes tags to GitHub but not releases. This workflow
triggers on tag push, polls Gitea API for the release and
artifacts, then creates a matching GitHub release with the
same binaries and changelog.
2026-06-02 13:59:54 -04:00
lerko a5b499c247 Merge pull request 'fix(ci): make Grype CVE scan non-blocking' (#48) from fix/git-cliff-install into main
CI / test (push) Successful in 2m23s
CI / lint (push) Successful in 40s
CI / vulncheck (push) Successful in 41s
Release Binaries / release (push) Successful in 2m6s
Release Docker / docker (push) Successful in 21m22s
Reviewed-on: #48
2026-06-02 15:41:48 +00:00
lerko c963acb574 fix(ci): make Grype CVE scan non-blocking for known wish vuln
CI / test (pull_request) Successful in 2m21s
CI / lint (pull_request) Successful in 46s
CI / vulncheck (pull_request) Successful in 31s
GHSA-xjvp-7243-rg9h (wish SCP middleware path traversal) is
not exploitable — uptop only uses bubbletea middleware.
Scan still runs and warns but won't fail the release.
2026-06-02 11:24:44 -04:00
lerko c293301051 Merge pull request 'fix(ci): extract git-cliff to /tmp to avoid dirty worktree' (#47) from fix/git-cliff-install into main
CI / test (push) Successful in 2m28s
CI / lint (push) Successful in 45s
CI / vulncheck (push) Successful in 34s
Release Binaries / release (push) Successful in 2m10s
Release Docker / docker (push) Failing after 20m51s
Reviewed-on: #47
2026-06-02 14:20:23 +00:00
lerko 094de23bd0 fix(ci): extract git-cliff to /tmp to avoid dirty worktree
CI / test (pull_request) Successful in 2m29s
CI / lint (pull_request) Successful in 46s
CI / vulncheck (pull_request) Successful in 40s
GoReleaser refuses to release when untracked files exist.
The git-cliff tarball was extracting into the workspace, leaving
a git-cliff-*/ directory that made git status dirty.
2026-06-02 10:15:47 -04:00
lerko fdadafc128 Merge pull request 'fix(ci): resolve git-cliff download URL dynamically' (#46) from fix/git-cliff-install into main
CI / test (push) Successful in 2m23s
CI / lint (push) Successful in 46s
CI / vulncheck (push) Successful in 40s
Release Binaries / release (push) Failing after 28s
Release Docker / docker (push) Has been cancelled
Reviewed-on: #46
2026-06-02 14:05:18 +00:00
lerko 78fff3fb33 fix(ci): resolve git-cliff download URL dynamically
CI / test (pull_request) Successful in 2m36s
CI / lint (pull_request) Successful in 46s
CI / vulncheck (pull_request) Successful in 40s
Asset naming changed upstream — version number now embedded in
filename (git-cliff-2.13.1-x86_64-... instead of git-cliff-x86_64-...).
The latest/download shortcut 404s. Query GitHub API for current version
and build the correct URL.
2026-06-02 09:42:03 -04:00
lerko aaee3f7ebf Merge pull request 'refactor(ci): split release pipeline, add packaging and scanning' (#45) from refactor/split-release-workflows into main
CI / test (push) Successful in 2m24s
CI / lint (push) Successful in 41s
CI / vulncheck (push) Successful in 30s
Release Binaries / release (push) Failing after 22s
Release Docker / docker (push) Has been cancelled
Reviewed-on: #45
2026-06-02 13:24:37 +00:00