11 KiB
NOTES-PAGES.md: page audit
An audit of every internal link in web/src against every route astro build actually
emits, plus a crawl of the built dist/ for links that resolve to nothing and anchors
that point at ids no page has. Findings first, then what was built to close them.
Audit run against 58 indexed mints, API up. Re-run it with:
pnpm build && node web/scripts/check-links.mjs web/dist
LIST_TARGETS=1 on that command prints every distinct internal target and which pages
link to it. The checker exits non-zero on a problem, so it can gate a deploy.
1. Findings
Routes are listed once each. "Linked from" is where the link lives in the source, not every page that renders the component.
| Route | Linked from | Existed before | Action |
|---|---|---|---|
/ |
Topbar brand, 404 | yes | none |
/mints |
Topbar nav, Footer, home hero + CTA + section links, mint page breadcrumb, 404 | yes | none |
/mints?sort=reviews ?sort=rating ?sort=recent |
home quick filters | yes (handled by the island) | none |
/mints?nut=17 |
home quick filter "Supports WebSockets" | route yes, filter no | not fixed, see below |
/mints#mint-search |
Topbar search affordance | id exists, link never renders | not fixed, see below |
/mint/[host] |
MintCard, home review cards | yes, 58 prerendered | none |
/about |
Topbar nav, Footer, home CTA | yes | none |
/about#nip87 |
home "Signed, not submitted" card | yes | none |
/about#nuts |
mint page NUT panel | yes | none |
/wallets |
Topbar nav, Footer | yes | none |
/#latest-reviews |
Topbar nav "Reviews" | anchor existed | changed: nav now points at /reviews |
/reviews |
nothing linked to it | no | built |
/terms |
nothing linked to it | no | built, linked from the footer |
/privacy |
nothing linked to it | no | built, linked from the footer |
/disclaimer |
nothing linked to it | no | built, linked from the footer and every mint page |
/404 |
(served by nginx on a miss) | yes | reworked, see below |
/sitemap.xml |
nothing | no | built |
/robots.txt |
nothing | no | built |
/api/*, /icons/* |
mint icons, islands | not in dist by design |
none: nginx proxies both to the API |
The link crawl over the built site found zero broken internal links and zero dead anchors, before and after. Nothing pointed at the missing pages, which is exactly why they were easy to miss: the gap was between the page inventory and the navigation, not inside the navigation.
Findings not closed by this task
/mints?nut=17 does nothing. The home page offers "Supports WebSockets" as a quick
filter and links to /mints?nut=17. The /mints island reads q and sort from the
query string and ignores everything else, so the link lands on an unfiltered list. The
fix is not one line: MintListItem (the payload /api/mints returns and the only data
/mints has) carries no nuts array, so MintCard has nothing to put in a
data-nuts attribute. Either the API adds nuts to the list payload, or /mints
fetches all 58 mint details at build time the way /mint/[host] already does. Worth
doing, out of scope for a page audit.
/mints#mint-search never renders. Topbar shows a "Search N mints" affordance
only when a mintCount prop is passed. Nothing passes a number: the mint page passes
mintCount={undefined} explicitly and no other page passes it at all, so the anchor
is never in the emitted HTML. The knock-on is in Base.astro: the / key handler
falls back to "no search box on this page, so follow the topbar search link", and that
branch is unreachable, so / does nothing on the mint pages, /about, /wallets and
the three new legal pages. Either pass the real count from /mints and the mint pages,
or drop the affordance and the fallback branch.
Islands and the view transitions router. Base.astro now renders <ClientRouter />
(added by concurrent work on the site, not by this task). Under it, a same-site
navigation swaps the document without re-running page scripts, and no island on the
site re-initialises on astro:page-load: /mints search and sort, the mint page
reviews panel, the pulse ticker and the new /reviews feed all run once, on the first
full page load. Every island needs the same astro:page-load treatment the reveal
script in Base.astro already has. Flagged rather than fixed here, because it is one
cross-cutting change across every island and it belongs with the router work.
2. What was built, and what was already there
Built
/reviews(web/src/pages/reviews.astro). The global feed: every mint's reviews in one list, newest first.- Build time: the 30 most recent written reviews are read from the relays and
prerendered, so the page has real content for a crawler and for a reader with
JavaScript off. The controls hide themselves through a
<noscript>rule rather than sitting there doing nothing. - Runtime: the island re-queries the relays for kind 38000 with no mint filter and resolves each event back to a known mint, then paginates 20 items to a page. Measured against the live relays: 644 events, 156 resolving to indexed mints across 25 mints.
- Filters: All, 5 star, Critical, each with a live count, plus a mint filter. The
mint filter is a text input backed by a
<datalist>of every mint, so typing part of a name or a domain narrows it; 58 options is too many to scroll.?mint=in the URL preselects one. - Cards are the mint page's review cards, literally:
web/src/lib/review-cards.tswas extracted out of the reviews island so both render from the same functions. Same identity resolution (kind 0 name, NIP-05, generated avatar, "Anon" fallback), same copy-to-clipboard npub, same provenance note. Each card links to its mint. - Same rating-only rule, applied per day: a review with a body is a card, and every rating with no comment from the same day collapses into that day's single summary line, which sits in the feed at that day's newest rating. Most review events on the network are ratings with no comment (586 of 644 in the measured run), so without this the feed would be nothing else. An opened summary stays open across filter and page changes.
- Build time: the 30 most recent written reviews are read from the relays and
prerendered, so the page has real content for a crawler and for a reader with
JavaScript off. The controls hide themselves through a
/terms,/privacy,/disclaimer. Prose in.proseat 720px, h1 plus h2 sections, sentence case, no em dashes, dated. The disclaimer is linked from the site footer and from the bottom of every mint page, which is where the decision it is about actually gets made./sitemap.xml(web/src/pages/sitemap.xml.ts). Generated at build: 8 static pages plus one entry per prerendered mint page, 66 URLs in the audit run. Written by hand rather than with@astrojs/sitemapbecause a mint page has a reallastmodavailable (the newest oflast_review_atandlast_online) and the integration would stamp every URL with the build time instead./robots.txt(web/src/pages/robots.txt.ts). Allows everything and names the sitemap. Generated rather than dropped inpublic/so theSitemap:line followsSITE_URLinstead of hardcoding the production host.web/scripts/check-links.mjs. The link checker this audit ran on.
Changed
/404. It already existed, was styled, and already held the mint fallback (on a/mint/{host}miss it asks the API and renders a summary behind the page level skeleton). It was missing three things the inventory asks for: the pixel moai, the "This page does not exist" heading, and a search input. All three added; the fallback is untouched and still runs before anything says the page is missing. The moai is 44px, because DESIGN.md says it is never scaled large.Topbar: "Reviews" pointed at/#latest-reviews, a section of the home page. It now points at/reviews.Footer: gained a second row with Disclaimer, Terms and Privacy, an "All reviews" link, and the one line worth repeating on every page ("mints are custodial, a mint can disappear with your money").- Home page: the "Latest reviews" strip's section link said "All mints →" and went
to
/mints. It now says "All reviews →" and goes to/reviews. web/src/components/Reviews.astro: the card builders moved out tolib/review-cards.ts. Behaviour is unchanged, verified in a browser: same cards, same counts, same filters, same collapsed summary.shared/src/nostr.ts:WRITTEN_MIN_CHARSmoved here, since the build time feed and both browser islands now all need the same answer to "is this a written review".
Already present, verified rather than touched
/walletsis real content, not a stub: eight wallets with platforms and a one-line description each, a card grid on the shared design system, and a closing note that the list is not an endorsement. Worth recording why it looks curated rather than ported: perNOTES.md, the old React site had no wallet directory to port, only a header link todocs.cashu.space/wallets./abouthas every section FRONTEND.md specifies, with the anchor ids the rest of the site links to:#cashu,#mints,#nip87,#how,#relays,#nuts,#numbers,#source./mint/[host]prerenders all 58 mints and the client fallback handles unknown hosts.
3. For the operator, before this goes live
Two things in the new pages depend on how the site is deployed, and this repo cannot know either. Both are written conditionally and one is marked on the page itself.
- Log retention (
/privacy, "Server logs"). The page says the web server keeps standard access logs with IP, time, request, status, user agent and referrer, which is true of any default nginx. It carries a visible operator note saying the retention figure needs confirming: the nginx config inREADME.mddoes not setaccess_logat all, so the real answer is your distribution's default (on Debian and Ubuntu, logrotate daily keeping 14 days). Confirm what your server actually does, write the number in, and delete the note and the.operator-notestyle. - Contact route (
/terms, "Contact"). It points takedown requests at the GitHub repository and names Azzamo as the operator. If there should be an email address or a Nostr contact instead, that is the paragraph to change.
Neither page invents a legal entity, a jurisdiction, a governing law clause or a company name, because none of those are knowable from the code. If the site needs them, they have to come from whoever runs it.
4. Verification
pnpm build: 67 pages (58 mints, 7 static pages, 404) plussitemap.xmlandrobots.txt.pnpm typecheck: 0 errors.node web/scripts/check-links.mjs web/dist: 67 pages, 97 distinct internal targets, no broken internal links, no dead anchors.- Headless browser over
astro preview:/reviewsloads 156 live reviews, the rating filters and the mint filter both narrow the list and the counts, pagination works, day summaries open and stay open across a re-render, and a card's mint link resolves (/mint/mint.belgianbitcoinembassy.org→ 200). With JavaScript off, 30 prerendered cards render and the controls are hidden. The mint page reviews panel is unchanged after the extraction./mint/does-not-exist.example.comstill runs the API lookup and then shows the moai, the heading and the search box.