Fix: unlocked post renders via its own page; no duplicate supporter button
Two things the tester hit once the passkey flow worked:
- Duplicate "Word supporter". The gate's no-WebAuthn fallback relabelled the
unlock button to "Word supporter", which sat next to the real "Word
supporter op Patreon" button. In the two-button layout that fallback now
hides the (unusable) unlock button instead; the single-button layout keeps
the relabel since it's the only button.
- The unlocked content sat in a bare <div>, so it lost the post page's layout
and scoped typography ("div niet in een goeie div"). Instead of injecting
HTML into the gate, /paid/unlock now returns a short-lived (120s) signed
unlock capability and the client reloads the real post URL with it (?u=).
GET /:slug renders the FULL post through its normal template when the
capability is valid: correct wrapper, scoped styles, and working audio
players (which bind on load and couldn't init in injected HTML). Not a
cookie and not stored: the token lives only in that one URL and expires.
Changed files:
src/routes/paid.js
- /unlock returns { ok, redirect } with a 120s unlock blob; drop the
renderPostBodyHtml import (no longer injected here)
src/routes/posts.js
- a valid ?u= capability bypasses the paid gate and renders the post
src/views/pages/paid-gate.ejs
- no-WebAuthn fallback hides the unlock button when a Patreon page exists;
success reloads the real post via the redirect
-robo
Co-Authored-By: Claude Opus 4.8 <noreply@…>