Spec 023 — Website monitor MVP (CRUD + manual probe + check history)#70
Merged
Conversation
Phase 5 starts here. Folder + README + spec markdown for the data layer (websites + website_checks tables, WebsiteProbeAction + RecordWebsiteCheckAction) plus CRUD UX and a sync manual "Probe now". Spec 024 will add the scheduler + uptime calc + activity events; spec 025 wires the Overview KPI + Reverb live updates.
First spec of phase 5. Stand up website uptime monitoring data layer
plus a working CRUD UX with a sync manual "Probe now" that records a
real HTTP check.
- New `websites` + `website_checks` tables. Two enums:
WebsiteStatus (includes `pending`) and WebsiteCheckStatus (Up/
Down/Slow/Error only — checks only exist after a probe ran).
- WebsiteProbeResult DTO carries the probe outcome between the pure
RunWebsiteProbeAction (HTTP, no DB) and the
RecordWebsiteCheckAction (persistence). Probe classifies:
Up → request succeeded with expected_status_code
Slow → success but response_time_ms > 3000ms (hard threshold
phase-1; per-website config is future polish)
Down → request succeeded but status code mismatched
Error → DNS / TCP / TLS / timeout / connection refused
Slow is treated as a successful run for the parent
Website.last_success_at update.
- Catch list narrowed to ConnectionException|RequestException —
programmer bugs (typo, future enum drift, OOM) bubble up loudly
instead of getting silently classified as "site down."
- WebsitePolicy gates create/update/delete/probe to project owners;
any verified user can view (matches RepositoryPolicy phase-1
conventions; cross-tenant uniform fix when teams ship).
- WebsiteController resourceful + WebsiteProbeController single-
action sync probe. Sync because the user clicked the button —
they want the result. Spec 024's scheduler is where async becomes
the natural mode; the same RecordWebsiteCheckAction is reused on
both paths.
- Form requests StoreWebsiteRequest + UpdateWebsiteRequest validate
field shapes; project_id is intentionally NOT editable post-create
(moving a website between projects would orphan its check history).
WebsiteController::store re-authorises via the policy after
validation (belt-and-suspenders, matches RepositoryController).
- Routes nested under /monitoring/* so phase-6 hosts can sit beside
it as /monitoring/hosts/*. Sidebar `Monitoring` entry flipped
from disabled → routeName: monitoring.websites.index.
- 4 Vue pages: Index (cross-project list), Create + Edit (form),
Show (header with Probe now / Edit / Delete + recent-checks list).
Tests: 28 new tests across 5 files (probe action, record action,
policy, controller, probe controller). 19 net new passing tests;
full suite 310 passed (was 291). The 10 new env-CSRF POST/PATCH/
DELETE failures are the same baseline pattern affecting every POST
test in the repo locally — CI passes them.
Self-review pass via superpowers:code-reviewer; addressed all 3
recommendations (narrowed catch list, belt-and-suspenders authorize,
column comment on response_time_ms clarifying it's wall-clock not
server-reported TTFB).
Spec 024 will add the scheduler (DispatchDueWebsiteChecksJob +
RunWebsiteCheckJob), uptime % calculation, and activity event
creation on status transitions. Spec 025 wires the Overview KPI
widget + Reverb live updates.
Spec 023's create() called authorize('create', [Website::class, null])
when no ?project_id= was provided, returning 403. The form needs to
open so the user can pick one of their own projects from the dropdown
— store() re-authorises at submit time once project_id is in the
request.
Coarsen the page-level gate to viewAny (any verified user) and demote
the preselect lookup to a soft-narrow: a foreign or non-existent
project ID drops to null instead of 403'ing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #69
Spec: specs/phase-5-monitoring/023-website-monitor-mvp.md
First spec of phase 5. Stand up the data layer for website uptime monitoring plus a working CRUD UX with a sync manual "Probe now" that records a real HTTP check. Scheduler is spec 024; Overview KPI integration + Reverb live updates ride spec 025.
Summary
websites+website_checkstables.WebsiteStatus(includespending),WebsiteCheckStatus(Up/Down/Slow/Error only — checks only exist after a probe ran).WebsiteProbeResultimmutable DTO +RunWebsiteProbeAction(pure HTTP, no DB writes; classifies up/slow/down/error against a 3000ms hard threshold) +RecordWebsiteCheckAction(persists a check, updatesWebsite.last_*, treatsSlowas success).WebsitePolicygates create/update/delete/probe to project owners; any verified user can view (matchesRepositoryPolicyphase-1 conventions — see "Known limitations" below).WebsiteControllerresourceful CRUD +WebsiteProbeControllersingle-action sync probe. Routes nested under/monitoring/*so phase-6 hosts can sit beside it. SidebarMonitoringentry flipped from disabled → linked.Test plan
vendor/bin/pint --testpasses.php artisan test— 19 net new passing tests across 5 files (probe action 6, record action 6, policy 4, controller 9 / 3 new GET pass; 6 POST/PATCH/DELETE hit the env-CSRF baseline; probe controller 3 — all POST, env-CSRF). Full suite 310 passed (was 291). The 10 new local failures are the same env-CSRF (419) baseline affecting every POST test in the repo; CI passes them.npm run buildclean.https://example.com, observe it lands on the Show page; click "Probe now", observe a check row + status update; edit + delete flows.Self-review notes
Self-review pass via
superpowers:code-reviewer; addressed all 3 recommendations:ConnectionException|RequestExceptionso programmer bugs (typo, future enum drift, OOM) bubble up loudly instead of getting silently classified as "site down."WebsiteController::storeafter validation — mirrorsRepositoryController::store, prevents a stale prop from slipping past.response_time_msclarifies it's wall-clock (DNS / TCP / TLS / send / receive), not server-reported TTFB. Future timing fields (per spec 023's "Out of scope" + roadmap §8.8 "Later") will sit alongside.Known limitations
viewparity: any verified user canviewany website by ID — same single-tenant gap asRepositoryPolicy. Theindexquery scopes correctly; theshowroute doesn't tighten beyond verified-auth. Uniform fix when teams ship.response_time_msis wall-clock. Per-leg DNS / TCP / TLS / TTFB timings land in a future spec per §8.8's "Later" list.Phase 5 progress after merge