Skip to content

feat: configurable time range for the status page heartbeat bar - #7620

Open
zigapovhe wants to merge 103 commits into
louislam:masterfrom
zigapovhe:feat/status-page-history-bar
Open

feat: configurable time range for the status page heartbeat bar#7620
zigapovhe wants to merge 103 commits into
louislam:masterfrom
zigapovhe:feat/status-page-history-bar

Conversation

@zigapovhe

@zigapovhe zigapovhe commented Jul 23, 2026

Copy link
Copy Markdown

Summary

This picks up #5916 by @peaktwilight and finishes it the way we discussed in
that thread. Their commits are included, I built on top of them.

In this pull request, the following changes are made:

  • Status pages get a new setting heartbeat_bar_days (0–365). Default is 0,
    which keeps the current behaviour (last ~100 raw heartbeats). Any other value
    makes the bar cover that many days.
  • For a configured range I aggregate the hourly/daily stats from
    UptimeCalculator into time-anchored buckets (hourly tier up to 30 days, daily
    above that). UptimeCalculator itself is untouched, the bucketing sits in
    server/utils/heartbeat-buckets.js.
  • A bucket is never narrower than the stored tier or the monitor's check
    interval. This is what fixes the grey gaps that killed the previous attempts:
    getDataArray() is sparse, so asking for more slots than the tier stores
    leaves holes. With the cap, a grey beat really means "no data recorded".
  • Beats are single colour for now (down > maintenance > up), as suggested in
    feat(status page): Add configurable heartbeat bar range options (100beats, 1..365 days) #5916. Proportional fill can come in a follow-up.
  • Fewer beats than fit the bar (short ranges, slow monitors) just spread out
    over the full width. 1 day is the sparsest case, it can only ever have 24
    hourly beats. For fine-grained recent history the default 0 is the better
    view anyway.
  • The newest bucket can contain downtime from days ago, so it can't act as
    "current status". The API sends the real latest heartbeat separately and the
    uptime badge / overall status / favicon use that.

Tests: unit tests for the bucketing (tier boundaries, gaps, slow intervals,
empty data, range end) plus an e2e test for the setting, including the server
side clamp.

API changes on /api/status-page/heartbeat/:slug, since it's public:

  • Pages on the default setting: response unchanged, except uptimeList also
    gets a plain <id> key (the <id>_24 keys stay) and there's a new empty
    lastHeartbeatList field.
  • Pages with a configured range: heartbeatList entries are buckets (ping
    is null, time is the bucket end, empty buckets are 0), uptimeList is
    keyed by <id> with the uptime for the range, and lastHeartbeatList has
    the latest real heartbeat per monitor.
  • New optional maxBeats query param (1–100).

Nothing changes for existing installs, the migration defaults everything to 0.

Two pre-existing issues I ran into while testing, not touched here (can open
issues for them): entering edit mode fires an async getStatusPage that can
overwrite form input typed within the first second (this is also why the RSS
custom title e2e sometimes fails on fast local machines), and the uptime badge
doesn't update live in edit mode.

Please follow this checklist to avoid unnecessary back and forth (click to expand)
  • ⚠️ If there are Breaking change (a fix or feature that alters existing functionality in a way that could cause issues) I have called them out
  • 🧠 I have disclosed any use of LLMs/AI in this contribution and reviewed all generated content.
    I understand that I am responsible for and able to explain every line of code I submit.
  • 🔍 Any UI changes adhere to visual style of this project.
  • 🛠️ I have self-reviewed and self-tested my code to ensure it works as expected.
  • 📝 I have commented my code, especially in hard-to-understand areas (e.g., using JSDoc for methods).
  • 🤖 I added or updated automated tests where appropriate.
  • 📄 Documentation updates are included (if applicable).
  • 🧰 Dependency updates are listed and explained.
  • ⚠️ CI passes and is green.

I used Claude as an assistant for this PR. I reviewed everything, I can explain
every line, and I tested it manually (all ranges, resizing, save/clamp round
trip, auto mode) on top of the automated tests.

Screenshots for Visual Changes

Same status page at different heartbeat_bar_days values. Test data has a
3-day monitoring gap, some down days and a maintenance window.

35 days (red = down days, grey = the monitoring gap, blue = maintenance):
status-35d-spread

0 (default, unchanged behaviour) status-auto-before
1 day status-1d
7 days status-7d-clean
90 days status-90d-clean
365 days status-365d-clean

Add a configurable range option (7/30/60/90/180/365 days) for heartbeat bars on status pages.
This allows status page owners to customize how much historical data is shown to visitors.

Changes:
- Add database migration for heartbeat_bar_range_days column
- Update StatusPage model to include heartbeat bar range setting
- Modify heartbeat API endpoint to respect configured range
- Add UI controls in status page editor for range selection
- Update English translations for new settings
- Default to 90 days for backward compatibility

Resolves louislam#1888
- Removed fallback to "auto" for heartbeat bar range in the StatusPage model.
- Simplified heartbeat range logic in the status page router to ensure consistent date handling.
- Updated socket handler to directly assign heartbeat bar range from config.
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for the PR! If anyone would like to help with testing, run: npx kuma-pr zigapovhe:feat/status-page-history-bar (requires Node.js and Docker)

@github-actions

Copy link
Copy Markdown
Contributor

Hello and thanks for lending a paw to Uptime Kuma! 🐻👋
As this is your first contribution, please be sure to check out our Pull Request guidelines.
In particular: - Mark your PR as Draft while you’re still making changes - Mark it as Ready for review once it’s fully ready
If you have any design or process questions, feel free to ask them right here in this pull request - unclear documentation is a bug too.

@zigapovhe
zigapovhe marked this pull request as ready for review July 28, 2026 09:32
@github-actions github-actions Bot added the pr:needs review this PR needs a review by maintainers or other community members label Jul 28, 2026
@mapperCoderZ

Copy link
Copy Markdown

Hello, do you have updates? we are interested in this feature too

@zigapovhe

Copy link
Copy Markdown
Author

@mapperCoderZ and others watching this: the PR is finished and waiting for review. I've been running it myself for a while without problems, but my own testing doesn't count for much here. The maintainers are probably waiting for some independent test reports before picking it up, so that's currently the best way to help:

npx kuma-pr zigapovhe:feat/status-page-history-bar

Needs Node.js and Docker. Try a few day ranges (and switching back to 0), monitors with slow intervals, check that the uptime badge still shows the right thing. And please report back even if everything just works, that's useful info too.

@shafay07

shafay07 commented Aug 25, 2026

Copy link
Copy Markdown

@zigapovhe, thank you for pointing this out. I’ll be rolling this PR out across our existing deployments and will report back with results to help with maintainer acceptance.

@shafay07

shafay07 commented Aug 27, 2026

Copy link
Copy Markdown

Hello @zigapovhe — I tested the PR against an independent copy of one of our existing Uptime Kuma deployments, using a cloned copy of the production SQLite database/config, so the test data is representative of a real deployment.

I tested the Heartbeat Bar Days functionality with:

  • 1 day / 24 hours
  • 7 days
  • 14 days
  • 30 days
  • Changing the value back to 0
  • A monitor with a 600-second interval
  • Existing historical downtime
  • Changing the value in Status Page edit mode and verifying the live preview
  • Saving/discarding changes and reloading the status page
  • Testing values above 365 days

Everything behaved as expected in my testing. The heartbeat bars are now displaying correctly across the different time ranges and available widths. The number of bars initially looked surprising for ranges such as 14 and 30 days, but after checking the behavior it makes sense that the history is aggregated according to the available display width, rather than producing exactly one bar per day.

The 1-day view correctly represents the 24-hour period, while longer ranges are appropriately aggregated to fit the available width. Existing historical uptime/downtime was also preserved correctly.

I also tested the 365-day limit. Values above 365 are not accepted, and the field is constrained/reset to the 365-day maximum.

I didn't encounter any errors or regressions during testing.

I've attached screenshots from the different ranges, including the edit-mode preview and the 365-day validation, for reference.

Test result: ✅ PASS

Thanks for putting this together — the width-related issue appears to be resolved and the heartbeat history is behaving correctly across the tested ranges.

Note: The static logo is not loading in the screenshots because it has not been configured in this test deployment. This is unrelated to the changes in this PR.

📸 Test screenshots

Heartbeat history ranges

Range Screenshot
24 hours 24h
7 days 7d
14 days 14d
30 days 30d

Edit mode / validation

Edit mode

365-day validation

@Xgabi86

Xgabi86 commented Aug 27, 2026

Copy link
Copy Markdown

Hi, I tried out your PR, and it's really great work!
Just one thing, is it normal for test to show as “offline” when the page says everything is online and the sensor has been showing “online” for the past 20 minutes?
And not showing the downtime (eg: 2 minutes)?

Status Page:
image

Dashboard:
image

@mapperCoderZ

mapperCoderZ commented Aug 27, 2026

Copy link
Copy Markdown

It's working on my database, it's approx 1 month of history, installed many months ago and based on kuma 1.23.16, and 30 HTTPS/JSON probes Maybe.

9h :
image
1d :
image
5d :
image

I do not understand if it's a bug, when I Go to "Edit a status page", the latest 3hours disappear. not really anoying by the way, maybe it's by design:

Status page :
image

Edit status page:
image

@zigapovhe

Copy link
Copy Markdown
Author

@shafay07 @mapperCoderZ thanks for testing on real databases, exactly what this PR needed. @mapperCoderZ the disappearing hours in edit mode were a real bug, the bar wasn't resizing when the editor sidebar opens so the newest beats got clipped off the right edge. Fixed and pushed, running npx kuma-pr zigapovhe:feat/status-page-history-bar again should show the full bar now.

@zigapovhe

Copy link
Copy Markdown
Author

@Xgabi86 thanks for trying it out. That's expected with a range configured: each beat covers a whole bucket (a day at 35d), and a bucket with any downtime shows red, so a 2 minute dip from earlier colors the newest beat even while the monitor is up. The overall status and badge use the real latest heartbeat, which is why the page says operational. Proportional fill per bucket is planned as a follow-up.

@zigapovhe
zigapovhe force-pushed the feat/status-page-history-bar branch from 86c4015 to 9d2ba96 Compare September 2, 2026 17:07
@Xgabi86

Xgabi86 commented Sep 2, 2026

Copy link
Copy Markdown

@Xgabi86 thanks for trying it out. That's expected with a range configured: each beat covers a whole bucket (a day at 35d), and a bucket with any downtime shows red, so a 2 minute dip from earlier colors the newest beat even while the monitor is up. The overall status and badge use the real latest heartbeat, which is why the page says operational. Proportional fill per bucket is planned as a follow-up.

Would it be possible to display a brief history of the day's incidents by hovering the mouse over the bar for that day?
For example, for a day with two outages:

 [5:48] - Down for 4 minutes
          Re-UP at 5:52
          
[13:54] - Down for 12 minutes
          Re-UP at 14:06

@zigapovhe

Copy link
Copy Markdown
Author

@Xgabi86 good idea, but that would need a separate endpoint. The bar is drawn from aggregated stats (up/down counts per bucket), so there's no incident timing in the data it gets. Showing outages per day means fetching the status-change heartbeats for that bucket from the status page, which is its own feature. Out of scope here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:needs review this PR needs a review by maintainers or other community members

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Configurable Range heartbeat bar (7/30/60/90/180/365 days)

6 participants