Admin: award Suite capacity to any member as a team bonus

Marty asked for a way to grant impressions to anybody, labelled as an admin
bonus. Team Grants already did this for leaders, but only within their own
organisation and only out of a level pool.

adminGrant() is deliberately a different thing: it comes from no pool (the
team is not a position and has no allowance), it can reach ANY registered
position rather than only someone downline, and it carries a reason that
survives in the ledger. Recorded with by:0 so nothing downstream mistakes it
for a leader's grant — the member's own page renders those as "the team"
rather than "#0".

The route verifies the position exists on-chain before crediting it; a typo
would otherwise sit in the ledger crediting nobody, and it would be invisible
until someone went looking.

Awards land on top of whatever the level already allows and expire with the
month like every other allowance. The admin page lists what has been awarded
this month, so bonuses stay auditable rather than vanishing once given.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-08-30 05:40:00 -05:00
parent 59bd4bd4ab
commit 05afe5c5f6
4 changed files with 130 additions and 4 deletions
+20
View File
@@ -1356,6 +1356,26 @@ async function handleApi(req,res,pathname){
const s=getSession(req);if(s){sessions.delete(s.token);saveSessions();}return json(res,200,{ok:true},{'Set-Cookie':'ctb.sid=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0'});
}
if(pathname.startsWith('/api/admin/')&&!requireAdmin(req,res))return;
if(pathname==='/api/admin/grant'){
if(req.method==='GET'){
return json(res,200,{tools:suiteGrants.tools(),log:suiteGrants.adminLog(60)});
}
if(req.method==='POST'){
const b=await bodyJson(req)||{};
const to=Number(b.to)||0;
if(!to)return json(res,400,{error:'Enter the position number.'});
// Confirm the position actually exists before crediting it — a typo would
// otherwise sit in the ledger crediting nobody.
let md=null;
try{ md=await chain.memberPublic(to); }catch(err){}
if(!md||!md.registered)return json(res,404,{error:'Position #'+to+' is not registered on-chain.'});
try{
const rec=suiteGrants.adminGrant({to:to,tool:String(b.tool||''),n:b.n,note:b.note});
return json(res,200,{granted:rec,levelName:md.levelName,log:suiteGrants.adminLog(60)});
}catch(err){ return json(res,400,{error:String(err.message||err)}); }
}
}
if(req.method==='GET'&&pathname==='/api/admin/matrix-tree'){
return json(res,200,chain.getMatrixTree());
}