Changeset 72ec6a4 in Klonkt for src/views


Ignore:
Timestamp:
07/31/2026 06:55:59 PM (6 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
17546af
Parents:
bfa6fa1
Message:

Hub-modus en guardian-lite eruit

Robins besluit (31-7): een instance is een eigenaar. Twee dingen weg.

HUB-MODUS was al dood: getTenancy() gaf sinds 24-6 hardcoded 'solo'
terug, dus elke tenancy === 'hub'-tak was onbereikbaar. Nu ook echt
verwijderd: getTenancy/setTenancy zelf, de /user/:slug-routing in
resolveSite, de hub-takken in admin, zoeken, audio, posts (neighbours
en related over alle sites), download, en de push-prefix. In de views
verdwijnen de hub-tagline, de hub-navigatie, de sites- en
users-tabellen (die kwamen alleen in hub-modus gevuld en verwezen nu
naar locals die niemand meer meegeeft), de eigenaar-toewijzing bij een
site, de /user/-slugprefix, het hub-brandblok en de hub-thuisknop.

GUARDIAN-LITE was de laatste multi-user-rest: /guardian/invite gaf een
link waarmee iemand via /guardian/join een echte user plus een site met
guardian_only=1 aanmaakte. Dat zette andermans wachtwoordhash, sessie
en PRIVATE actor-sleutel in jouw database, waardoor een verhuizing of
export nooit netjes kon (shaer-qw6q). Routes, formulier, kolom en
uitnodigingstabel zijn weg. Het guardian-DASHBOARD blijft: dat is
FEP-633c en werkt voor guardians met een eigen Klonkt. Bestaande
installaties houden kolom en tabel ongebruikt; nieuwe krijgen ze niet.

Changed files:
src/services/SettingsService.js

  • getTenancy/setTenancy verwijderd; kop herschreven

src/middleware/site.js, src/middleware/render.js

  • /user/:slug-routing weg; tenancy en hubTitle uit de locals

src/routes/admin.js, admin-settings.js, audio.js, download.js,
src/routes/posts.js, search.js

  • hub-takken en hub-queries weg; postNeighbors zonder isHub

src/services/ActivityPubService.js

  • pushPrefix is nu gewoon ; getTenancy-import weg

src/routes/guardian.js

  • /invite en /join verwijderd (dashboard blijft), imports opgeschoond

src/config/database.js

  • guardian_only-kolom en ap_guardian_invites-tabel niet meer aangemaakt

src/views/pages/admin.ejs, admin-users.ejs, admin-site-edit.ejs,
src/views/pages/guardian.ejs, partials/topnav.ejs, chrome.ejs, bottom-tab.ejs

  • alle hub-takken en de uitnodigingsknop weg

remarks: 320 regels weg, 63 erbij. Suite 372 groen; alle 85 templates
compileren; en met een wegwerp-kopie van de database daadwerkelijk
gedraaid en ingelogd: /, /admin, /admin/users, /admin/sites,
/admin/settings, /admin/sites/demo/edit, /admin/media en /guardian
geven alle 200 zonder fouten in het log, en /guardian/invite en
/guardian/join geven nu 404.

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

Location:
src/views
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • src/views/pages/admin-site-edit.ejs

    rbfa6fa1 r72ec6a4  
    1414        <span class="slug-url">
    1515          <span class="slug-url-host" id="slug-host"
    16                 data-prefix="<%= (typeof tenancy !== 'undefined' && tenancy === 'hub') ? '/user/' : '/' %>">website.com/</span><input
     16                data-prefix="/">website.com/</span><input
    1717                type="text" name="slug" id="slug-input" class="slug-url-input" value="<%= site.slug %>" required <% if (!isNew) { %>readonly<% } %>
    1818                pattern="[a-z0-9_-]{2,40}" placeholder="<%= t('asite.slug_placeholder') %>"
     
    2424        <input type="text" name="title" value="<%= site.title || '' %>" required maxlength="200">
    2525      </label>
    26       <%# Assign (or reassign) owner — ONLY god AND only in hub mode. In hub this
    27           gives each user their own self-managed Klonkt. In solo (one site,
    28           one owner) this is pointless/confusing, so it's hidden. %>
    29       <% if (user && user.role === 'god' && (typeof tenancy !== 'undefined' && tenancy === 'hub')) { %>
    30       <label>
    31         <span><%= t('asite.owner') %> <small class="form-hint-inline"><%= t('asite.owner_hint') %></small></span>
    32         <% var _godSuffix = t('asite.owner_god_suffix'); %>
    33         <select name="owner_id">
    34           <% (typeof users !== 'undefined' ? users : []).forEach(function(u){ %>
    35             <option value="<%= u.id %>" <%= (site.owner_id === u.id) ? 'selected' : '' %>><%= u.username %><%= u.role === 'god' ? _godSuffix : '' %></option>
    36           <% }); %>
    37         </select>
    38       </label>
    39       <% } %>
    4026      <label>
    4127        <span><%= t('asite.tagline') %> <small class="form-hint-inline"><%= t('asite.tagline_hint') %></small></span>
  • src/views/pages/admin-users.ejs

    rbfa6fa1 r72ec6a4  
    7171
    7272          <div class="ax-user-controls">
    73             <%# Hub: give this user their own self-managed Klonkt — opens the
    74                 new-site form with this user already pre-selected as owner. %>
    75             <% if (typeof tenancy !== 'undefined' && tenancy === 'hub' && canMutate) { %>
    76               <a href="/admin/sites/new?owner=<%= u.id %>" class="ax-icon-btn"
    77                  aria-label="<%= t('ausr.new_klonkt_for', { name: u.username }) %>" title="<%= _u_newklonkt %>">+🌐</a>
    78             <% } %>
    7973            <form method="post" action="/admin/users/<%= u.id %>/role" class="ax-role-form">
    8074              <label class="ax-role">
  • src/views/pages/admin.ejs

    rbfa6fa1 r72ec6a4  
    1717    .prem-badge:hover{filter:brightness(1.08)}
    1818  </style>
    19   <% if (tenancy === 'hub') { %>
    20     <p class="admin-tagline"><%= t('admin.tagline_hub') %></p>
    21   <% } else if (typeof circlesOn !== 'undefined' && circlesOn) { %>
     19  <% if (typeof circlesOn !== 'undefined' && circlesOn) { %>
    2220    <p class="admin-tagline"><%= t('admin.tagline_cirkels') %></p>
    2321  <% } else { %>
     
    2624
    2725  <nav class="admin-quick-links" aria-label="<%= t('nav.admin') %>">
    28     <% if (tenancy === 'hub') { %>
    29       <a href="/admin/sites" class="btn"><%= t('admin.b_sites') %></a>
    30       <a href="/admin/users" class="btn"><%= t('admin.b_users') %></a>
    31       <a href="/admin/media" class="btn"><%= t('admin.b_media') %></a>
    32       <% if (typeof audioEnabled === 'undefined' || audioEnabled) { %>
    33       <a href="/admin/playlists" class="btn"><%= t('admin.b_playlists') %></a>
    34       <% } %>
    35       <% if (primarySite) { %><a href="/admin/seo" class="btn"><%= t('admin.b_seo') %></a><% } %>
    36       <a href="/admin/settings" class="btn"><%= t('admin.b_settings') %></a>
    37     <% } else { %>
    3826      <a href="/posts/new" class="btn"><%= t('admin.b_newpost') %></a>
    3927      <a href="/admin/media" class="btn"><%= t('admin.b_media') %></a>
     
    4533      <% if (primarySite) { %><a href="/admin/seo" class="btn"><%= t('admin.b_seo') %></a><% } %>
    4634      <a href="/admin/settings" class="btn"><%= t('admin.b_settings') %></a>
    47     <% } %>
    4835    <% if (typeof premiumUnlocked === 'undefined' || premiumUnlocked) { %>
    4936      <a href="/admin/stats" class="btn"><%= t('admin.b_stats') %></a>
    5037      <a href="/admin/paid" class="btn"><%= t('admin.b_paid') %></a>
    5138      <a href="/admin/newsletter" class="btn"><%= t('admin.b_newsletter') %></a>
    52       <% if (tenancy !== 'hub' && primarySite) { %><a href="/pers" class="btn" target="_blank"><%= t('admin.b_perskit') %></a><% } %>
    53       <% if (tenancy !== 'hub' && primarySite) { %><a href="/downloads" class="btn" target="_blank"><%= t('admin.b_downloads') %></a><% } %>
    54       <% if (tenancy !== 'hub' && primarySite) { %><a href="/links" class="btn" target="_blank"><%= t('admin.b_linkbio') %></a><% } %>
     39      <% if (primarySite) { %><a href="/pers" class="btn" target="_blank"><%= t('admin.b_perskit') %></a><% } %>
     40      <% if (primarySite) { %><a href="/downloads" class="btn" target="_blank"><%= t('admin.b_downloads') %></a><% } %>
     41      <% if (primarySite) { %><a href="/links" class="btn" target="_blank"><%= t('admin.b_linkbio') %></a><% } %>
    5542      <a href="/admin/shows" class="btn"><%= t('admin.b_agenda') %></a>
    5643    <% } %>
     
    6350
    6451  <div class="admin-stats">
    65     <% if (tenancy === 'hub') { %>
    66       <div class="stat-card"><div class="stat-num"><%= stats.users %></div><div class="stat-label"><%= t('admin.st_users') %></div></div>
    67       <div class="stat-card"><div class="stat-num"><%= stats.sites %></div><div class="stat-label"><%= t('admin.st_sites') %></div></div>
    68     <% } %>
    6952    <div class="stat-card"><div class="stat-num"><%= stats.posts %></div><div class="stat-label"><%= t('admin.st_posts') %></div></div>
    7053    <div class="stat-card"><div class="stat-num"><%= stats.published %></div><div class="stat-label"><%= t('admin.st_published') %></div></div>
     
    9073  <% } %>
    9174
    92   <% if (sites && sites.length) { %>
    93     <h2><%= t('admin.sec_sites') %></h2>
    94     <table class="admin-table">
    95       <thead>
    96         <tr><th><%= t('admin.th_slug') %></th><th><%= t('admin.th_title') %></th><th><%= t('admin.th_owner') %></th><th><%= t('admin.th_created') %></th></tr>
    97       </thead>
    98       <tbody>
    99         <% sites.forEach(function(s) { %>
    100           <tr>
    101             <td><code><%= s.slug %></code></td>
    102             <td><%= s.title %></td>
    103             <td><%= s.owner_username || '—' %></td>
    104             <td><%= formatDate(s.created_at) %></td>
    105           </tr>
    106         <% }); %>
    107       </tbody>
    108     </table>
    109   <% } %>
    110 
    111   <% if (users && users.length) { %>
    112     <h2><%= t('admin.sec_users') %></h2>
    113     <table class="admin-table">
    114       <thead>
    115         <tr><th><%= t('admin.th_username') %></th><th><%= t('admin.th_email') %></th><th><%= t('admin.th_role') %></th><th><%= t('admin.th_joined') %></th></tr>
    116       </thead>
    117       <tbody>
    118         <% users.forEach(function(u) { %>
    119           <tr>
    120             <td><%= u.username %></td>
    121             <td><%= u.email %></td>
    122             <td><span class="role-pill role-<%= u.role %>"><%= u.role %></span></td>
    123             <td><%= formatDate(u.created_at) %></td>
    124           </tr>
    125         <% }); %>
    126       </tbody>
    127     </table>
    128   <% } %>
    129 
    13075</div>
    13176
  • src/views/pages/guardian.ejs

    rbfa6fa1 r72ec6a4  
    2626    <span class="g-me" title="<%= t('guardian.acting_as') %>">@<%= state.site %></span>
    2727    <% } %>
    28     <form method="post" action="/guardian/invite" style="display:inline">
    29     <button class="quiet small" type="submit">Invite a guardian</button>
    30   </form>
    3128</header>
    3229
  • src/views/partials/bottom-tab.ejs

    rbfa6fa1 r72ec6a4  
    1313                        && permissions && permissions.canCreatePost && permissions.canCreatePost(user, site));
    1414const _ownProfile  = user ? ('/users/' + user.username) : null;
    15 const _isHub       = (typeof tenancy !== 'undefined' && tenancy === 'hub');
    16 // Home in hub mode ALWAYS goes to the hub root (/), not to an artist's sub-home.
    17 // In solo/circle _siteUrlBase is empty, so it stays '/'.
    18 const _homeHref    = (_isHub ? '' : _siteUrlBase) + '/';
     15const _homeHref    = _siteUrlBase + '/';
    1916
    2017// Strip site prefix for cleaner matching
     
    2623
    2724let _active = null;
    28 // In hub, Home is only active on the real hub root, not on an artist sub-home.
    29 if (_isHub ? (_path === '/') : (_p === '/' || _p === '')) _active = 'home';
     25if (_p === '/' || _p === '') _active = 'home';
    3026else if (_p.indexOf('/search') === 0 || _p.indexOf('/tag/') === 0 || _p.indexOf('/type/') === 0) _active = 'search';
    3127else if (_p === '/posts/new' || /^\/posts\/[^/]+\/edit$/.test(_p)) _active = 'create';
  • src/views/partials/chrome.ejs

    rbfa6fa1 r72ec6a4  
    1717var _isAuth    = typeof bodyClass === 'string' && bodyClass.indexOf('on-auth') >= 0;
    1818var _isChat    = typeof bodyClass === 'string' && bodyClass.indexOf('on-chat') >= 0;
    19 var hubLanding = (typeof bodyClass === 'string' && bodyClass.indexOf('on-hub') >= 0)
    20    || ((typeof tenancy !== 'undefined' && tenancy === 'hub') && !siteUrlBase && !_isAdmin);
     19var hubLanding = (typeof bodyClass === 'string' && bodyClass.indexOf('on-hub') >= 0);
    2120// Zelfstandige premium-feature-pagina's hebben hun eigen kop → geen profielkop/
    2221// switcher, wél de topnav (Robin 2026-06-18: "bovenste weg, behoud de nav").
  • src/views/partials/topnav.ejs

    rbfa6fa1 r72ec6a4  
    2828      <% if (_isAdmin || _isSpecial) { %>
    2929        <a href="<%= _siteUrlBase %>/" class="nav-link nav-link-admin"><%= t('nav.back_to_site') %></a>
    30       <% } else if (typeof tenancy !== 'undefined' && tenancy === 'hub') { %>
    31         <%# Hub: the brand navigates to the hub home via HTMX. The chrome is always
    32             rendered in hub mode and hidden on the landing via body.on-hub (CSS), so htmx
    33             can switch without a full reload → the audio player stays alive. %>
    34         <a class="nav-hub-home" href="/" aria-label="Home" title="Home"
    35            hx-get="/?partial=1" hx-target="#pcms-main" hx-swap="innerHTML" hx-push-url="/" hx-indicator="#pcms-loading">
    36           <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
    37         </a>
    38       <% } else if (site) { %>
    39         <a class="site-title" href="<%= _siteUrlBase %>/"
    40            hx-get="<%= _siteUrlBase %>/?partial=1" hx-target="#pcms-main" hx-swap="innerHTML"
    41            hx-push-url="<%= _siteUrlBase %>/" hx-indicator="#pcms-loading"><%= site.title %></a>
    4230      <% } else { %>
    4331        <a class="site-title" href="/">Klonkt</a>
Note: See TracChangeset for help on using the changeset viewer.