feat(web): public mesh stats counter + /api/public/stats endpoint
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled

Live social-proof counter on the landing page, tied to the E2E
narrative. Formatted as understated mono footer, not hero brag.

Backend — new GET /api/public/stats (unauthed, 60s in-memory cache):
  {
    messagesRouted: SELECT COUNT(*) FROM mesh.message_queue,
    meshesCreated: SELECT COUNT(*) FROM mesh.mesh WHERE archivedAt IS NULL,
    peersActive: SELECT COUNT(*) FROM mesh.presence WHERE disconnectedAt IS NULL,
    lastUpdated: ISO timestamp,
  }

Aggregate counts only — no ids, no names, no ciphertext, no routing
metadata. Safe for public consumption. cache-control header sets
public/s-maxage=60 for edge caching. `x-cache: HIT|MISS` for debug.

Frontend — new MeshStats Server Component at
modules/marketing/home/mesh-stats.tsx. Reads the endpoint server-side
via the ~/lib/api/server client, renders monospace footer:

  ciphertext routed → 4,217 messages · 12 meshes · 8 peers online
  broker sees none of it

Graceful zero state: when messagesRouted === 0 shows
"ciphertext → ready to route" instead of embarrassing zeros. Tabular-
nums for the numeric spans so they don't jitter across renders.

Mounted between <CallToAction /> and <LatestNewsToaster />. Page-level
`export const revalidate = 60` so Next.js ISR refreshes the counter
every minute without a DB hit on every request (combined with the
API cache = two-layer 60s TTL, DB sees ~1 query/minute).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-05 16:00:00 +01:00
parent d0dfce6e33
commit 509af3afe0
5 changed files with 150 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
import {
publicStatsResponseSchema,
type PublicStatsResponse,
} from "@turbostarter/api/schema";
import { handle } from "@turbostarter/api/utils";
import { api } from "~/lib/api/server";
const ZERO_STATS: PublicStatsResponse = {
messagesRouted: 0,
meshesCreated: 0,
peersActive: 0,
lastUpdated: new Date(0).toISOString(),
};
const fetchStats = async (): Promise<PublicStatsResponse> => {
try {
return await handle(api.public.stats.$get, {
schema: publicStatsResponseSchema,
})();
} catch {
return ZERO_STATS;
}
};
const nf = new Intl.NumberFormat("en-US");
export const MeshStats = async () => {
const stats = await fetchStats();
const empty = stats.messagesRouted === 0;
return (
<section className="border-t border-[var(--cm-border)] bg-[var(--cm-bg)] px-6 py-10 md:px-12">
<div className="mx-auto max-w-[var(--cm-max-w)]">
<div
className="flex flex-col items-center gap-1 text-center text-[13px] text-[var(--cm-fg-tertiary)] md:flex-row md:justify-center md:gap-2"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<span className="text-[var(--cm-fg-secondary)]">
ciphertext routed
</span>
<span className="text-[var(--cm-clay)]"></span>
{empty ? (
<span className="text-[var(--cm-fg-secondary)]">
ready to route
</span>
) : (
<>
<span className="tabular-nums text-[var(--cm-fg)]">
{nf.format(stats.messagesRouted)} messages
</span>
<span className="hidden text-[var(--cm-border)] md:inline">·</span>
<span className="tabular-nums text-[var(--cm-fg-secondary)]">
{nf.format(stats.meshesCreated)} meshes
</span>
<span className="hidden text-[var(--cm-border)] md:inline">·</span>
<span className="tabular-nums text-[var(--cm-fg-secondary)]">
{nf.format(stats.peersActive)} peers online
</span>
</>
)}
</div>
<p
className="mt-2 text-center text-[11px] text-[var(--cm-fg-tertiary)]/70"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
broker sees none of it
</p>
</div>
</section>
);
};