Administer the directory from iPX with an rwa token (#206)
A Directory feed's page offers Check now and Remove to an admin whose token is rwa (the page learns it from can_admin on the feed's page, asked of the directory only for admins). Admin, Directory lists the servers sharing, from a new GET /v1/instances, and the blocked feeds by title: a feed is often blocked because its address carries a key, so the address stays in the directory. Unblocking or dropping a snapshot sends this server's snapshot again at once; since directory's 403 reaches the page as 403, not 502. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -92,7 +92,8 @@ async function drawDirectorySettings(){
|
||||
<span class="hint">Sends the directory the feeds people here subscribe to, and how many of them do, every
|
||||
half hour. Never a private feed: one marked private, one with a login, or one whose address carries a key.</span></div>
|
||||
<div class="field"><label>Status</label><p class="hint" id="dstatus">Asking the directory…</p></div>
|
||||
<div class="cardacts"><button class="btn primary" id="dsave">${ICON.check} Save</button></div>`;
|
||||
<div class="cardacts"><button class="btn primary" id="dsave">${ICON.check} Save</button></div>
|
||||
<div id="dadmin"></div>`;
|
||||
const save = async (patch: Record<string, unknown>, said: string) => {
|
||||
try{
|
||||
await api('/api/settings', {method: 'PATCH', body: JSON.stringify(patch)});
|
||||
@@ -129,6 +130,48 @@ async function drawDirectoryStatus(){
|
||||
(sh.refused_blocked ? ` ${plural(sh.refused_blocked, 'feed')} the directory has removed.` : ''));
|
||||
}
|
||||
el.innerHTML = lines.join('<br>');
|
||||
if(s.access === 'rwa') drawDirectoryAdmin();
|
||||
}
|
||||
|
||||
/// What a token that may administer the directory can change from here (#206): the servers
|
||||
/// sharing with it, and the feeds removed from it.
|
||||
async function drawDirectoryAdmin(){
|
||||
const box = $('#dadmin');
|
||||
let a;
|
||||
try{ a = await api('/api/directory-admin'); }catch(e){ box.innerHTML = `<p class="hint">${esc(e.message)}</p>`; return; }
|
||||
if(!box.isConnected) return;
|
||||
box.innerHTML = `<h2>Servers</h2>
|
||||
<p class="hint">Each server sharing with the directory, by its token's name. Dropping one removes its
|
||||
feeds and counts until it next shares; to keep it out, revoke its token in the directory's config.</p>
|
||||
${a.instances.length ? a.instances.map(i => `<div class="inline urow" data-name="${esc(i.name)}">
|
||||
<div style="flex:1;min-width:0"><b style="overflow-wrap:anywhere">${esc(i.name)}</b>
|
||||
<small style="display:block;color:var(--faint)">${plural(i.feeds, 'feed')} · ${plural(i.subscribers, 'subscriber')}</small></div>
|
||||
<button class="btn ico danger" data-a="drop" title="Drop ${esc(i.name)}'s feeds" aria-label="Drop ${esc(i.name)}'s feeds">${ICON.trash}</button></div>`).join('')
|
||||
: '<p class="hint">No server is sharing yet.</p>'}
|
||||
<h2>Blocked</h2>
|
||||
<p class="hint">Feeds removed from the directory. No snapshot brings them back until they are unblocked.</p>
|
||||
${a.blocked.length ? a.blocked.map(b => `<div class="inline urow" data-id="${esc(b.id)}">
|
||||
<div style="flex:1;min-width:0"><b style="overflow-wrap:anywhere">${esc(b.title || 'Untitled')}</b>
|
||||
<small style="display:block;color:var(--faint)">Removed ${dateOf(b.at)}</small></div>
|
||||
<button class="btn" data-a="unblock">Unblock</button></div>`).join('')
|
||||
: '<p class="hint">Nothing is blocked.</p>'}`;
|
||||
box.onclick = async ev => {
|
||||
const b = (ev.target as Element).closest<HTMLElement>('[data-a]');
|
||||
if(!b) return;
|
||||
const row = b.closest<HTMLElement>('.urow');
|
||||
try{
|
||||
if(b.dataset.a === 'drop'){
|
||||
const name = row.dataset.name;
|
||||
if(!confirm(`Drop ${name}'s feeds and counts from the directory? They come back the next time ${name} shares.`)) return;
|
||||
await api(`/api/directory-instances/${encodeURIComponent(name)}`, {method: 'DELETE'});
|
||||
toast(`Dropped ${name}'s snapshot`);
|
||||
}else if(b.dataset.a === 'unblock'){
|
||||
await api(`/api/directory-blocked/${encodeURIComponent(row.dataset.id)}`, {method: 'DELETE'});
|
||||
toast('Unblocked. It comes back with the next snapshot that has it');
|
||||
}
|
||||
drawDirectoryAdmin();
|
||||
}catch(e){ toast(e.message, true); }
|
||||
};
|
||||
}
|
||||
|
||||
/* ---------------- accounts ---------------- */
|
||||
|
||||
Reference in New Issue
Block a user