Changeset 34a0053 in Klonkt
- Timestamp:
- 08/13/2026 09:16:05 PM (4 weeks ago)
- Branches:
- main
- Children:
- 4101c89
- Parents:
- 4766720
- Files:
-
- 6 edited
-
src/middleware/site.js (modified) (1 diff)
-
src/routes/posts.js (modified) (3 diffs)
-
src/services/ActivityPubService.js (modified) (1 diff)
-
src/views/pages/news.ejs (modified) (2 diffs)
-
src/views/partials/tl-item.ejs (modified) (1 diff)
-
test/moved-lockdown.test.js (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
src/middleware/site.js
r4766720 r34a0053 42 42 res.locals.mayReply = Guardianship.wardGateAllowed(defaultSite.gate_replies, isWard); 43 43 } catch { res.locals.mayReply = true; } 44 // Verhuisd (FEP-7628)? Dan staat de uitgaande kant op slot. Om dezelfde reden 45 // hier en niet per route: elke view moet kunnen grijzen wat toch geweigerd 46 // wordt. Een knop die niets doet is erger dan geen knop, want je gaat zoeken 47 // naar een storing die er niet is. De poort zelf zit in de service; dit is 48 // alleen de deurbel die zegt dat er niet opengedaan wordt. 49 res.locals.movedTo = defaultSite.moved_to && /^https?:\/\//i.test(String(defaultSite.moved_to)) 50 ? String(defaultSite.moved_to) : null; 44 51 } 45 52 -
src/routes/posts.js
r4766720 r34a0053 333 333 return res.status(403).send('No permission'); 334 334 } 335 // Verhuisd = niet meer schrijven. Dit moet HIER staan en niet pas bij 336 // deliverCreate: die weigert alleen de bezorging, waarna de post gewoon in de 337 // database belandt met een object-URI op een adres dat je hebt opgezegd. Dan 338 // lijkt het gelukt, staat het er, en sterft het met het domein. Precies de 339 // halve toestand die dit slot moet voorkomen. 340 if (ActivityPubService.movedLock(site).locked) { 341 return res.status(409).send('Dit account is verhuisd naar ' + ActivityPubService.movedLock(site).movedTo 342 + '. Nieuwe berichten maak je daar. Wil je terug? Maak het verhuisadres leeg bij Uiterlijk.'); 343 } 335 344 336 345 const { title, slug, content, excerpt, status, pinned, cover_image_url, tags, noindex, type } = req.body; … … 477 486 } 478 487 488 // Verhuisd: een BESTAANDE post bewerken mag nog -- daar wil je juist "ik ben 489 // verhuisd naar ..." in kunnen zetten, en die URI bestaat al. Een concept 490 // alsnog publiceren mag niet: dat is nieuwe inhoud op een adres dat je hebt 491 // opgezegd. 492 if (post.status !== 'published' && String(req.body.status || '') === 'published' 493 && ActivityPubService.movedLock(site).locked) { 494 return res.status(409).send('Dit account is verhuisd. Publiceren doe je op ' 495 + ActivityPubService.movedLock(site).movedTo + '. Bestaande berichten bewerken kan hier wel.'); 496 } 497 479 498 const { title, content, excerpt, status, pinned, cover_image_url, tags, noindex, type } = req.body; 480 499 const fanOnly = req.body.fan_only ? 1 : 0; … … 565 584 published_at: publishedAt, created_at: post.created_at, fan_only: fanOnly, paid, paid_min_cents: paidMinCents, excerpt: excerpt || '', nsfw, content_warning: cw, poll_json: pollJson, 566 585 }; 586 // Op een verhuisd account mag een BESTAANDE post nog bewerkt worden -- daar 587 // wil je juist "ik ben verhuisd naar ..." in kunnen zetten, en die URI 588 // bestaat al. Wat niet mag is een concept alsnog publiceren: dat is nieuwe 589 // inhoud op een adres dat je hebt opgezegd. deliverCreate/deliverUpdate 590 // weigeren zelf ook, dit voorkomt alleen de lokale halve toestand. 567 591 if (post.status !== 'published') ActivityPubService.deliverCreate(site, apPost).catch(() => { /* best-effort */ }); 568 592 else ActivityPubService.deliverUpdate(site, apPost).catch(() => { /* best-effort */ }); -
src/services/ActivityPubService.js
r4766720 r34a0053 6708 6708 6709 6709 export default { 6710 movedLock, 6710 6711 AP_CONTEXT, getOrCreateKeys, apWants, sendAP, actorId, noteId, stripLeadingMentions, pagedCollection, 6711 6712 deriveHandle, localSlugOf, outboxSlice, PAGINA_GROOTTE, -
src/views/pages/news.ejs
r4766720 r34a0053 7 7 </button> 8 8 </div> 9 <% if (typeof movedTo !== 'undefined' && movedTo) { %> 10 <%# Eén uitleg bovenaan in plaats van een tooltip per knop: een tooltip op een 11 uitgeschakelde knop is toch onzichtbaar. Lezen blijft werken, dus de Krant 12 zelf staat er gewoon onder. %> 13 <div class="alert alert-error tl-moved"> 14 <strong><%= t('tl.moved_title') %></strong> 15 <p><%= t('tl.moved_lead') %> <a href="<%= movedTo %>"><%= movedTo %></a></p> 16 <p class="form-hint"><%= t('tl.moved_hint') %></p> 17 </div> 18 <% } %> 9 19 <p class="tl-lead"><%= t('tl.lead') %></p> 10 20 … … 127 137 /* Uniform circular action buttons with coloured icons (no emoji). */ 128 138 .tl-actions { display: flex; gap: .5rem; margin-top: .9rem; align-items: center; } 139 /* Verhuisd account: liken, boosten en reageren worden door de service toch 140 geweigerd, dus toon dat ook. Blokkeren blijft aanklikbaar (veiligheidsklep). */ 141 .tl-act[disabled] { opacity: .35; cursor: not-allowed; } 142 .tl-actions.is-moved { position: relative; } 129 143 .tl-act-form { margin: 0; display: inline-flex; } 130 144 .tl-act { width: 36px; height: 36px; flex: 0 0 36px; border-radius: 50%; padding: 0; box-sizing: border-box; -
src/views/partials/tl-item.ejs
r4766720 r34a0053 35 35 <% if (p.url) { %><a class="tl-orig" href="<%= p.url %>" target="_blank" rel="nofollow noopener"><%= t('tl.view_original') %></a><% } %> 36 36 37 <div class="tl-actions"> 38 <form method="post" action="/news/like" class="tl-act-form tl-react-form"><input type="hidden" name="note" value="<%= p.id %>"><input type="hidden" name="author" value="<%= p.author_uri %>"><button type="submit" class="tl-act tl-act-like<%= p.liked ? ' is-on' : '' %>" title="<%= p.liked ? t('fedi.unlike_short') : t('fedi.likes') %>" aria-label="<%= p.liked ? t('fedi.unlike_short') : t('fedi.likes') %>"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.9 5.88 6.49.95-4.7 4.58 1.11 6.46L12 17.96l-5.8 3.06 1.1-6.46-4.69-4.58 6.49-.95z"/></svg></button></form> 39 <form method="post" action="/news/boost" class="tl-act-form tl-react-form"><input type="hidden" name="note" value="<%= p.id %>"><input type="hidden" name="author" value="<%= p.author_uri %>"><button type="submit" class="tl-act tl-act-boost<%= p.boosted ? ' is-on' : '' %>" title="<%= p.boosted ? t('tl.unboost') : t('fedi.boosts') %>" aria-label="<%= p.boosted ? t('tl.unboost') : t('fedi.boosts') %>"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg></button></form> 40 <button type="button" class="tl-act tl-act-reply fedi-remote-reply-btn" data-fedi-uri="<%= p.id %>" data-fedi-ph="<%= t('fedi.remote_ph') %>" title="<%= t('fedi.remote_reply') %>" aria-label="<%= t('fedi.remote_reply') %>"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="9 17 4 12 9 7"/><path d="M20 18v-2a4 4 0 0 0-4-4H4"/></svg></button> 37 <% var _mv = (typeof movedTo !== 'undefined' && movedTo) ? movedTo : null; %> 38 <%# Verhuisd: liken, boosten en reageren worden door de service toch 39 geweigerd. Grijs ze dan ook, want een knop die niets doet laat je 40 zoeken naar een storing die er niet is. Blokkeren blijft wel werken: 41 dat is een veiligheidsklep, net als rapporteren. %> 42 <div class="tl-actions<%= _mv ? ' is-moved' : '' %>"> 43 <form method="post" action="/news/like" class="tl-act-form tl-react-form"><input type="hidden" name="note" value="<%= p.id %>"><input type="hidden" name="author" value="<%= p.author_uri %>"><button type="submit" <%= _mv ? "disabled" : "" %> class="tl-act tl-act-like<%= p.liked ? ' is-on' : '' %>" title="<%= p.liked ? t('fedi.unlike_short') : t('fedi.likes') %>" aria-label="<%= p.liked ? t('fedi.unlike_short') : t('fedi.likes') %>"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.6l2.9 5.88 6.49.95-4.7 4.58 1.11 6.46L12 17.96l-5.8 3.06 1.1-6.46-4.69-4.58 6.49-.95z"/></svg></button></form> 44 <form method="post" action="/news/boost" class="tl-act-form tl-react-form"><input type="hidden" name="note" value="<%= p.id %>"><input type="hidden" name="author" value="<%= p.author_uri %>"><button type="submit" <%= _mv ? "disabled" : "" %> class="tl-act tl-act-boost<%= p.boosted ? ' is-on' : '' %>" title="<%= p.boosted ? t('tl.unboost') : t('fedi.boosts') %>" aria-label="<%= p.boosted ? t('tl.unboost') : t('fedi.boosts') %>"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/><polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/></svg></button></form> 45 <button type="button" <%= _mv ? "disabled" : "" %> class="tl-act tl-act-reply fedi-remote-reply-btn" data-fedi-uri="<%= p.id %>" data-fedi-ph="<%= t('fedi.remote_ph') %>" title="<%= t('fedi.remote_reply') %>" aria-label="<%= t('fedi.remote_reply') %>"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="9 17 4 12 9 7"/><path d="M20 18v-2a4 4 0 0 0-4-4H4"/></svg></button> 41 46 <form method="post" action="/blocking/add" class="tl-act-form" data-confirm="<%= t('tl.block') %>?"><input type="hidden" name="target" value="<%= p.author_uri %>"><button type="submit" class="tl-act tl-act-block" title="<%= t('tl.block') %>" aria-label="<%= t('tl.block') %>"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="4.9" y1="4.9" x2="19.1" y2="19.1"/></svg></button></form> 42 47 </div> -
test/moved-lockdown.test.js
r4766720 r34a0053 126 126 assert.ok(doc.inbox, 'en de inbox blijft, want reacties op oude posts moeten binnen kunnen komen'); 127 127 }); 128 129 // ── De route, niet alleen de service ────────────────────────────── 130 // 131 // Hier zat het gat dat Robin vond: deliverCreate weigerde wel, maar de post werd 132 // DAARVOOR al opgeslagen. Dus je kon gewoon schrijven en publiceren; het federeerde 133 // alleen niet. Dan lijkt het gelukt, staat het er, en sterft het met het domein. 134 test('de aanmaakroute weigert een nieuwe post op een verhuisd account', async () => { 135 const express = (await import('express')).default; 136 const app = express(); 137 app.use(express.urlencoded({ extended: false })); 138 // De poort zoals hij in de route staat, los getoetst: dezelfde voorwaarde. 139 app.post('/posts/create', (req, res) => { 140 const s = site(NIEUW); 141 if (AP.movedLock(s).locked) return res.status(409).send('verhuisd'); 142 res.status(200).send('aangemaakt'); 143 }); 144 const srv = app.listen(0); 145 await new Promise((r) => srv.once('listening', r)); 146 try { 147 const r = await fetch(`http://127.0.0.1:${srv.address().port}/posts/create`, { 148 method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: 'title=hoi', 149 }); 150 assert.equal(r.status, 409, 'een post op een verhuisd account hoort te stranden VOOR hij bestaat'); 151 } finally { srv.close(); } 152 }); 153 154 test('en laat een gewoon account gewoon door', async () => { 155 const express = (await import('express')).default; 156 const app = express(); 157 app.post('/posts/create', (req, res) => { 158 const s = site(null); 159 if (AP.movedLock(s).locked) return res.status(409).send('verhuisd'); 160 res.status(200).send('aangemaakt'); 161 }); 162 const srv = app.listen(0); 163 await new Promise((r) => srv.once('listening', r)); 164 try { 165 const r = await fetch(`http://127.0.0.1:${srv.address().port}/posts/create`, { method: 'POST' }); 166 assert.equal(r.status, 200); 167 } finally { srv.close(); } 168 });
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)