source: Klonkt/src/views/pages/paid-gate.ejs@ 59f404a

main
Last change on this file since 59f404a was 59f404a, checked in by Robin <roboburr@…>, 4 days ago

De laatste twee scripts uit gewisselde inhoud (shaer-0i6)

shaer-bqr haalde de eigen logica van paid-gate en paid-passkey al uit de
pagina naar een module, maar liet de WebAuthn-bibliotheek als losse tag in
de body staan. Beide pagina's gaan via renderPage, dus bij een navigatie
binnen de site komen ze als fragment binnen -- met de nonce van dat
verzoek, die het al geladen document niet kent. Precies de vorm die Bart
destijds gemeld zag.

Het gevolg was stil: de module laadde wel, vond window.SimpleWebAuthn-
Browser niet, en viel terug op "geen WebAuthn hier". De passkey-knop deed
niets; de ontgrendelknop verdween. Alleen na een harde herlading werkte
het, en dat is precies waarom dit lang onzichtbaar bleef.

De modules halen de bibliotheek nu zelf op met loadWebAuthn() in lib.js.
Een dynamische import vanuit een module die de shell met een geldige nonce
startte is toegestaan -- daar is strict-dynamic voor. De UMD-bundel zet
zichzelf zonder CommonJS of AMD op globalThis, dus de global staat er na
afloop. init() wacht erop voordat hij bedraadt, anders zet de terugval de
knop uit terwijl er niets aan de hand is.

Verder nagelopen wat nog meer een scripttag draagt: vijf views hebben er
een van type application/json (een datablok, dat voert de browser nooit
uit) en twee gaan via res.render in plaats van renderPage -- losse
documenten die htmx nooit inwisselt. Die staan als uitzondering in de
nieuwe toets, met een tweede toets die controleert dat ze nog bestaan,
zodat een hernoeming de uitzondering niet stilletjes uitholt.

MOD_V naar 63: er wijzigde iets in mod/.

  • Property mode set to 100644
File size: 4.0 KB
Line 
1<div class="container pg-navwrap">
2 <%- include('../partials/post-nav', { newerPost: (typeof newerPost !== 'undefined' ? newerPost : null), olderPost: (typeof olderPost !== 'undefined' ? olderPost : null) }) %>
3</div>
4
5<article class="pg-page" id="pg-page">
6 <% if (typeof pgTitle !== 'undefined' && pgTitle) { %><h1 class="pg-title"><%= pgTitle %></h1><% } %>
7
8 <% if (typeof pgTeaser !== 'undefined' && pgTeaser) { %>
9 <div class="pg-teaser"><p><%= pgTeaser %></p></div>
10 <% } %>
11
12 <%# De opengezette muziek staat VOOR de poort en niet erachter: wie hem in een
13 hub of in Mastodon al kan afspelen, hoort hem ook hier te kunnen afspelen.
14 Alleen de tekst zit achter de poort, en die staat hier niet -- de route
15 geeft alleen de audio-shortcodes mee (paidOpenAudioHtml). %>
16 <% if (typeof pgAudio !== 'undefined' && pgAudio) { %>
17 <div class="pg-audio post-content"><%- pgAudio %></div>
18 <% } %>
19
20 <% var _hasPatron = (typeof pgPatronUrl !== 'undefined' && pgPatronUrl); %>
21 <section class="pg-card">
22 <div class="pg-lock">💶</div>
23 <h2 class="pg-h2"><%= t('pgate.h') %></h2>
24 <p class="pg-sub">
25 <% if (typeof pgCents !== 'undefined' && pgCents) { %><%= t('pgate.sub_cents', { eur: (pgCents/100).toFixed(2) }) %><% } else { %><%= t('pgate.sub') %><% } %>
26 </p>
27
28 <div class="pg-actions">
29 <% if (_hasPatron) { %>
30 <a class="pg-btn" href="<%= pgPatronUrl %>" target="_blank" rel="noopener"><%= t('pgate.join') %></a>
31 <button type="button" id="pg-unlock" class="pg-btn pg-btn-ghost"><%= t('pgate.unlock_have') %></button>
32 <% } else { %>
33 <button type="button" id="pg-unlock" class="pg-btn"><%= t('pgate.unlock') %></button>
34 <% } %>
35 </div>
36 <p id="pg-status" class="pg-status" hidden></p>
37 </section>
38</article>
39
40<%# Het script staat in assets/js/mod/paid-gate.js; de gegevens via partials/page-data.ejs (shaer-bqr).
41 De WebAuthn-bibliotheek stond hier als `<script src>` en is weg (shaer-0i6):
42 deze pagina komt via een link binnen de site als htmx-fragment binnen, en
43 dan draagt zo'n tag de nonce van een ander verzoek en weigert de CSP hem.
44 De module haalt hem nu zelf op met loadWebAuthn() uit lib.js. %>
45
46<style>
47 .pg-navwrap { max-width: 720px; margin: 0.5rem auto 1.5rem; padding: 0 1rem; }
48 .pg-page { max-width: 720px; margin: 0 auto 3rem; padding: 0 1rem; }
49 .pg-title { font-family: var(--font-display, serif); font-size: clamp(1.6rem, 4vw, 2.2rem); margin: 0 0 1rem; }
50 .pg-teaser { font-family: var(--font-body, serif); font-size: 1.1rem; line-height: 1.7; color: var(--ink); opacity: .95;
51 -webkit-mask-image: linear-gradient(180deg, #000 55%, transparent); mask-image: linear-gradient(180deg, #000 55%, transparent); }
52 .pg-audio { margin: 1.5rem 0 0; }
53 .pg-card { margin: 1.5rem 0 0; border: 1px solid color-mix(in srgb, var(--ink, #000) 16%, transparent); border-radius: 18px; padding: 30px 26px; text-align: center; }
54 .pg-lock { font-size: 38px; margin-bottom: 6px; }
55 .pg-h2 { font-size: 22px; margin: 0 0 8px; }
56 .pg-sub { opacity: .85; line-height: 1.6; margin: 0 auto 12px; max-width: 34em; }
57 .pg-btn { display: inline-block; text-align: center; text-decoration: none; padding: 12px 24px; border: none; border-radius: 10px; font: inherit; font-weight: 600; cursor: pointer;
58 background: var(--accent, #6b8f71); color: #fff; }
59 .pg-btn:disabled { opacity: .6; cursor: default; }
60 .pg-btn:hover { filter: brightness(1.05); }
61 .pg-actions { display: flex; flex-direction: column; gap: 10px; align-items: stretch; max-width: 320px; margin: 0 auto; }
62 .pg-btn-ghost { background: transparent; color: var(--ink, #222); border: 1px solid color-mix(in srgb, var(--ink, #000) 22%, transparent); }
63 .pg-status { margin: 12px 0 0; opacity: .9; }
64 .pg-status.is-err { color: #c0392b; }
65</style>
66<%- include('../partials/page-data', { pageData: { base: (typeof siteUrlBase !== 'undefined' && siteUrlBase ? siteUrlBase : ''), slug: pgSlug, hasPatron: !!_hasPatron, i18n: { join: t('pgate.join_short'), confirm: t('pgate.confirm'), failed: t('pgate.failed'), error: t('pgate.error') } } }) %>
Note: See TracBrowser for help on using the repository browser.