Member dashboard: drill-down tree, list view, legend, and strategy-aware referral tool
- "Your team" now covers the member's FULL leg with click-to-drill pyramid, breadcrumb, and a list-view toggle (mirrors the admin matrix). - Legend under both the member tree and admin matrix explaining the qualified checkmark, tier badges, and downline rollup. - "Share this position" card: while under 2/2 it's a personal recruiting tool — per-member QR (vendored qrcode-generator, CSP-safe), copy link, and a join CTA carrying the member's ID as sponsor. At 2/2 the page automatically retires the link and redirects sharing to the team rotator, so personal referrals can't undermine the moving-link strategy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -437,7 +437,7 @@ async function memberPublic(id) {
|
|||||||
const inc = await fetchIncome(id);
|
const inc = await fetchIncome(id);
|
||||||
out.income = inc.map(p => ({ fromId: p.fromId, pol: p.pol, ts: p.ts, desc: describeIncome(p.fromTier, p.level, p.pol) })).reverse().slice(0, 50);
|
out.income = inc.map(p => ({ fromId: p.fromId, pol: p.pol, ts: p.ts, desc: describeIncome(p.fromTier, p.level, p.pol) })).reverse().slice(0, 50);
|
||||||
} catch (e) { out.income = []; }
|
} catch (e) { out.income = []; }
|
||||||
out.subtree = getSubtree(id, 3);
|
out.subtree = getSubtree(id, 99); // full leg — the dashboard drills down client-side
|
||||||
const chain = [];
|
const chain = [];
|
||||||
const seen = new Set([id]);
|
const seen = new Set([id]);
|
||||||
let cur = m.uplineId;
|
let cur = m.uplineId;
|
||||||
|
|||||||
+1
-1
@@ -117,7 +117,7 @@ function renderList(){
|
|||||||
}
|
}
|
||||||
function renderMatrix(){
|
function renderMatrix(){
|
||||||
const d=matrixUI.data;
|
const d=matrixUI.data;
|
||||||
const head=`${d.memberCount} positions · snapshot ${new Date(d.snapshotAt).toISOString().replace('T',' ').slice(0,16)} UTC · P = Premium, S = Standard`;
|
const head=`${d.memberCount} positions · snapshot ${new Date(d.snapshotAt).toISOString().replace('T',' ').slice(0,16)} UTC · <span class="mtp-qmark" style="position:static;display:inline-grid;vertical-align:middle">✓</span> qualified (2/2 directs) · <span class="mt-badge mt-prem">P</span> Premium · <span class="mt-badge">S</span> Standard · ⬇ full downline below that position`;
|
||||||
document.getElementById('treeToggleBtn').classList.remove('hidden');
|
document.getElementById('treeToggleBtn').classList.remove('hidden');
|
||||||
document.getElementById('treeToggleBtn').textContent=matrixUI.mode==='pyramid'?'List view':'Pyramid view';
|
document.getElementById('treeToggleBtn').textContent=matrixUI.mode==='pyramid'?'List view':'Pyramid view';
|
||||||
if(matrixUI.mode==='pyramid')renderPyramid();else renderList();
|
if(matrixUI.mode==='pyramid')renderPyramid();else renderList();
|
||||||
|
|||||||
+3
-2
@@ -6,11 +6,12 @@
|
|||||||
<section id="dash" class="hidden">
|
<section id="dash" class="hidden">
|
||||||
<div style="display:flex;justify-content:space-between;align-items:center;gap:14px;flex-wrap:wrap;margin-bottom:6px"><div><div class="eyebrow">Member position</div><h1 style="margin:6px 0 0">Position <span class="gold" id="dTitle">#—</span> <span id="dVerified" class="live-badge" style="vertical-align:middle;margin-left:8px"><span class="dot"></span> Verified on-chain</span> <span id="dQualified"></span></h1></div><button id="switchId" class="btn btn-secondary btn-sm">Look up another ID</button></div>
|
<div style="display:flex;justify-content:space-between;align-items:center;gap:14px;flex-wrap:wrap;margin-bottom:6px"><div><div class="eyebrow">Member position</div><h1 style="margin:6px 0 0">Position <span class="gold" id="dTitle">#—</span> <span id="dVerified" class="live-badge" style="vertical-align:middle;margin-left:8px"><span class="dot"></span> Verified on-chain</span> <span id="dQualified"></span></h1></div><button id="switchId" class="btn btn-secondary btn-sm">Look up another ID</button></div>
|
||||||
<div id="dFacts" class="facts" style="grid-template-columns:repeat(3,1fr);margin:18px 0"></div>
|
<div id="dFacts" class="facts" style="grid-template-columns:repeat(3,1fr);margin:18px 0"></div>
|
||||||
<div class="table-card" style="margin-bottom:18px"><h2 style="margin:0 0 4px">Your team</h2><p style="color:var(--muted);font-size:13px;margin:0 0 14px">Your position's matrix — the rollup line on each card counts everyone underneath, all the way down. Open slots are where the next placements land.</p><div id="dTree"></div></div>
|
<div class="table-card" style="margin-bottom:18px"><div style="display:flex;justify-content:space-between;gap:12px;align-items:flex-start;flex-wrap:wrap"><div><h2 style="margin:0 0 4px">Your team</h2><p style="color:var(--muted);font-size:13px;margin:0 0 14px">Your position's matrix — the rollup line on each card counts everyone underneath, all the way down. Click a position to drill into that leg. Open slots are where the next placements land.</p></div><button id="dTreeToggle" class="btn btn-secondary btn-sm">List view</button></div><div id="dTreeNav" style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:12px"></div><div id="dTree"></div><p class="micro" style="margin:14px 0 0"><span class="mtp-qmark" style="position:static;display:inline-grid;vertical-align:middle">✓</span> qualified (2/2 directs) · <span class="mt-badge mt-prem">P</span> Premium · <span class="mt-badge">S</span> Standard · ⬇ everyone below that position (all generations) and the POL they've earned</p></div>
|
||||||
|
<div class="table-card" style="margin-bottom:18px"><h2 style="margin:0 0 4px">Share this position</h2><div id="dShare"></div></div>
|
||||||
<div class="table-card" style="margin-bottom:18px"><h2 style="margin:0 0 4px">Payments received</h2><p style="color:var(--muted);font-size:13px;margin:0 0 10px">Every payment your position has received, straight from the smart contract.</p><div id="dIncome"></div></div>
|
<div class="table-card" style="margin-bottom:18px"><h2 style="margin:0 0 4px">Payments received</h2><p style="color:var(--muted);font-size:13px;margin:0 0 10px">Every payment your position has received, straight from the smart contract.</p><div id="dIncome"></div></div>
|
||||||
<div class="table-card"><h2 style="margin:0 0 4px">Your lineage</h2><p id="dLineage" style="color:var(--muted);font-size:14px;line-height:1.8;margin:8px 0 0"></p></div>
|
<div class="table-card"><h2 style="margin:0 0 4px">Your lineage</h2><p id="dLineage" style="color:var(--muted);font-size:14px;line-height:1.8;margin:8px 0 0"></p></div>
|
||||||
<div class="notice" style="margin-top:18px"><strong>Team reminder:</strong> once your two directs are placed, retire your link and help your two get their two — always send new members through the <a href="/start" style="color:var(--gold)">current team sponsor page</a>.</div>
|
<div class="notice" style="margin-top:18px"><strong>Team reminder:</strong> once your two directs are placed, retire your link and help your two get their two — always send new members through the <a href="/start" style="color:var(--gold)">current team sponsor page</a>.</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<footer class="wrap disclaimer">All figures are read live from the RM Circle smart contract on Polygon and are historical facts, not a promise of future results. Participation involves cryptocurrency and smart-contract risk. Never use funds you cannot afford to lose.</footer>
|
<footer class="wrap disclaimer">All figures are read live from the RM Circle smart contract on Polygon and are historical facts, not a promise of future results. Participation involves cryptocurrency and smart-contract risk. Never use funds you cannot afford to lose.</footer>
|
||||||
<script src="/track.js"></script><script src="/my.js"></script><script src="/payouts.js" defer></script><script src="/chat.js" defer></script></body></html>
|
<script src="/track.js"></script><script src="/qrlib.js"></script><script src="/my.js"></script><script src="/payouts.js" defer></script><script src="/chat.js" defer></script></body></html>
|
||||||
|
|||||||
+62
-5
@@ -6,20 +6,54 @@
|
|||||||
|
|
||||||
function pathId(){const m=location.pathname.match(/^\/my\/(\d+)$/);if(m)return m[1];const q=new URLSearchParams(location.search).get('id');if(q&&/^\d+$/.test(q))return q;try{return localStorage.getItem('ctb.myId')||''}catch(e){return ''}}
|
function pathId(){const m=location.pathname.match(/^\/my\/(\d+)$/);if(m)return m[1];const q=new URLSearchParams(location.search).get('id');if(q&&/^\d+$/.test(q))return q;try{return localStorage.getItem('ctb.myId')||''}catch(e){return ''}}
|
||||||
|
|
||||||
|
const treeUI={byId:{},parent:{},homeId:0,rootId:0,mode:'pyramid'};
|
||||||
|
function indexTree(n,par){if(!n)return;treeUI.byId[n.id]=n;if(par)treeUI.parent[n.id]=par.id;indexTree(n.left,n);indexTree(n.right,n)}
|
||||||
function card(n,focus){
|
function card(n,focus){
|
||||||
if(!n)return '<div class="mtp-card mtp-open">open<br>slot</div>';
|
if(!n)return '<div class="mtp-card mtp-open">open<br>slot</div>';
|
||||||
const badge=n.tier===2?'<span class="mt-badge mt-prem">P</span>':'<span class="mt-badge">S</span>';
|
const badge=n.tier===2?'<span class="mt-badge mt-prem">P</span>':'<span class="mt-badge">S</span>';
|
||||||
const qmark=n.directCount>=2?'<span class="mtp-qmark" title="Qualified — 2/2 directs">✓</span>':'';
|
const qmark=n.directCount>=2?'<span class="mtp-qmark" title="Qualified — 2/2 directs">✓</span>':'';
|
||||||
const down=n.downCount?`<span class="mtp-down">⬇ ${n.downCount} below · ${fmt(n.downPol)} POL</span>`:'<span class="mtp-down mtp-down-none">no downline yet</span>';
|
const down=n.downCount?`<span class="mtp-down">⬇ ${n.downCount} below · ${fmt(n.downPol)} POL</span>`:'<span class="mtp-down mtp-down-none">no downline yet</span>';
|
||||||
return `<div class="mtp-card${focus?' mtp-focus':''}" style="cursor:default">${qmark}${badge} <span class="mt-id">#${n.id}</span><span class="mtp-lvl">${esc(n.levelName)}</span><span class="mtp-sub">${n.directCount}/2 · ${fmt(n.earnedPol)} POL</span>${down}</div>`;
|
return `<button class="mtp-card${focus?' mtp-focus':''}" data-tid="${n.id}">${qmark}${badge} <span class="mt-id">#${n.id}</span><span class="mtp-lvl">${esc(n.levelName)}</span><span class="mtp-sub">${n.directCount}/2 · ${fmt(n.earnedPol)} POL</span>${down}</button>`;
|
||||||
}
|
}
|
||||||
function renderTree(root){
|
function renderPyramid(){
|
||||||
if(!root){document.getElementById('dTree').innerHTML='<div class="empty">Team view is still indexing — check back in a few minutes.</div>';return}
|
const out=document.getElementById('dTree'),nav=document.getElementById('dTreeNav');
|
||||||
|
const root=treeUI.byId[treeUI.rootId];
|
||||||
|
if(!root){out.innerHTML='<div class="empty">Team view is still indexing — check back in a few minutes.</div>';nav.innerHTML='';return}
|
||||||
const rows=[[root]];
|
const rows=[[root]];
|
||||||
for(let g=1;g<4;g++)rows.push(rows[g-1].flatMap(n=>n?[n.left||null,n.right||null]:[null,null]));
|
for(let g=1;g<4;g++)rows.push(rows[g-1].flatMap(n=>n?[n.left||null,n.right||null]:[null,null]));
|
||||||
if(!rows[3].some(Boolean))rows.pop();
|
if(!rows[3].some(Boolean))rows.pop();
|
||||||
document.getElementById('dTree').innerHTML=`<div class="mtp">${rows.map((r,i)=>`<div class="mtp-row">${r.map(n=>card(n,i===0&&n&&true)).join('')}</div>`).join('')}</div>`;
|
out.innerHTML=`<div class="mtp">${rows.map((r,i)=>`<div class="mtp-row">${r.map(n=>card(n,i===0)).join('')}</div>`).join('')}</div>`;
|
||||||
|
const path=[];let cur=treeUI.rootId;
|
||||||
|
while(cur){path.unshift(cur);if(cur===treeUI.homeId)break;cur=treeUI.parent[cur]}
|
||||||
|
nav.innerHTML=path.length>1?`<span class="micro" style="margin:0">Viewing:</span> `+path.map((id,i)=>i===path.length-1?`<strong class="mt-id">#${id}</strong>`:`<button class="btn btn-secondary btn-sm" data-tid="${id}">#${id}</button>`).join(' <span style="color:var(--muted)">›</span> ')+` <button class="btn btn-secondary btn-sm" data-tid="${treeUI.parent[treeUI.rootId]}">↑ Up one</button>`:'';
|
||||||
|
const wire=el=>el.querySelectorAll('[data-tid]').forEach(b=>b.addEventListener('click',()=>{const t=Number(b.dataset.tid);if(t&&treeUI.byId[t]){treeUI.rootId=t;renderTree()}}));
|
||||||
|
wire(out);wire(nav);
|
||||||
}
|
}
|
||||||
|
function renderList(){
|
||||||
|
const out=document.getElementById('dTree');
|
||||||
|
document.getElementById('dTreeNav').innerHTML='';
|
||||||
|
const home=treeUI.byId[treeUI.homeId];
|
||||||
|
if(!home){out.innerHTML='<div class="empty">Team view is still indexing — check back in a few minutes.</div>';return}
|
||||||
|
const node=(n,depth)=>{
|
||||||
|
if(!n)return '';
|
||||||
|
const ch=[n.left,n.right].filter(Boolean);
|
||||||
|
const qmark=n.directCount>=2?'<span class="mtp-qmark" style="position:static;display:inline-grid;width:16px;height:16px;font-size:10px;vertical-align:middle">✓</span> ':'';
|
||||||
|
const badge=n.tier===2?'<span class="mt-badge mt-prem">P</span>':'<span class="mt-badge">S</span>';
|
||||||
|
const label=`${qmark}${badge} <span class="mt-id">#${n.id}</span> <span class="mt-meta">${esc(n.levelName)} · ${n.directCount}/2 directs · ${fmt(n.earnedPol)} POL${n.downCount?` · ⬇ ${n.downCount} below (${fmt(n.downPol)} POL)`:''}</span>`;
|
||||||
|
const kids=ch.length?`<ul class="mt-kids">${ch.map(c=>node(c,depth+1)).join('')}</ul>`:'';
|
||||||
|
return ch.length?`<li class="mt-node"><details${depth<3?' open':''}><summary>${label}</summary>${kids}</details></li>`:`<li class="mt-node mt-leaf">${label}</li>`;
|
||||||
|
};
|
||||||
|
out.innerHTML=`<ul class="mt-tree">${node(home,0)}</ul>`;
|
||||||
|
}
|
||||||
|
function renderTree(){
|
||||||
|
const tb=document.getElementById('dTreeToggle');
|
||||||
|
if(tb)tb.textContent=treeUI.mode==='pyramid'?'List view':'Pyramid view';
|
||||||
|
if(treeUI.mode==='pyramid')renderPyramid();else renderList();
|
||||||
|
}
|
||||||
|
document.getElementById('dTreeToggle').addEventListener('click',()=>{
|
||||||
|
treeUI.mode=treeUI.mode==='pyramid'?'list':'pyramid';
|
||||||
|
renderTree();
|
||||||
|
});
|
||||||
function render(d){
|
function render(d){
|
||||||
prompt.classList.add('hidden');dash.classList.remove('hidden');
|
prompt.classList.add('hidden');dash.classList.remove('hidden');
|
||||||
document.getElementById('dTitle').textContent='#'+d.id;
|
document.getElementById('dTitle').textContent='#'+d.id;
|
||||||
@@ -34,15 +68,38 @@
|
|||||||
`<div class="fact"><small>Total received</small><strong style="color:var(--ok)">${fmt(d.totalEarnedPol)} POL</strong></div>`+
|
`<div class="fact"><small>Total received</small><strong style="color:var(--ok)">${fmt(d.totalEarnedPol)} POL</strong></div>`+
|
||||||
`<div class="fact"><small>Team below you</small><strong>${d.subtree?`${d.subtree.downCount} member${d.subtree.downCount===1?'':'s'}`:'—'}</strong></div>`+
|
`<div class="fact"><small>Team below you</small><strong>${d.subtree?`${d.subtree.downCount} member${d.subtree.downCount===1?'':'s'}`:'—'}</strong></div>`+
|
||||||
`<div class="fact"><small>Member since</small><strong>${date(d.joinedAt)}</strong></div>`;
|
`<div class="fact"><small>Member since</small><strong>${date(d.joinedAt)}</strong></div>`;
|
||||||
renderTree(d.subtree);
|
treeUI.byId={};treeUI.parent={};treeUI.homeId=d.id;treeUI.rootId=d.id;
|
||||||
|
indexTree(d.subtree,null);
|
||||||
|
renderTree();
|
||||||
const inc=d.income||[];
|
const inc=d.income||[];
|
||||||
document.getElementById('dIncome').innerHTML=inc.length
|
document.getElementById('dIncome').innerHTML=inc.length
|
||||||
?`<div class="table-wrap"><table class="table" style="min-width:520px"><thead><tr><th>When</th><th>From member</th><th>For</th><th>Amount</th></tr></thead><tbody>${inc.map(p=>`<tr><td>${date(p.ts)}</td><td>#${p.fromId}</td><td>${esc(p.desc)}</td><td><strong>${fmt(p.pol)} POL</strong></td></tr>`).join('')}</tbody></table></div>`
|
?`<div class="table-wrap"><table class="table" style="min-width:520px"><thead><tr><th>When</th><th>From member</th><th>For</th><th>Amount</th></tr></thead><tbody>${inc.map(p=>`<tr><td>${date(p.ts)}</td><td>#${p.fromId}</td><td>${esc(p.desc)}</td><td><strong>${fmt(p.pol)} POL</strong></td></tr>`).join('')}</tbody></table></div>`
|
||||||
:'<div class="empty">No payments yet — they appear here the moment they land on-chain.</div>';
|
:'<div class="empty">No payments yet — they appear here the moment they land on-chain.</div>';
|
||||||
|
renderShare(d);
|
||||||
document.getElementById('dLineage').innerHTML=d.uplineChain&&d.uplineChain.length
|
document.getElementById('dLineage').innerHTML=d.uplineChain&&d.uplineChain.length
|
||||||
?`<strong style="color:var(--text)">#${d.id}</strong> → ${d.uplineChain.map(i=>'#'+i).join(' → ')} <span style="color:#8498aa">(root)</span>`
|
?`<strong style="color:var(--text)">#${d.id}</strong> → ${d.uplineChain.map(i=>'#'+i).join(' → ')} <span style="color:#8498aa">(root)</span>`
|
||||||
:'You are at the top of your line.';
|
:'You are at the top of your line.';
|
||||||
}
|
}
|
||||||
|
function qrSvg(url){
|
||||||
|
try{
|
||||||
|
const q=qrcode(0,'M');q.addData(url);q.make();
|
||||||
|
return q.createSvgTag({cellSize:4,margin:3,scalable:true});
|
||||||
|
}catch(e){return ''}
|
||||||
|
}
|
||||||
|
function renderShare(d){
|
||||||
|
const el=document.getElementById('dShare');
|
||||||
|
if(!el)return;
|
||||||
|
const dashUrl=`https://rmcircle.saasy.top/my/${d.id}`;
|
||||||
|
if(d.directCount>=2){
|
||||||
|
el.innerHTML=`<p style="color:var(--muted);font-size:14px;line-height:1.6;margin:6px 0 14px"><span class="q-badge q-yes" style="margin-right:6px">★ Qualified</span> This position has its 2 directs, so its referral link is retired under the team strategy. Send new members through the team rotation — the current sponsor is always live on the start page.</p><div style="display:flex;gap:16px;align-items:center;flex-wrap:wrap"><img src="/qr-start.svg" alt="Team QR code" class="qr-img"><div><a class="btn btn-primary" href="/start">Open the Team Sponsor Page →</a><p class="micro" style="margin:10px 0 0">This QR sends prospects to the team rotation — show it or print it.</p></div></div>`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const need=2-d.directCount;
|
||||||
|
el.innerHTML=`<p style="color:var(--muted);font-size:14px;line-height:1.6;margin:6px 0 14px">You need <strong style="color:var(--text)">${need} more direct${need===1?'':'s'}</strong> to qualify. Until then this is your personal recruiting page — share the link or let a prospect scan the code. They'll see this position's live results, and the join button below carries <strong style="color:var(--gold)">your ID #${d.id}</strong> as sponsor.</p><div style="display:flex;gap:16px;align-items:center;flex-wrap:wrap"><span id="dQr" class="qr-img"></span><div style="min-width:220px"><div style="display:flex;gap:8px;flex-wrap:wrap"><button id="copyShare" class="btn btn-secondary btn-sm">Copy my page link</button><a class="btn btn-primary btn-sm" href="${esc(d.referralUrl||'#')}" target="_blank" rel="noopener noreferrer">Join under #${d.id} →</a></div><p class="micro" style="margin:10px 0 0;word-break:break-all">${esc(dashUrl)}</p><p class="micro" style="margin:6px 0 0">When you reach 2/2 this page automatically retires your link and points new members to the team rotation.</p></div></div>`;
|
||||||
|
const qr=document.getElementById('dQr');if(qr)qr.innerHTML=qrSvg(dashUrl);
|
||||||
|
const cp=document.getElementById('copyShare');
|
||||||
|
if(cp)cp.addEventListener('click',async()=>{try{await navigator.clipboard.writeText(dashUrl);cp.textContent='Copied ✓';setTimeout(()=>cp.textContent='Copy my page link',1400)}catch(e){}});
|
||||||
|
}
|
||||||
async function load(id){
|
async function load(id){
|
||||||
try{
|
try{
|
||||||
const r=await fetch('/api/public/member?id='+id);
|
const r=await fetch('/api/public/member?id='+id);
|
||||||
|
|||||||
+2297
File diff suppressed because it is too large
Load Diff
@@ -114,3 +114,7 @@ a{color:inherit}.wrap{width:min(1160px,calc(100% - 32px));margin:auto}.nav{heigh
|
|||||||
.qr-cta small{color:var(--muted);font-weight:600}
|
.qr-cta small{color:var(--muted);font-weight:600}
|
||||||
@media(min-width:561px){.hero-actions{align-items:center}.qr-cta{order:3;flex-basis:100%;justify-content:center;margin-top:8px}}
|
@media(min-width:561px){.hero-actions{align-items:center}.qr-cta{order:3;flex-basis:100%;justify-content:center;margin-top:8px}}
|
||||||
@media(max-width:560px){.qr-cta{justify-content:center}}
|
@media(max-width:560px){.qr-cta{justify-content:center}}
|
||||||
|
|
||||||
|
.qr-img{width:120px;height:120px;background:#fff;padding:7px;border-radius:12px;display:block;flex:0 0 auto}
|
||||||
|
.qr-img svg,.qr-img img{width:100%;height:100%;display:block}
|
||||||
|
img.qr-img{object-fit:contain}
|
||||||
|
|||||||
@@ -266,6 +266,7 @@ async function handleApi(req,res,pathname){
|
|||||||
if(cached&&Date.now()-cached.ts<120000)return json(res,200,cached.data,{'Cache-Control':'public, max-age=60'});
|
if(cached&&Date.now()-cached.ts<120000)return json(res,200,cached.data,{'Cache-Control':'public, max-age=60'});
|
||||||
try{
|
try{
|
||||||
const r=await Promise.race([chain.memberPublic(id),new Promise((_,rej)=>setTimeout(()=>rej(new Error('Blockchain lookup timed out — try again.')),20000))]);
|
const r=await Promise.race([chain.memberPublic(id),new Promise((_,rej)=>setTimeout(()=>rej(new Error('Blockchain lookup timed out — try again.')),20000))]);
|
||||||
|
if(r.registered)r.referralUrl=`${getConfig().dappReferralBaseUrl}${encodeURIComponent(id)}`;
|
||||||
memberCache.set(id,{data:r,ts:Date.now()});
|
memberCache.set(id,{data:r,ts:Date.now()});
|
||||||
if(memberCache.size>500)memberCache.delete(memberCache.keys().next().value);
|
if(memberCache.size>500)memberCache.delete(memberCache.keys().next().value);
|
||||||
return json(res,200,r,{'Cache-Control':'public, max-age=60'});
|
return json(res,200,r,{'Cache-Control':'public, max-age=60'});
|
||||||
|
|||||||
Reference in New Issue
Block a user