remove em dashes

This commit is contained in:
2026-04-09 21:07:28 +04:00
parent 5f20b41e91
commit 79ba63d86b
31 changed files with 154 additions and 154 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
// Password gate for protected status pages. We sign a short-lived cookie with
// the page id + a tag derived from the current password hash + an expiry, so
// a successful password unlock survives across page loads without us having
// to hit Postgres on every request and so changing the page password
// to hit Postgres on every request - and so changing the page password
// invalidates every cookie issued under the old password.
//
// Cookie format: <pageId>.<pwTag>.<exp>.<sig>
+1 -1
View File
@@ -1,7 +1,7 @@
// Tiny in-memory TTL cache keyed by string. Status pages serve the same payload
// to many visitors during an outage; we don't want every page hit to fan out to
// Postgres. The cache is per-process; behind a load balancer each replica fills
// independently, which is fine short TTLs converge quickly.
// independently, which is fine - short TTLs converge quickly.
interface Entry<T> { value: T; expires: number }
+18 -18
View File
@@ -1,5 +1,5 @@
// Loads the read-only data needed to render a public status page. NEVER reads
// the raw `pings` table uses `monitor_region_state` for current state and
// the raw `pings` table - uses `monitor_region_state` for current state and
// `monitor_uptime_rollup` for historical uptime windows.
import sql from "./db";
@@ -46,12 +46,12 @@ export interface MonitorRow {
// etc.) must never leak to anonymous visitors via the JSON endpoint.
// Group correlator. Emitted as the matching group's `position` index
// (0-based string), NOT the underlying UUID, so the JSON doesn't leak
// internal IDs. The HTML render works the same either way it just
// internal IDs. The HTML render works the same either way - it just
// looks up groups by this token.
group_id: string | null;
position: number;
display_mode: "compact" | "expanded"; // resolved (per-monitor override → page default → 'expanded')
// 'paused' means the monitor was disabled in the dashboard the runner has
// 'paused' means the monitor was disabled in the dashboard - the runner has
// stopped checking it, and the public page should treat it as planned
// maintenance rather than an outage.
current_state: "up" | "down" | "unknown" | "paused";
@@ -64,7 +64,7 @@ export interface MonitorRow {
}
// Multi-window uptime (24h / 7d / 30d / 90d) is now derived from the same
// rollup row set that loadMonitors pulls for the bar chart see the in-JS
// rollup row set that loadMonitors pulls for the bar chart - see the in-JS
// aggregation pass below. This used to be a second SQL round-trip running
// four FILTER aggregates that redid arithmetic the raw bucket rows already
// contained.
@@ -116,9 +116,9 @@ export async function loadMonitors(
// Step 1: page → monitors with display overrides + group + position. Pull
// m.enabled too so we can render disabled monitors as "Maintenance" on the
// public page (the runner stops checking them when disabled, so their
// region_states would otherwise drift to a stale "up" visitors should
// region_states would otherwise drift to a stale "up" - visitors should
// see this as planned downtime, not phantom uptime).
// Deliberately do NOT select m.url see the MonitorRow comment for why the
// Deliberately do NOT select m.url - see the MonitorRow comment for why the
// raw target URL must never reach the public payload.
const monitorRows = await sql<any[]>`
SELECT
@@ -160,7 +160,7 @@ export async function loadMonitors(
//
// Union of all of those is "hourly back N hours OR daily back N days" with
// N chosen to cover whichever consumer needs the widest window. The rows
// are then partitioned by purpose entirely in JS no second round-trip,
// are then partitioned by purpose entirely in JS - no second round-trip,
// no duplicate FILTER aggregates inside Postgres.
const bucket: BucketType = barFrequency;
const count = Math.max(1, Math.min(180, barCount));
@@ -270,7 +270,7 @@ export async function loadMonitors(
const mid = r.monitor_id;
const bt: BucketType = r.bucket_type;
// Bar chart accumulators only rows matching the configured bar frequency.
// Bar chart accumulators - only rows matching the configured bar frequency.
if (bt === bucket) {
if (!barIndexed[mid]) barIndexed[mid] = {};
const slot = barIndexed[mid]![startIso] ?? { total: 0, up: 0 };
@@ -292,7 +292,7 @@ export async function loadMonitors(
}
// Multi-window uptime accumulators. 24h uses hourly buckets; 7d/30d/90d
// use daily buckets same as the old loadMultiWindowUptime SQL did.
// use daily buckets - same as the old loadMultiWindowUptime SQL did.
// Strict `<` to match the old SQL's `bucket_start > now() - interval`.
const wt = initWindowTotals(mid);
if (bt === "hourly" && nowMs - startMs < ms24h) {
@@ -319,7 +319,7 @@ export async function loadMonitors(
// Sort the latency sparkline rows by ts ASC per monitor (the unified query
// sorts by bucket_type then region then bucket_start, so the per-monitor
// hourly subset is already ordered within a region but interleaved across
// regions this normalises it the same way the old separate query did).
// regions - this normalises it the same way the old separate query did).
for (const mid of Object.keys(latByMonitor)) {
latByMonitor[mid]!.sort((a, b) => a.ts.localeCompare(b.ts));
}
@@ -372,7 +372,7 @@ export async function loadMonitors(
}
// Multi-window uptime is a straight read from the windowTotals accumulator.
// We deliberately do NOT round to 2 decimals here the formatter on the
// We deliberately do NOT round to 2 decimals here - the formatter on the
// public page truncates (not rounds) to 2 decimals so a value like 99.9999%
// doesn't visually round up to "100.00%". Pre-rounding here would erase that
// information before the formatter ever sees it.
@@ -396,7 +396,7 @@ export async function loadMonitors(
const anyUp = region_states.some((s) => s.state === "up");
current_state = anyDown ? "down" : anyUp ? "up" : "unknown";
}
// A disabled monitor is in operator-declared maintenance runner has
// A disabled monitor is in operator-declared maintenance - runner has
// stopped checking it. Override whatever the last region state was so the
// public page reads "Maintenance" instead of a stale "Operational".
if (m.enabled === false) current_state = "paused";
@@ -405,7 +405,7 @@ export async function loadMonitors(
if (buckets.length > 0) {
const tot = buckets.reduce((a, b) => a + b.total, 0);
const upT = buckets.reduce((a, b) => a + b.up, 0);
// Full precision the display layer truncates (not rounds) to 2 decimals
// Full precision - the display layer truncates (not rounds) to 2 decimals
// so any downtime, however small, never visually rounds up to 100%.
uptime_pct = tot > 0 ? (100 * upT / tot) : null;
}
@@ -487,7 +487,7 @@ export interface MonitorDetailPayload {
export async function loadMonitorDetail(slug: string, monitorId: string, window?: Window): Promise<MonitorDetailPayload | null> {
const page = await loadStatusPage(slug);
if (!page) return null;
// Existence check only confirm the monitor is actually attached to this
// Existence check only - confirm the monitor is actually attached to this
// page. The bulk loader below produces the full payload; this query exists
// purely so we can return null on a wrong slug/monitor combo without firing
// the bigger query at all.
@@ -499,7 +499,7 @@ export async function loadMonitorDetail(slug: string, monitorId: string, window?
if (!link) return null;
const win = (window ?? page.default_window) as Window;
// Reuse the bulk loader with a single-monitor list keeps the bucket/state
// Reuse the bulk loader with a single-monitor list - keeps the bucket/state
// logic in one place. Cheap because we're querying for one ID. We also need
// the page's groups so we can redact the monitor's group_id (UUID → public
// position-as-string token), matching what /:slug.json emits.
@@ -556,10 +556,10 @@ export async function loadMonitorDetail(slug: string, monitorId: string, window?
// The shape we actually expose to anonymous visitors. Computed by stripping
// internal IDs and any field a public consumer doesn't need from the row
// types see redactPageForPublic / redactGroupsAndMonitors below.
// types - see redactPageForPublic / redactGroupsAndMonitors below.
//
// custom_css and analytics_html are kept here even though they're noisy to
// JSON consumers, because the HTML render reads from this same object and
// JSON consumers, because the HTML render reads from this same object - and
// they're already publicly visible in the rendered HTML, so dropping them
// from JSON wouldn't actually add any privacy.
export interface PublicPageView {
@@ -626,7 +626,7 @@ function redactPageForPublic(p: StatusPageRow): PublicPageView {
// Replace each group's UUID with its position-as-string. Monitors carry the
// same token in their group_id field, so the consumer can still join them
// they just see opaque "0", "1", "2" tokens instead of internal UUIDs.
// - they just see opaque "0", "1", "2" tokens instead of internal UUIDs.
function redactGroupsAndMonitors(
groups: GroupRow[],
monitors: MonitorRow[],
+1 -1
View File
@@ -1,4 +1,4 @@
// Read-only Postgres client. The status service does NOT run migrations
// Read-only Postgres client. The status service does NOT run migrations -
// schema is owned by apps/api. This file just opens a connection.
import postgres from "postgres";
+14 -14
View File
@@ -38,7 +38,7 @@ function clientIp(req: Request): string {
}
// 404s must NOT be cached by browsers or Cloudflare. The same URL frequently
// flips between "200 with data" and "404 not-found" for example when an
// flips between "200 with data" and "404 not-found" - for example when an
// operator adds a password to a previously-public page, or when a slug
// changes, or when a monitor is removed. If Cloudflare cached a 404 (default
// behaviour for unspecified Cache-Control on 404 responses) the operator
@@ -81,7 +81,7 @@ function isAuthorised(page: { id: string; password_hash: string | null }, req: R
if (!page.password_hash) return true;
// Pass the current password_hash so verifyAuthCookie can derive the
// expected pwTag and reject any cookie that was issued under a previous
// password i.e. rotating the password evicts every existing session.
// password - i.e. rotating the password evicts every existing session.
return verifyAuthCookie(req.headers.get("cookie"), page.id, page.password_hash);
}
@@ -95,7 +95,7 @@ function splitSlugAndFormat(raw: string): { slug: string; format: "html" | "json
}
async function renderHtml(slug: string, request: Request): Promise<Response> {
// Page row is fetched fresh on every request never cached. The page row
// Page row is fetched fresh on every request - never cached. The page row
// carries the live password_hash + index_search + display config; an
// operator changing any of those in the dashboard must take effect
// immediately, not after a TTL window. The single PK lookup is sub-ms,
@@ -103,7 +103,7 @@ async function renderHtml(slug: string, request: Request): Promise<Response> {
const page = await loadStatusPage(slug);
if (!page) return notFound();
if (!isAuthorised(page, request)) {
// Do NOT pass page.title to the password template that would let any
// Do NOT pass page.title to the password template - that would let any
// OSINT scraper iterating slugs harvest the human-readable name of every
// private page without ever authenticating. Slug is fine: it's already
// in the URL the visitor typed.
@@ -115,7 +115,7 @@ async function renderHtml(slug: string, request: Request): Promise<Response> {
const payload = await cached(`payload:${slug}`, 15, () => loadPagePayload(slug));
if (!payload) return notFound();
const html = eta.render("page", { ...payload, expandJsHash, appCssHash });
// Password-protected pages MUST be private never let an edge cache or
// Password-protected pages MUST be private - never let an edge cache or
// shared proxy hold a copy that some other visitor could pull. Public
// pages keep the 15s shared cache for performance under viral hits.
const cacheControl = page.password_hash
@@ -146,7 +146,7 @@ async function renderJson(slug: string, request: Request, win?: Window): Promise
const cacheKey = `payload:${slug}:${win ?? page.default_window}`;
const payload = await cached(cacheKey, 15, () => loadPagePayload(slug, win));
if (!payload) return jsonNotFound();
// Password-protected JSON must be private same reasoning as renderHtml.
// Password-protected JSON must be private - same reasoning as renderHtml.
const cacheControl = page.password_hash
? "private, no-store, must-revalidate"
: "public, max-age=15, s-maxage=15";
@@ -182,11 +182,11 @@ async function renderRssResp(slug: string, request: Request): Promise<Response>
}
const app = new Elysia()
// No status page lives at the root show the same 404 visitors get for any
// No status page lives at the root - show the same 404 visitors get for any
// unknown slug, so a stray hit on the apex doesn't leak service identity.
.get("/", () => notFound())
// Static expand.js cached aggressively, hash-busted via query string.
// Static expand.js - cached aggressively, hash-busted via query string.
.get("/_static/expand.js", () => new Response(Bun.file(expandJsPath), {
headers: {
"content-type": "application/javascript; charset=utf-8",
@@ -194,7 +194,7 @@ const app = new Elysia()
},
}))
// Static app.css same caching contract as expand.js. The query string in
// Static app.css - same caching contract as expand.js. The query string in
// the <link> tag is the file's MD5 hash, so deploys propagate immediately
// even though the asset itself is marked immutable for a year.
.get("/_static/app.css", () => new Response(Bun.file(appCssPath), {
@@ -204,7 +204,7 @@ const app = new Elysia()
},
}))
// Single public route dispatches HTML / JSON / RSS by extension on the slug.
// Single public route - dispatches HTML / JSON / RSS by extension on the slug.
.get("/:slug", async ({ params, request, query }) => {
const { slug, format } = splitSlugAndFormat(params.slug);
if (!allow(slug, clientIp(request))) return rateLimited();
@@ -214,7 +214,7 @@ const app = new Elysia()
})
// Public SVG badge. Password-protected pages 404 here so an unauthenticated
// shields-style embed can't reveal a private page's current state and
// shields-style embed can't reveal a private page's current state - and
// crucially can't even confirm whether a private slug exists, since the
// 404 is identical to a totally bogus slug.
.get("/:slug/badge.svg", async ({ params, request }) => {
@@ -240,7 +240,7 @@ const app = new Elysia()
// Per-monitor detail JSON for the click-to-expand UI in compact mode.
// Path is /:slug/monitor/:idWithExt where idWithExt is e.g. "abc123.json".
// We strip the .json suffix in the handler same trick as the slug route to
// We strip the .json suffix in the handler - same trick as the slug route to
// dodge memoirist's "two params at the same position" rule.
.get("/:slug/monitor/:idWithExt", async ({ params, request, query }) => {
if (!allow(params.slug, clientIp(request))) return rateLimited();
@@ -320,8 +320,8 @@ const app = new Elysia()
const port = Number(process.env.STATUS_PORT ?? 3003);
const server = Bun.serve({
port,
// Wrap app.handle in a try/catch so any unexpected throw Postgres
// connection blip, template render error, missing env var, etc. turns
// Wrap app.handle in a try/catch so any unexpected throw - Postgres
// connection blip, template render error, missing env var, etc. - turns
// into a generic 500 with an opaque body. Without this wrapper Bun's
// default error path may include framework details, file paths, or stack
// traces in the response, which would leak internal layout to anyone who
+1 -1
View File
@@ -1,6 +1,6 @@
// Per-(slug, IP) token bucket. 30 requests in a 10s window. Cheap, in-memory,
// resets on process restart. Behind a load balancer each replica enforces its
// own bucket that's fine, the goal is "stop a hostile script from melting one
// own bucket - that's fine, the goal is "stop a hostile script from melting one
// box", not perfect distributed accounting.
interface Bucket { tokens: number; refillAt: number }
+1 -1
View File
@@ -32,7 +32,7 @@ export async function renderRss(page: StatusPageRow, baseUrl: string): Promise<s
body_html: u.body_html,
}));
const channelTitle = escapeXml(`${page.title} Incidents`);
const channelTitle = escapeXml(`${page.title} - Incidents`);
const channelDescription = escapeXml(page.description || `${page.title} status updates`);
const channelLink = `${baseUrl}/${page.slug}`;
+2 -2
View File
@@ -3,7 +3,7 @@
visitors as soon as the new bundle is deployed.
Per-page custom CSS is still inlined in page.ejs (it's per-request data,
not a build artifact) kept in a separate <style> block AFTER this
not a build artifact) - kept in a separate <style> block AFTER this
file is loaded so it always wins on specificity ties. */
:root {
@@ -126,7 +126,7 @@ h1 { font-size: 1.75rem; font-weight: 700; margin: 0 0 0.5rem; }
.incident-update .body code { background: var(--bg); padding: 0.1em 0.3em; border-radius: 3px; font-size: 0.85em; }
.past-incidents { margin-top: 3rem; }
.past-incidents h2 { font-size: 1.1rem; margin-bottom: 1rem; }
/* Past incidents Atlassian-style: grouped by date with light typography. */
/* Past incidents - Atlassian-style: grouped by date with light typography. */
.past-day { margin-bottom: 2rem; }
.past-day-header { font-size: 0.95rem; font-weight: 600; color: var(--fg); padding-bottom: 0.5rem; border-bottom: 1px solid var(--border); margin: 0 0 1rem; }
.past-day-empty { font-size: 0.85rem; color: var(--muted); margin: 0.25rem 0 0; }
+4 -4
View File
@@ -31,7 +31,7 @@
var timeOpts = { hour: "2-digit", minute: "2-digit" };
if (barFrequency === "hourly") {
return start.toLocaleDateString(undefined, dateOpts) + ", " +
start.toLocaleTimeString(undefined, timeOpts) + " " +
start.toLocaleTimeString(undefined, timeOpts) + " - " +
end.toLocaleTimeString(undefined, timeOpts);
}
return start.toLocaleDateString(undefined, { weekday: "short", month: "short", day: "numeric" });
@@ -51,11 +51,11 @@
var lat = latRaw == null ? null : parseInt(latRaw, 10);
if (!start) return;
// Full precision pct so the formatter can decide. Anything below 100% gets
// 2 truncated (not rounded) decimals same rule as the page-level uptime
// 2 truncated (not rounded) decimals - same rule as the page-level uptime
// numbers, so a bucket with one failed check never displays as "100%".
var pct = total > 0 ? (100 * up / total) : null;
var pctText;
if (pct == null) pctText = "";
if (pct == null) pctText = "-";
else if (pct >= 100) pctText = "100%";
else pctText = (Math.floor(pct * 100) / 100).toFixed(2) + "%";
var html = '<div class="head">' + fmtBucketRange(start) + "</div>";
@@ -67,7 +67,7 @@
html += '<div class="row"><span>Avg ping</span><span>' + lat + "ms</span></div>";
}
} else {
html += '<div class="row"><span>No data</span><span></span></div>';
html += '<div class="row"><span>No data</span><span>-</span></div>';
}
tooltip.innerHTML = html;
tooltip.style.display = "block";
+7 -7
View File
@@ -15,9 +15,9 @@
const groupOrder = [...groups.map(g => g.id), ''];
function fmtPct(p) {
if (p == null) return '';
// Only show "100%" when the value is *exactly* 100. Anything below even
// 99.9999% must show 2 decimals so visitors can see there was downtime.
if (p == null) return '-';
// Only show "100%" when the value is *exactly* 100. Anything below - even
// 99.9999% - must show 2 decimals so visitors can see there was downtime.
// Truncate (floor) rather than round, otherwise 99.9999 would render as
// "100.00" and silently swallow the downtime.
if (p >= 100) return '100%';
@@ -48,7 +48,7 @@
return 'bad';
}
function fmtUptime(p) {
if (p == null) return '';
if (p == null) return '-';
// Only "100%" if the monitor was up for every single check in the window.
// Truncate (not round) below that so 99.9999% never displays as "100.00".
if (p >= 100) return '100%';
@@ -56,7 +56,7 @@
}
// Overall status: down if any monitor is down, degraded if any partial, else up.
// Paused monitors are operator-declared maintenance they don't count
// Paused monitors are operator-declared maintenance - they don't count
// toward "down" or "degraded", but we surface a small note in the banner
// when at least one is in maintenance so visitors aren't confused.
let paused_count = 0;
@@ -77,7 +77,7 @@
: overall === 'down' ? 'Major outage in progress'
: 'Partial outage';
if (paused_count > 0) {
overallText += ' ' + paused_count + (paused_count === 1 ? ' service' : ' services') + ' under maintenance';
overallText += ' - ' + paused_count + (paused_count === 1 ? ' service' : ' services') + ' under maintenance';
}
const overallBg = overall === 'up' ? 'rgba(16,185,129,0.1)'
: overall === 'degraded' ? 'rgba(245,158,11,0.1)'
@@ -204,7 +204,7 @@
</div>
<div class="monitor-meta">
<% if (m.current_state === 'paused') { %>
<span class="maintenance-pill" title="This service is paused for maintenance checks are not running.">Maintenance</span>
<span class="maintenance-pill" title="This service is paused for maintenance - checks are not running.">Maintenance</span>
<% } else { %>
<% if (page.show_response_time && m.avg_latency != null) { %><span><%= m.avg_latency %>ms</span><% } %>
<% const winKey = page.default_window === '24h' ? 'd24' : page.default_window === '7d' ? 'd7' : page.default_window === '30d' ? 'd30' : 'd90'; %>