Changeset 5c373b8 in Klonkt for src/assets/js


Ignore:
Timestamp:
07/25/2026 07:51:28 AM (7 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
e62f65d
Parents:
28267519
Message:

Guardian 2: follow-goedkeuring (FEP-633c §5.3)

Een Follow op een ward wordt niet meer automatisch geaccepteerd: hij wacht op
goedkeuring van de guardians. Afgebakend zoals de spec: gating geldt ALLEEN voor
ward-actors (die guardians hebben); een gewone site zonder guardians accepteert
als vanouds, geen gedragswijziging. Een gecommitte guardian die zelf volgt wordt
wel meteen geaccepteerd (Barts regel: die heeft geen gate nodig, en zo werkt het
meekijken). Quorum per ward: 'any' (default, één volstaat), 'all' of 'none'; een
enkele reject weigert.

De guardian ziet de verzoeken in /guardian2 (ward en guardian zitten op dezelfde
familie-Klonkt, dus lokaal leesbaar) en tikt Accept/Deny. Bij goedkeuring stuurt
Klonkt de Accept(Follow) en legt de follower vast, zodat bezorging (ook
followers-only) begint. Cross-instance federatie van de goedkeuring is een latere
verfijning (de daemon heeft het patroon).

Changed files:
src/services/ActivityPubService.js

  • inbound Follow: gate voor ward-actors; acceptGatedFollow/rejectGatedFollow

src/config/database.js

  • ap_pending_follows + ap_pending_follow_approvals

src/services/guardianship/index.js

  • follows-module geexporteerd

src/routes/guardian2.js

  • GET /api/follow-requests, POST /api/follow/:id (guardian-gecheckt)

src/views/pages/guardian2.ejs, src/assets/js/guardian2.js

src/services/i18n.js

  • guardian2 follow_title/follow_sub (nl/en/de)

New file:
src/services/guardianship/follows.js

  • de gating-store + quorum-beslissing (any/all), pure en testbaar

test/follow-gating.test.js

  • any/all-quorum en single-reject

remarks: npm test 167/167. v1 /guardian en niet-ward-sites onaangeraakt.

-robo
Co-Authored-By: Claude Opus 4.8 <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/assets/js/guardian2.js

    r28267519 r5c373b8  
    165165  }
    166166
     167  // ── 0b. Follow requests on your wards (§5.3) ────────────────────────────
     168  function answerFollow(id, decision, btn) {
     169    if (btn) btn.disabled = true;
     170    fetch('/guardian2/api/follow/' + encodeURIComponent(id), {
     171      method: 'POST', headers: { 'Content-Type': 'application/json' },
     172      body: JSON.stringify({ decision: decision, site: S.site }),
     173    }).then(loadFollowReqs);
     174  }
     175  function renderFollowReqs(items) {
     176    var list = document.getElementById('follow-list');
     177    list.textContent = '';
     178    (items || []).forEach(function (f) {
     179      var card = el('div', 'g-card');
     180      var row = el('div', 'row');
     181      row.appendChild(el('span', 'who grow', f.follower + '  →  @' + f.ward));
     182      var ok = el('button', 'small', T.accept || 'Accept');
     183      ok.addEventListener('click', function () { answerFollow(f.id, 'approve', ok); });
     184      var no = el('button', 'quiet small', T.reject || 'Deny');
     185      no.addEventListener('click', function () { answerFollow(f.id, 'reject', no); });
     186      row.appendChild(ok); row.appendChild(no);
     187      card.appendChild(row);
     188      list.appendChild(card);
     189    });
     190    show('follow-section', (items || []).length > 0);
     191  }
     192  function loadFollowReqs() {
     193    return fetch('/guardian2/api/follow-requests?site=' + encodeURIComponent(S.site))
     194      .then(function (r) { return r.json(); })
     195      .then(function (f) { if (f && !f.error) renderFollowReqs(f.items); })
     196      .catch(function () { /* stays hidden */ });
     197  }
     198
    167199  function refresh() {
    168200    return fetch('/guardian2/api/state?site=' + encodeURIComponent(S.site))
    169201      .then(function (r) { return r.json(); })
    170202      .then(function (s) { if (s && !s.error) { S = s; T = s.strings || T; renderAll(); } })
    171       .then(loadFeed);
     203      .then(loadFeed).then(loadFollowReqs);
    172204  }
    173205
     
    263295  });
    264296
    265   renderAll(); pushState(); loadFeed();
     297  renderAll(); pushState(); loadFeed(); loadFollowReqs();
    266298  setInterval(refresh, 45000);   // live-ish while open
    267299  } catch (e) {
Note: See TracChangeset for help on using the changeset viewer.