Wallet-verified member messaging (matrix-lines permissions, admin visibility)
- messages.js: challenge/personal_sign/recover auth (vendored pinned js-sha3 0.9.3 + noble-secp256k1 1.7.1, server-side only; self-tested positive + tamper cases), 30d HttpOnly sessions, message store on the volume, matrix-line permissions (your downline direct or broadcast, your upline chain - nothing else, so spam is impossible by construction), daily rate limits (30 direct / 3 broadcasts), 1500-char plain text - chain.js: memberIdByAccount (wallet -> position for sign-in) - API: msg-challenge/-verify/-me/-inbox/-send/-read public + msg-unread (count only, no auth) + admin/messages (full visibility, disclosed to members in the UI) - Dashboard: Messages card with unread bell, one-tap wallet sign-in, inbox with auto-read, compose with to-ID or whole-team broadcast - Admin: Member Messages review table - Chatbot canned answer + AI system prompt updated Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -41,6 +41,7 @@ function render(){
|
||||
const orgEl=document.getElementById('orgRoot');if(orgEl&&!orgEl.value)orgEl.value=state.config.orgRootId||'21';
|
||||
if(!orgShareAutoLoaded&&orgEl&&orgEl.value){orgShareAutoLoaded=true;loadOrgShare();}
|
||||
if(!coachAutoLoaded){coachAutoLoaded=true;loadCoaching();}
|
||||
if(!msgsAutoLoaded){msgsAutoLoaded=true;loadAdminMsgs();}
|
||||
const efi=document.getElementById('emailFromInput');if(efi&&!efi.value)efi.value=state.config.emailFrom||em.from||'';
|
||||
const f=document.getElementById('configForm'),c=state.config;for(const k of ['siteName','programName','bridgeHeadline','bridgeSubheadline','premiumEntryPol','dappReferralBaseUrl','telegramUrl','supportLabel','bemobPostbackUrl','telegramBotToken','telegramChatId','telegramTopicId','teamRootId','teamAlertEmail','ownerAlertEmail'])if(f.elements[k])f.elements[k].value=c[k]??'';f.elements.showSponsorName.checked=!!c.showSponsorName;f.elements.showQueueProgress.checked=!!c.showQueueProgress;
|
||||
}
|
||||
@@ -121,6 +122,15 @@ async function loadCoaching(){
|
||||
}
|
||||
const cr=document.getElementById('coachRefresh');if(cr)cr.addEventListener('click',loadCoaching);
|
||||
let coachAutoLoaded=false;
|
||||
async function loadAdminMsgs(){
|
||||
const tb=document.getElementById('msgRows');if(!tb)return;
|
||||
try{
|
||||
const d=await api('/api/admin/messages');
|
||||
tb.innerHTML=(d.messages||[]).map(m=>`<tr><td>${esc(new Date(m.ts).toLocaleString())}</td><td><strong>#${m.fromId}</strong></td><td>${m.org?'📣 whole team':'#'+m.toId}</td><td style="white-space:pre-wrap;max-width:460px">${esc(m.body)}</td><td>${m.readCount}</td></tr>`).join('')||'<tr><td colspan="5" class="empty">No messages yet.</td></tr>';
|
||||
}catch(x){tb.innerHTML=`<tr><td colspan="5" class="empty">${esc(x.message)}</td></tr>`}
|
||||
}
|
||||
const mr=document.getElementById('msgRefresh');if(mr)mr.addEventListener('click',loadAdminMsgs);
|
||||
let msgsAutoLoaded=false;
|
||||
let incomeAutoLoaded=false;
|
||||
async function loadIncome(){
|
||||
const ids=(document.getElementById('incomeIds').value||'').trim();
|
||||
|
||||
Reference in New Issue
Block a user