heartwood every commit a ring

Refresh home cards and restore date-based changelog

ff837987 by Isaac Bythewood · 2 days ago

Refresh home cards and restore date-based changelog

Drop the "capabilities" eyebrow and rewrite each home card for accuracy
and brevity. Replace the single v1.0 changelog card with the original
date+bullet history (carried over from the Django era) plus a new
2026-05-09 entry covering the rust rewrite and its follow-up fixes.
modified templates/pages/changelog.html
@@ -1,5 +1,9 @@{% extends "base.html" %}{% block extra_css %}<link rel="stylesheet" href="{{ vite_asset('static_src/pages/index.js', 'css') }}">{% endblock %}{% block breadcrumbs %}<nav aria-label="breadcrumb">  <ol class="breadcrumb mb-0">
@@ -10,24 +14,167 @@{% endblock %}{% block main %}<div class="container my-4">  <div class="row mb-4">    <div class="col-12">      <div class="section-label mb-1">project</div>      <h1 class="fw-bolder text-white" style="letter-spacing: -0.01em;">Changelog</h1>      <p class="text-muted mb-0 small">An ongoing record of what's shipped.</p><div class="container my-5">  <div class="row">    <div class="col-lg-8 offset-lg-2">      <div class="section-label mb-2">release log</div>      <h1 class="fw-bolder text-white" style="letter-spacing: -0.01em;">{{ title }}</h1>      <p class="text-muted">Shipped changes, incidents, and the occasional tease for something coming.</p>    </div>  </div>  <div class="bg-surface border-subtle rounded border p-4">    <h2 class="h5 text-white fw-bolder">v1.0 · Rust rewrite</h2>    <ul class="text-muted small">      <li>Single-binary axum service replaces the Django + scheduler pair.</li>      <li>Single-password operator auth replaces the multi-user accounts model.</li>      <li>SEO crawler ported to reqwest + scraper. Lighthouse still shells out to the npm CLI.</li>      <li>Email alerts use direct-MX delivery via lettre + hickory-resolver. Discord webhooks use plain HTTP POST.</li>      <li>Migration subcommand: <code>./status migrate &lt;django.sqlite3&gt;</code> preserves Property UUIDs.</li>    </ul>  <div class="row mt-4">    <div class="col-lg-8 offset-lg-2">      <div class="changelog-entry">        <div class="changelog-date">2026-05-09</div>        <ul>          <li>Rewrite the entire backend from Django to a single statically-linked axum binary in Rust. sqlx + SQLite directly, minijinja for templates, no more Gunicorn or separate scheduler process. Property UUIDs are preserved so existing public status URLs keep working</li>          <li>Drop the multi-user accounts model. Auth is now a single operator password from <code>.env</code> with signed-cookie sessions and <code>SameSite=Strict</code> instead of CSRF tokens</li>          <li>Port the SEO crawler from <code>requests</code> + <code>BeautifulSoup</code> to <code>reqwest</code> + <code>scraper</code>. The 38 ranked checks (SEO, links, sitemap, accessibility, content, performance, security) match the original Python output</li>          <li>Switch alert email to <code>lettre</code> + <code>hickory-resolver</code>, still opportunistic STARTTLS direct-to-MX on port 25; Discord webhooks are plain HTTP POST</li>          <li>Add a <code>./status migrate &lt;django.sqlite3&gt;</code> subcommand that imports an existing Django SQLite into the new schema while preserving Property UUIDs</li>          <li>Run the lighthouse CLI through <code>bun run --bun</code> so the runtime image drops nodejs and npm; chromium stays for lighthouse only</li>          <li>Probe Content-Encoding with a non-decompressing client so <code>response_ms</code> reflects the wire payload, not the decoded body</li>          <li>Fix the mobile nav menu being clipped by the fixed navbar height</li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2026-04-26</div>        <ul>          <li>Drop SQLite <code>mmap_size</code> to avoid a multi-process corruption hazard</li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2026-04-18</div>        <ul>          <li>Centralize SQLite settings and expand PRAGMA tuning (WAL, synchronous, cache, mmap, temp_store)</li>          <li>Commit alert state before firing notifications so a crash mid-alert can't cause duplicate sends</li>          <li>Harden the chromium wrapper with timeouts, <code>/tmp</code> temp files, and <code>--headless=new</code></li>          <li>Redesign alert emails to match the warm-earth site palette</li>          <li>Stop wedge-reset from failing a healthy queued backlog of checks</li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2026-04-17</div>        <ul>          <li>Redesign the whole dashboard with a warm-earth palette: mossy forest green and amber on deep charcoal, Monaspace Argon throughout</li>          <li>Rework the property page with live-signals tiles, Lighthouse score cards, styled Chart.js panels, and compact insight tables</li>          <li>Add recent-uptime bar strips and rolling uptime percentage to every property row on the dashboard</li>          <li>Surface the full Lighthouse performance breakdown: weighted metric tiles and top savings opportunities, ranked by impact</li>          <li>Swap the rocket emoji favicon for a themed SVG tile matching the in-app logo</li>          <li>Update the chromium wrapper to also pick up <code>google-chrome</code> so the webdev container runs locally without extra setup</li>          <li>Fix logout 405 by switching to a POST form + CSRF (Django 5 <code>LogoutView</code> is POST-only)</li>          <li>Refactor the scheduler with thread pools and graceful shutdown</li>          <li>Collapse the web and scheduler services into a single container via a Python supervisor in <code>entrypoint.py</code></li>          <li>Replace the Exim relay with a Python direct-to-MX email backend</li>          <li>Set <code>PYTHONUNBUFFERED</code> so scheduler output reaches <code>docker logs</code></li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2026-04-16</div>        <ul>          <li>Add live crawl/lighthouse status panel with real-time recrawl and rerun buttons</li>          <li>Rewrite the SEO crawler in-process using <code>requests</code> + <code>BeautifulSoup</code>, no more Scrapy subprocess</li>          <li>Harden the scheduler, alert state machine, and crawl pipeline against races and partial failures</li>          <li>Harden the Lighthouse runner and surface failure reasons in the UI</li>          <li>Migrate the frontend build from Webpack/Yarn to Vite/Bun</li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2026-04-09</div>        <ul>          <li>Migrate Python package management from Pipenv/pyenv to <code>uv</code></li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2026-04-03</div>        <ul>          <li>Bind the Docker port to localhost only, host should proxy in</li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2025-06-01</div>        <ul>          <li>Refactor property email templates to share a base</li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2025-05-31</div>        <ul>          <li>Improve alert system with state tracking and recovery notifications, no more alert spam on flapping sites</li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2022-07-25</div>        <ul>          <li>Add recrawl button</li>          <li>Run crawler weekly on a schedule</li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2022-07-23</div>        <ul>          <li>Add crawler insights: title, description, canonical, OG tags, H1 extraction per page</li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2022-07-16</div>        <ul>          <li>Add docker init to handle Lighthouse Chrome zombies</li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2022-07-15</div>        <ul>          <li>Add check queue to be more resource friendly</li>          <li>Add Lighthouse scores</li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2022-07-04</div>        <ul>          <li>Add properties listing pagination</li>          <li>Add properties listing search</li>          <li>Improve properties listing UI</li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2022-06-26</div>        <ul>          <li>Add HSTS monitoring</li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2022-06-25</div>        <ul>          <li>Add uptime chart</li>        </ul>      </div>      <div class="changelog-entry">        <div class="changelog-date">2022-06-19</div>        <ul>          <li>Create status project</li>        </ul>      </div>    </div>  </div></div>{% endblock %}
modified templates/pages/home.html
@@ -71,7 +71,6 @@<section class="container my-5 py-4">  <div class="row mb-4">    <div class="col-12">      <div class="section-label mb-2">capabilities</div>      <h2 class="h3 text-white fw-bolder" style="letter-spacing: -0.01em;">        Everything your infrastructure team needs in one dashboard.      </h2>
@@ -82,42 +81,42 @@      <div class="feature">        <div class="feature-label">uptime</div>        <div class="feature-title">Live HTTP probes</div>        <p class="feature-desc">Three-minute intervals, two-strike alert debouncing, SSL and timeout mapping. Discord + SMTP notifications only fire on real state transitions.</p>        <p class="feature-desc">Three-minute checks with two-strike debouncing. Email and Discord fire only on real state transitions, never on flaps.</p>      </div>    </div>    <div class="col-12 col-md-6 col-lg-4">      <div class="feature">        <div class="feature-label">performance</div>        <div class="feature-title">Lighthouse audits</div>        <p class="feature-desc">Daily headless runs surface performance, accessibility, best practices, and SEO scores with weighted metric breakdowns.</p>        <p class="feature-desc">Daily headless runs scoring performance, accessibility, best practices, and SEO. Top weighted metrics and savings opportunities ranked by impact.</p>      </div>    </div>    <div class="col-12 col-md-6 col-lg-4">      <div class="feature">        <div class="feature-label">security</div>        <div class="feature-title">Header analysis</div>        <p class="feature-desc">HTTPS, HSTS, HSTS preload, XSS and content-sniffing protection, clickjack defence, and server-version leak checks.</p>        <p class="feature-desc">HTTPS, HSTS, HSTS preload, XSS and content-sniffing protection, clickjack defence, and server-version leak checks on every probe.</p>      </div>    </div>    <div class="col-12 col-md-6 col-lg-4">      <div class="feature">        <div class="feature-label">crawler</div>        <div class="feature-title">Weekly SEO spider</div>        <p class="feature-desc">Extracts title, description, canonical, OG tags, and H1 per page — every mismatch becomes a severity-ranked insight.</p>        <p class="feature-desc">In-process spider runs weekly. 38 ranked checks across SEO, links, sitemap, accessibility, content, performance, and security.</p>      </div>    </div>    <div class="col-12 col-md-6 col-lg-4">      <div class="feature">        <div class="feature-label">sharing</div>        <div class="feature-title">Public status pages</div>        <p class="feature-desc">Toggle a property public and share the URL — no account required, customers see live status, response graphs, and uptime at a glance.</p>        <p class="feature-desc">Flip a property public and share the URL. Customers see live status, response graphs, and uptime at a glance, no account required.</p>      </div>    </div>    <div class="col-12 col-md-6 col-lg-4">      <div class="feature">        <div class="feature-label">ownership</div>        <div class="feature-title">Single binary, single container</div>        <p class="feature-desc">Rust + axum + SQLite. Ships as a Docker Compose stack. Your data, your ingress, your retention policy. No SaaS vendor lock-in.</p>        <p class="feature-desc">Rust + axum + SQLite, shipped as one Docker Compose stack. Your data, your ingress, your retention policy. No SaaS lock-in.</p>      </div>    </div>  </div>