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:
2026-10-12 16:06:03 +00:00
parent 10a78efcc3
commit 954399ed5d
14 changed files with 299 additions and 11 deletions

View File

@@ -225,6 +225,25 @@ async function fillShow(box,p){
}
// A broken feed says so, or it reads as a quiet one (#142).
if(got.failing){ const f=$('#dfail',box); f.textContent=got.failing; f.hidden=false; }
// An admin whose token may administer the directory (#206); once, as Refresh fills it again.
const acts=$('.show .acts',box);
if(got.can_admin&&!$('[data-a="dremove"]',acts)){
acts.insertAdjacentHTML('beforeend',
`<button class="btn ico" data-a="drefresh" title="Check this feed now" aria-label="Check this feed now">${ICON.scan}</button>`
+`<button class="btn ico danger" data-a="dremove" title="Remove from the directory" aria-label="Remove from the directory">${ICON.trash}</button>`);
$('[data-a="drefresh"]',acts).onclick=async()=>{
try{ await api(`/api/directory/${encodeURIComponent(p.id)}/refresh`,{method:'POST'}); toast('Checked'); fillShow(box,p); }
catch(e){ toast(e.message,true); }
};
$('[data-a="dremove"]',acts).onclick=async()=>{
if(!confirm(`Remove ${NAME(p)} from the directory? It is blocked, so no server's snapshot brings it back. Admin, Directory can unblock it.`)) return;
try{ await api(`/api/directory/${encodeURIComponent(p.id)}`,{method:'DELETE'}); }
catch(e){ return toast(e.message,true); }
toast(`Removed ${NAME(p)} from the directory`);
dirRows=dirRows.filter(r=>r.id!==p.id); dirShow=null;
drawDirectory(box);
};
}
const items=got.items;
list.innerHTML=items.length?items.map(e=>{
const name=entryName(e), text=name.derived?'':plainText(e.description).slice(0,500);