Skip to content

Monitoring: watch your sites and services, not just this server - #85

Merged
jhd3197 merged 12 commits into
mainfrom
dev
Jul 28, 2026
Merged

Monitoring: watch your sites and services, not just this server#85
jhd3197 merged 12 commits into
mainfrom
dev

Conversation

@jhd3197

@jhd3197 jhd3197 commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Monitoring has spent its whole life describing exactly one machine — the one it runs on. Lots of CPU graphs, no way to answer "is the shop actually up?", which is the question most people open a control panel to ask. It turned out the answer was already half-built: StatusComponent was a complete synthetic check — HTTP/TCP/DNS/ping, target, interval, uptime history, auto-incidents — filed under "a component of a status page" and living inside the serverkit-status extension, so watching a site required publishing a status page. This PR promotes it instead of building a second engine: one nullable column, a core service, and the thing you could always half-do becomes the thing the page is about. The bigger surprise was that nothing ever ran the checks — there was no scheduler, so a configured monitor could only notice an outage if you personally clicked "Check now"; the ping probe, separately, was structurally incapable of reporting a failure at all. Also riding along from the same branch: the /cron rebuild, and two nav corrections that took a dev-only console off production installs.

Highlights

  • Monitor anything you point at — a website, an API endpoint, a database port, a mail server or a managed WordPress site — with HTTP, keyword, port, ping, DNS and SMTP checks, each on its own interval with expected status ranges, redirect and TLS switches, and a retry threshold before anything is called down.
  • Checks now actually run on a schedule. Outages open an incident on their own and close it again on recovery.
  • Every monitor gets a detail page: a response-time chart with 1h/24h/7d/30d windows, a 90-day uptime strip, a freezable live check log, and TLS certificate expiry.
  • One Incidents timeline covers both monitor outages and host CPU/memory/disk threshold alerts. Host alerting is unchanged — it just stopped being the only thing Monitoring could talk about.
  • Monitors, Incidents, Events and Jobs are tabs of one Monitoring group. Jobs left the sidebar; the Events stream was rebuilt on the same search-and-filter layout as the rest of the panel, and lost the second header it had been rendering on top of the group's.
  • Status pages now publish monitors you already have, and can drop one again without deleting it.
  • Cron matches the Domains list pattern, and its Last run / Next run / Status columns finally have data behind them.
Technical changes

Promoting monitors

  • Migration 081_monitors_first_class makes status_components.page_id and status_incidents.page_id nullable. The second one matters: a pageless monitor's auto-opened incident has no page either, and without it the outage path raises IntegrityError. Adds is_paused, check_method, expected_status, keyword, follow_redirects, verify_tls, retries, consecutive_failures, cert_issuer, cert_expires_at, cert_checked_at — each backing one surfaced control, nothing speculative.
  • New core app/services/monitor_service.py owns the check engine, moved out of the extension. StatusPageService keeps page publishing, branding, the public JSON and the badge, and delegates check-running so its existing callers and blueprint are unchanged.
  • New app/api/monitors.py at /api/v1/monitors: CRUD, POST /:id/check, POST /:id/pause, GET /:id/history, GET /:id/uptime (per-day buckets), GET /stats, plus incident CRUD. Incidents are core because the scheduler opens them with or without the extension installed.
  • StatusComponent.next_check_at is derived, not stored, so changing an interval takes effect immediately.

The scheduler that never existed

  • builtin.monitor_check added to _BUILTINS on a 30s tick. The tick bounds lateness; MonitorService.due_monitors() decides what is actually due, skipping paused monitors and those driven by the WordPress health sweep. Per-monitor try/except mirrors run_health_checks so one unreachable target can't stall the sweep.
  • run_health_checks no longer reaches through get_installed_extension_attr for the component sync — the engine is core now, so a lean panel keeps its site-bound monitors current.

Check-engine defects fixed while moving it

  • ping called run_command and then unconditionally set status='up', discarding the return code — only an exception could mark it down. It also used Linux-only -c/-W flags with no guard, breaking the Windows dev server.
  • http ignored its own configuration: no method, no expected-status range, no redirect control, verify=True hardcoded.
  • smtp was documented in the column comment but unimplemented and fell through to down; keyword didn't exist. Both implemented — a 200 carrying the wrong body is now an outage, which is the entire point of a keyword check.
  • run_check never recomputed uptime and never opened or resolved an incident; only the WordPress sync_component_from_health path did. Both now funnel through one _record so a network probe and a health verdict produce identical bookkeeping.
  • TLS issuer and expiry are captured on https probes, throttled to one read per 6h on its own cert_checked_at — gating on last_check_at meant an active 30s monitor's probe clock was never stale, so the certificate would be read once and never refreshed.
  • MonitorService.recent_response_times feeds the list sparkline from one bulk query per page rather than one per row, bounded by a time window so a busy monitor's history can't crowd out a quiet one's the way a single global LIMIT would.

Frontend

  • New pages/Monitors.jsx on the Domains/Cron pattern: clickable MetricCard KPIs as the status filter, SearchField + FilterDrawer published to the group bar via useTopbarActions, DataTable with a Sparkline response column and a live next-check countdown.
  • New pages/MonitorDetail.jsx at /monitoring/monitors/:id, deliberately outside the tab group (like /queue/:group/:queue) so it carries its own breadcrumb PageTopbar. Its sections are a SegControl, not a second tab strip.
  • New components/monitoring/UptimeBars.jsx — a day with no samples renders as its own --none state rather than a full green bar, so "we weren't looking" and "it was fine" stay distinguishable.
  • New pages/Incidents.jsx merges monitor incidents with getAlertHistory/active_alerts into one sorted timeline, with an incident drawer carrying the update timeline and a post-an-update form. FleetAlertsPanel moved here and keeps its own ack/resolve lifecycle.
  • Telemetry.jsx rewritten: dropped the <PageTopbar> it was rendering inside TabGroupLayout (the double header), telemetry-stat divs → KpiBand/MetricCard, inline collapsible filters → topbar SearchField + FilterDrawer, list → DataTable, bespoke drawer → DS Drawer. Correlation drill-down, every filter, test-event emit, cleanup and pagination all retained.
  • MONITOR_TABS becomes Overview / Monitors / Incidents / Rules / Capacity / Events / Jobs / Doctor. Alerts is gone as a tab; /monitoring/alerts and /monitoring/fleet-alerts redirect to Incidents. Jobs moves to /monitoring/jobs with /jobs redirecting, loses its sidebarItems entry, and its Activity/Scheduled sub-nav becomes an in-page SegControl; jobsTabs.jsx is deleted.
  • MonitoringOverview gains a MonitorsSummary section above host health — availability KPIs, a live check feed and a monitor list. The fleet grid's heading renames to "All hosts" so it doesn't collide.
  • StatusPages.jsx gains attach/detach against the core monitors API, because deleting a component now deletes a real monitor and its history — unpublishing needed its own action.
  • FilterDrawer grows children and onClear. Without onClear, "Clear all" resets only the chip groups and silently leaves host-supplied fields applied.
  • _monitoring.scss's root selector list widened to the new page roots so they inherit the shared mon-* vocabulary instead of duplicating it; new _monitors.scss; _telemetry.scss rewritten against the new markup.
  • Severity config split into tone (MetricCard) and pill (Pill). The two token sets overlap but aren't identical — Pill has no accent, so info events had been rendering with no chip at all.

Also in this branch

  • /cron rebuilt on the Domains pattern, and cron_service.list_jobs() gained the schedule_human / next_run / run-tracking joins jobs_for_application() had carried since the run-history work — without them the admin list could only ever show enabled-vs-disabled.
  • Test Sandbox dev-gated behind a shared useDevMode() hook on both the sidebar item and the route, so a visible nav entry can never redirect to the dashboard; the distro-matrix console had been shipping visible on every production install.
  • Inbound Webhooks moved to Settings → Admin next to the outbound subscriptions it complements, with /webhooks and /secrets/webhooks redirecting.
  • The retired .page-header layout deleted along with the overrides that outlived it, and the StyleGuide section that kept recommending it.

jhd3197 and others added 12 commits July 27, 2026 22:44
Two sidebar surfaces that shouldn't have been top-level pages. Both
touch App.jsx + sidebarItems.js, so they land together.

Test Sandbox: the distro-matrix console shipped visible and routable
on every production install. Add a shared useDevMode() hook
(module-scope cache + listeners, same shape as useModules) resolving
`import.meta.env.DEV || (isAdmin && dev_mode)`. The sidebar item gates
on requiresCondition: 'devMode' and the route on the same hook, so a
visible nav entry can never redirect to the dashboard. The guard waits
on `resolved` first, or an admin deep link would bounce before the
dev_mode fetch lands. Settings' toggle calls refreshDevMode() so the
sidebar reacts without a reload.

Webhooks: the inbound receive/verify/forward console is server
configuration, not a daily ops surface, so it becomes a Settings ->
Admin tab next to the outbound subscriptions it complements. /webhooks
and /secrets/webhooks redirect there; the sidebar item is gone, which
empties ADVANCED_ITEM_IDS down to the queue console. The command
palette entry drops its navId (a Settings tab has no nav item for the
workspace permission gate to consult) and settingsIndex.js gains the
three entries check-settings-index.mjs requires for a new tab. The
native confirm() on delete becomes the shared useConfirm() dialog.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The page kept a shape the rest of the app moved off: a four-card KPI
strip, its own in-body search input, a hand-rolled table, four icon
buttons per row, and an edit modal on row click.

It now matches /domains: the segmented filter carries the counts (no
KPI strip), SearchField lives in the top bar, the list is the shared
DataTable, and a row opens the DS Drawer. The top bar keeps its title
— /cron has no tab group, and the reference mock shows a titled bar
there; the body was what was stale.

Last run / Next run / Status columns need data list_jobs() never
returned: it skipped the schedule_human / next_run / run-tracking joins
that jobs_for_application() has done since plan 34, so the admin list
could only ever show enabled-vs-disabled. Apply the same enrichment,
with tests covering the schedule join, the last-run join, and the
no-history case.

The drawer's run history uses GET /cron/jobs/<id>/runs, which already
existed and only ScheduledTasksCard consumed. It's admin-only and
empty until a job carries the tracking shim, so both cases are stated
in the drawer (with an enable-tracking action) rather than rendering a
blank table.

Also fixes styles that never applied: .run-output-*, .spinner-inline
and the modal field overrides were nested under .cron-page, but Radix
Dialog and Sheet portal to <body>. Hoisted to top level.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
With /cron, /webhooks and /test-sandbox off it, the old
<div class="page-header"><h1>...</h1></div> block had one live user
left: Docker's Docker-not-installed branch, where the panel below it
already supplies its own heading. Remove that, and the class is dead.

Delete the base rule and the overrides that outlived it in
_docker.scss, _marketplace.scss, _wordpress.scss,
_wordpress-pipeline.scss and components/_tabs.scss, plus the unused
.docker-page-header. The base rule's slot keeps a comment saying what
replaced it.

StyleGuide's "Page Headers" section still described this as "the
canonical pattern, use this for all pages", which is how it kept
getting picked for new pages. It now demos the two live shapes — the
titleless TabGroupLayout bar (the default) and the titled standalone
bar — and names .page-header as retired. StyleGuide's own header moves
to PageTopbar.

Docker and the Database Explorer intentionally carry no page bar at
all; that is unchanged and deliberate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A StatusComponent was already a full synthetic check (http/tcp/dns/smtp/ping,
target, interval, timeout, HealthCheck history, uptime windows, auto-incidents)
— it was just framed as "a component of a status page" and its engine lived
inside the serverkit-status extension, so watching a site required publishing a
status page. Promote it:

- migration 081 makes status_components.page_id and status_incidents.page_id
  nullable, and adds the columns backing the monitor UI (is_paused,
  check_method, expected_status, keyword, follow_redirects, verify_tls, retries,
  consecutive_failures, cert_issuer, cert_expires_at)
- new core app/services/monitor_service.py owns the check engine; the extension
  keeps page publishing and delegates
- new /api/v1/monitors (CRUD, check, pause, history, uptime buckets, incidents)
- new builtin.monitor_check sweep on a 30s tick — until now checks only ran when
  someone pressed "Check now", so a configured monitor never noticed an outage

Check-engine defects fixed while moving it:

- ping ignored the process return code and could only ever report 'up'
- ping used Linux-only flags with no guard, breaking the Windows dev server
- http ignored its own method/expected_status/follow_redirects/verify_tls
- smtp was documented on the column but unimplemented; keyword did not exist
- run_check never recomputed uptime and never opened or resolved an incident —
  only the WordPress health-sync path did, so network monitors never paged
- TLS issuer/expiry are now captured (throttled to one read per 6h)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Monitoring only ever described the machine the panel runs on. These are the
screens for the things it is supposed to be watching:

- /monitoring/monitors — the check list on the Domains/Cron pattern: clickable
  KPI tiles as the status filter, topbar SearchField + FilterDrawer, and a table
  with a response sparkline, 30-day uptime and a live next-check countdown.
  Creating a monitor picks a check type (HTTP / keyword / port / ping / DNS /
  SMTP) and its probe options.
- /monitoring/monitors/:id — the drill-down, outside the tab group so it carries
  its own breadcrumb header. Sections are a SegControl, not a second tab strip:
  Performance (p50 chart + stat strip), Uptime (90-day bars + certificate),
  Check log (freezable so a streaming table stays readable), Configuration.
- /monitoring/incidents — absorbs the old Alerts tab. A CPU threshold crossing
  and a monitor going down are the same question asked of two subjects, and
  splitting them is what made alerting look like it only described this server.
  Host threshold alerting is unchanged, just rendered in the same timeline.
- UptimeBars renders a day with no samples as its own "unwatched" state rather
  than a full green bar — "we weren't looking" and "it was fine" are different
  facts, and conflating them is how uptime widgets lie about their history.

The list sparkline is fed by one bulk query for the whole page, not one per row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The group's information architecture had drifted. The Events tab pointed at
/telemetry, which was nested inside the monitoring TabGroupLayout but ALSO
rendered its own PageTopbar — two stacked headers. Jobs sat as a top-level
sidebar entry while being the same class of thing as Events, so the two read as
rival pages.

- MONITOR_TABS is now Overview / Monitors / Incidents / Rules / Capacity /
  Events / Jobs / Doctor (+ Status Pages from the extension). Alerts is gone as
  a tab; /monitoring/alerts and /monitoring/fleet-alerts redirect to Incidents,
  and FleetAlertsPanel moved there with its own ack/resolve lifecycle intact.
- Jobs moves to /monitoring/jobs and loses its sidebar entry; /jobs redirects.
  Activity/Scheduled becomes an in-page SegControl since the group's bar already
  owns the tab row. JOBS_TABS is deleted.
- Events (Telemetry) rebuilt on the standard pattern: no PageTopbar of its own,
  KpiBand + MetricCard severity filters instead of bespoke telemetry-stat divs,
  topbar SearchField + FilterDrawer instead of an inline collapsible panel,
  DataTable + DS Drawer. Every capability is kept — correlation drill-down, all
  filters, test-event emit, cleanup, pagination.
- The Overview leads with a monitors summary (availability, live check feed,
  monitor list); host resources keep everything they had, one section down.
- Status pages can now publish a monitor that already exists, and detach it
  again without destroying it — deleting a component now deletes a real monitor.

FilterDrawer grows `children` + `onClear` so a host can add non-list filters
(free text, date bounds) and still have "Clear all" reach them; without onClear
it would silently reset only the chip groups.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
_maybe_attach_certificate throttled on last_check_at, which is the probe clock.
An active monitor is checked every 30s, so that condition was never older than
the 6h refresh window: the certificate would be read once and then never
refreshed, leaving a stale expiry date on the detail page forever.

Adds cert_checked_at (migration 081) and throttles on that instead. The stamp
is written even when the read fails, so an unreachable TLS endpoint is retried
on the same cadence rather than on every single probe.

Also serves the Monitors list sparkline from one bulk query per page instead of
one per row, bounded by a time window so a busy monitor's history cannot crowd
out a quiet one's the way a single global LIMIT would.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Walks the AST of monitor_service and the monitors blueprint to assert neither
imports the plugin loader nor reaches for StatusPageService, and that the routes
register without the extension. This is the property the promotion exists to
provide — watching a site must keep working on a lean panel with
serverkit-status uninstalled — so it should fail loudly if it regresses.

AST rather than a source grep: the modules' own comments discuss the extension,
which a text match would trip over.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- The Events severity chip passed a MetricCard tone to Pill. The two token sets
  overlap but are not the same — Pill has no "accent", so info events rendered
  as bare text with no chip while every other severity had one. Split the config
  into `tone` (tile) and `pill` (chip).
- "128 ms" wrapped onto two lines in the Monitors table's narrow response
  column.

Both were only visible in a rendered page, not in lint or the build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Monitors, a monitor's detail and 90-day uptime strip, Incidents, Events and
Jobs. These double as the render smoke test for the pages this round added.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 28, 2026 05:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Promotes the existing status-page “components” synthetic-check engine into core, first-class Monitors that run on a scheduler, auto-open/resolve incidents, and surface new Monitoring UI (Monitors list, Monitor detail, unified Incidents timeline), while also restructuring Monitoring navigation and rebuilding /cron to match the Domains pattern.

Changes:

  • Backend: promote monitors into core (MonitorService + /api/v1/monitors), add a 30s builtin scheduler sweep, and migrate schema to support pageless monitors/incidents and new probe options.
  • Frontend: add Monitors/MonitorDetail/Incidents pages, refactor Monitoring tabs + Jobs/Webhooks/TestSandbox navigation, and update shared DS primitives (FilterDrawer, styles).
  • Cron: enrich cron_service.list_jobs() data to support “Last run / Next run / Status” in the admin list UI.

Reviewed changes

Copilot reviewed 61 out of 68 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
VERSION Bumps version to 1.7.75.
ROADMAP.md Documents monitors + monitoring navigation as recently completed.
frontend/src/styles/pages/_wordpress.scss Removes legacy .page-header styling from WordPress page styles.
frontend/src/styles/pages/_wordpress-pipeline.scss Removes legacy .page-header styling from WP pipeline page styles.
frontend/src/styles/pages/_test-sandbox.scss Removes unused .ts-intro styling.
frontend/src/styles/pages/_telemetry.scss Rewrites Telemetry/Events styles for DS primitives + new markup.
frontend/src/styles/pages/_secrets.scss Extends shared secrets styles to Settings→Admin Webhooks tab container.
frontend/src/styles/pages/_monitors.scss Adds full styling for monitors list/detail/incidents + uptime strip.
frontend/src/styles/pages/_monitoring.scss Broadens shared monitoring root selectors for mon-* vocabulary.
frontend/src/styles/pages/_marketplace.scss Removes legacy .page-header styles from Marketplace page.
frontend/src/styles/pages/_jobs.scss Adds in-page Activity/Scheduled view switch styling for Jobs tab.
frontend/src/styles/pages/_docker.scss Removes legacy page-header CSS; updates workspace comment wording.
frontend/src/styles/pages/_cron.scss Rebuilds Cron page styling (topbar search, table widths, drawer/modal rules).
frontend/src/styles/main.scss Imports new _monitors.scss.
frontend/src/styles/layout/_main-content.scss Deletes global .page-header styles; documents PageTopbar as replacement.
frontend/src/styles/components/_tabs.scss Updates .tab-embedded header-hiding rules to remove .page-header selectors.
frontend/src/styles/components/_design-system.scss Adds FilterDrawer “extra fields” styling block.
frontend/src/services/api/monitors.js Adds frontend API methods for monitors + monitor incidents.
frontend/src/services/api/index.js Registers the new monitors API module in ApiService.
frontend/src/pages/TestSandbox.jsx Removes intro paragraph (now dev-gated elsewhere).
frontend/src/pages/StyleGuide.jsx Migrates StyleGuide header + docs from .page-header to PageTopbar.
frontend/src/pages/StatusPages.jsx Adds attach/detach existing monitors to status pages; updates actions and modal.
frontend/src/pages/Settings.jsx Adds Settings→Admin Webhooks tab and refreshes dev-mode cache on change.
frontend/src/pages/Monitors.jsx Adds Monitors list page with KPIs, filters, polling, and create drawer.
frontend/src/pages/Monitoring.jsx Removes Alerts tab; routes users to Incidents conceptually.
frontend/src/pages/MonitorDetail.jsx Adds monitor drill-down page (performance/uptime/log/config) with polling.
frontend/src/pages/Jobs.jsx Moves Jobs into Monitoring group and replaces tab strip with SegControl switch.
frontend/src/pages/Incidents.jsx Adds unified incidents + host alerts timeline with drawer and update posting.
frontend/src/pages/Docker.jsx Removes legacy .page-header rendering from Docker page.
frontend/src/hooks/useDevMode.js Introduces shared dev-mode flag + cache for sidebar and route guarding.
frontend/src/data/settingsIndex.js Adds command-palette entries for inbound webhooks under Settings→Webhooks tab.
frontend/src/data/palettePages.js Updates command palette page routes (Jobs/Monitors/Incidents + settings webhooks).
frontend/src/components/sidebarItems.js Moves Jobs into Monitoring; removes Webhooks sidebar item; gates Test Sandbox by devMode.
frontend/src/components/Sidebar.jsx Wires useDevMode() into sidebar runtime conditions.
frontend/src/components/settings/WebhooksTab.jsx Converts inbound Webhooks page into Settings tab content + confirm dialog hook.
frontend/src/components/organization/organizationTabs.jsx Updates comments re: inbound webhooks location.
frontend/src/components/monitoring/UptimeBars.jsx Adds 90‑day uptime strip component with “none” state semantics.
frontend/src/components/monitoring/monitorTabs.jsx Updates Monitoring group tabs (Monitors/Incidents/Jobs) and narrative comments.
frontend/src/components/monitoring/MonitorsSummary.jsx Adds monitors KPIs + feed + list section to Monitoring overview.
frontend/src/components/monitoring/monitorShared.jsx Centralizes monitor status/type vocab + incident constants.
frontend/src/components/monitoring/MonitoringOverview.jsx Adds MonitorsSummary and adjusts section headings (“All hosts”).
frontend/src/components/monitoring/FleetAlertsPanel.jsx Updates comments to reflect Incidents tab hosting.
frontend/src/components/jobs/jobsTabs.jsx Deletes obsolete Jobs tab-strip definitions.
frontend/src/components/ds/FilterDrawer.jsx Adds children + onClear to support host-supplied extra filter fields.
frontend/src/components/dashboard/widgets/renderers.jsx Updates Jobs quick action route to /monitoring/jobs.
frontend/src/components/backups/ProtectionPanel.jsx Updates “View Jobs” navigation to /monitoring/jobs.
frontend/src/components/backups/BackupDetailDrawer.jsx Updates job link to /monitoring/jobs.
frontend/src/App.jsx Adds monitors/incidents/jobs routes under Monitoring group, adds DevOnlyRoute guard, redirects old routes.
builtin-extensions/serverkit-status/backend/status_page_service.py Delegates check/incident/component operations to core MonitorService; expands public-hidden fields.
backend/tests/test_monitor_service.py Adds extensive MonitorService coverage (status parsing, probes, scheduler, incidents, cert logic).
backend/tests/test_jobs.py Updates builtin schedule counts and asserts new builtin.monitor_check exists.
backend/tests/test_cron_runs.py Adds tests for CronService.list_jobs() enrichment (next_run/last_run/status joins).
backend/migrations/versions/081_monitors_first_class.py Makes page_id nullable for components/incidents and adds monitor option columns.
backend/app/services/cron_service.py Enriches list_jobs() with schedule_human/next_run/last-run tracking.
backend/app/models/status_page.py Makes page_id nullable, adds monitor option columns, and adds derived next_check_at.
backend/app/jobs/builtin_handlers.py Adds run_monitor_checks() and registers builtin.monitor_check on a 30s tick; updates WP health sync.
backend/app/api/monitors.py Adds core monitors API (CRUD, check, pause, history, uptime, stats, incidents).
backend/app/init.py Registers the monitors blueprint under /api/v1/monitors.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +151 to +161
def update(monitor_id, data):
monitor = StatusComponent.query.get(monitor_id)
if not monitor:
return None
if 'check_type' in data and data['check_type'] not in StatusComponent.CHECK_TYPES:
raise ValueError(f"Unknown check type: {data['check_type']}")
for field in MonitorService.WRITABLE_FIELDS:
if field in data:
setattr(monitor, field, data[field])
db.session.commit()
return monitor
Comment on lines +49 to +58
@monitors_bp.route('', methods=['POST'])
@monitors_bp.route('/', methods=['POST'])
@jwt_required()
def create_monitor():
data = request.get_json() or {}
try:
monitor = MonitorService.create(data)
except ValueError as e:
return {'error': str(e)}, 400
return jsonify(monitor.to_dict()), 201
@jhd3197
jhd3197 merged commit 3cfc62d into main Jul 28, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants