Changeset 61e3daf in Klonkt
- Timestamp:
- 07/21/2026 12:38:08 AM (7 weeks ago)
- Branches:
- main
- Children:
- 928d1c7
- Parents:
- 4a08bfc
- git-author:
- Robin <roboburr@…> (07/21/2026 12:38:07 AM)
- git-committer:
- Robin <roboburr@…> (07/21/2026 12:38:08 AM)
- Files:
-
- 5 added
- 5 edited
-
docs/paid-posts-design.md (modified) (1 diff)
-
docs/privacy-betaalde-posts.md (modified) (1 diff)
-
src/config/database.js (modified) (1 diff)
-
src/routes/admin-paid.js (added)
-
src/server.js (modified) (2 diffs)
-
src/services/CryptoBox.js (added)
-
src/services/PaidPatreonService.js (added)
-
src/views/pages/admin-paid.ejs (added)
-
src/views/pages/admin.ejs (modified) (1 diff)
-
test/paid-patreon.test.js (added)
Legend:
- Unmodified
- Added
- Removed
-
docs/paid-posts-design.md
r4a08bfc r61e3daf 70 70 71 71 `PAID_SECRET` (32 random bytes) lives in env, like the other secrets. 72 73 ## Concurrency (a property of the cookie-less model) 74 75 Because there is no session and no "current user", the model is inherently 76 multi-user. Two people unlock side by side with no shared state to collide: 77 each request carries its own assertion, verified against that credential's own 78 public key, and the content goes back in that one response. `paid_entitlements` 79 is keyed per credential, so N passkeys are N independent rows. The challenge is 80 stateless (the signed blob), so there is no single "pending challenge" slot a 81 second visitor could overwrite. Unlike a cookie session, "two people in the same 82 browser" cannot clobber each other. The only caveat is a shared browser profile: 83 the passkey picker would then list both passkeys (a small visibility hint, not 84 access). 72 85 73 86 ## Data model (additive) -
docs/privacy-betaalde-posts.md
r4a08bfc r61e3daf 59 59 geen gemis. 60 60 61 ## Meerdere mensen tegelijk 62 63 Omdat er geen sessie en geen "ingelogde gebruiker" bestaat, kunnen meerdere 64 mensen tegelijk en los van elkaar posts ontgrendelen: elke bevestiging staat op 65 zichzelf. Er is geen gedeelde toestand die van elkaar afhangt. Deel je hetzelfde 66 apparaat en profiel met iemand, dan kan de passkey-kiezer wel tonen dat de ander 67 een passkey heeft (geen toegang, alleen zichtbaar); gebruik dan aparte apparaten 68 of profielen. 69 61 70 ## Bewaartermijn en verlopen 62 71 -
src/config/database.js
r4a08bfc r61e3daf 310 310 last_used_at DATETIME 311 311 ); 312 -- Paid posts (klonkt-demo-aki): the site owner's own Patreon campaign. 313 -- Secrets are encrypted at rest (CryptoBox). Never reuses the instance-level 314 -- patreon_* settings, which are Klonkt Premium's separate license flow. 315 CREATE TABLE IF NOT EXISTS paid_patreon ( 316 site_id TEXT PRIMARY KEY, 317 client_id TEXT, 318 client_secret_enc TEXT, 319 campaign_id TEXT, 320 access_token_enc TEXT, 321 refresh_token_enc TEXT, 322 token_exp INTEGER, -- unix seconds 323 default_min_cents INTEGER DEFAULT 0, 324 updated_at DATETIME DEFAULT CURRENT_TIMESTAMP 325 ); 312 326 CREATE TABLE IF NOT EXISTS ap_outbox ( 313 327 id TEXT PRIMARY KEY, -- note path segment (uuid) → /ap/notes/<id> -
src/server.js
r4a08bfc r61e3daf 45 45 import adminPatreonRoutes from './routes/admin-patreon.js'; 46 46 import adminStatsRoutes from './routes/admin-stats.js'; 47 import adminPaidRoutes from './routes/admin-paid.js'; 47 48 import adminMediaRoutes from './routes/admin-media.js'; 48 49 import circleRoutes from './routes/circle.js'; … … 378 379 app.use('/admin/patreon', adminPatreonRoutes); 379 380 app.use('/admin/stats', adminStatsRoutes); 381 app.use('/admin/paid', adminPaidRoutes); 380 382 app.use('/admin/newsletter', adminNewsletterRoutes); 381 383 app.use('/admin/shows', adminShowsRoutes); -
src/views/pages/admin.ejs
r4a08bfc r61e3daf 46 46 <% if (typeof premiumUnlocked === 'undefined' || premiumUnlocked) { %> 47 47 <a href="/admin/stats" class="btn"><%= t('admin.b_stats') %></a> 48 <a href="/admin/paid" class="btn">Betaalde posts</a> 48 49 <a href="/admin/newsletter" class="btn"><%= t('admin.b_newsletter') %></a> 49 50 <% if (tenancy !== 'hub' && primarySite) { %><a href="/pers" class="btn" target="_blank"><%= t('admin.b_perskit') %></a><% } %>
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)