diff --git a/public/admin.html b/public/admin.html index e97dd9a..12400e1 100644 --- a/public/admin.html +++ b/public/admin.html @@ -306,6 +306,6 @@ - + diff --git a/public/assets/admin.js b/public/assets/admin.js index a26158f..9ebf2ea 100644 --- a/public/assets/admin.js +++ b/public/assets/admin.js @@ -276,11 +276,11 @@ const q = ($('memFilter').value || '').trim().toLowerCase(); const list = allMembers.filter(a => !q || [a.email, a.username, a.memberId, a.sponsorRef, a.address, a.code].join(' ').toLowerCase().includes(q)); $('memSub').textContent = list.length + ' of ' + allMembers.length; - $('memTable').innerHTML = 'EmailUsernameMember #WalletSponsorViaCodeJoined' + $('memTable').innerHTML = 'EmailUsernameMember #WalletSponsorPositionsViaCodeJoined' + list.map(a => '' + esc(a.email) + '' + (a.username ? '@' + esc(a.username) : 'none') + '' + '' + (a.memberId ? '#' + a.memberId : 'free') + '' + '' + (a.address ? esc(a.address.slice(0, 8) + '…' + a.address.slice(-6)) : 'none') + '' - + '' + esc(a.sponsorRef || '') + '' + esc(a.joinedVia || '') + '' + esc(a.code || '') + '' + + '' + esc(a.sponsorRef || '') + '' + (a.positions ? a.positions : '0') + '' + esc(a.joinedVia || '') + '' + esc(a.code || '') + '' + '' + when(a.created) + '' + '').join(''); } diff --git a/public/my.html b/public/my.html index 7b9fe07..edd044f 100644 --- a/public/my.html +++ b/public/my.html @@ -373,6 +373,7 @@

Your own money, your own wallets, a faster start. It is not an income promise: qualification only pays on future purchases in your line, and every purchase here is a real ad package you can spend.

+

Positions are for qualifying. Up to five per account, each registered directly under your main member. Once you are qualified, buy from your main wallet: that keeps your own sponsor paid in full, the same way your people's purchases pay you.

Qualified Start calculator

diff --git a/server.js b/server.js index ff5aaf0..19e2781 100644 --- a/server.js +++ b/server.js @@ -717,6 +717,19 @@ const server = http.createServer(async (req, res) => { // Qualified Start: a second (third…) wallet on the same account. It becomes // its own on-chain member under this member's id when it buys; the session // stays on the main wallet. + // policy (Marty, 2026-09-10): positions exist to qualify, so at most five per account, and a + // wallet that is already registered on-chain under anything other than this account's main + // member is refused (a position-under-position chain would recapture 70% of a self-buy) + const MAX_POS = 5; + const have = await accounts.positions(s.email); + if (!have.find(p => p.address === r.address.toLowerCase()) && have.length >= MAX_POS) + return json(res, 400, { error: 'You can link up to ' + MAX_POS + ' positions, which is everything Qualified Start needs. Once qualified, buy from your main wallet so your sponsor is paid in full.' }); + if (memberId) { + const mainId = await auth.refreshMemberId(s); + let mm = null; try { mm = await chain.member(memberId); } catch (e) {} + if (mm && mainId && mm.sponsorId !== mainId) + return json(res, 400, { error: 'That wallet is already registered on-chain under ' + (mm.sponsorId ? 'member #' + mm.sponsorId : 'no sponsor') + ', not under your main member #' + mainId + '. Only positions registered directly under you can be linked.' }); + } const pr = await accounts.addPosition(s.email, r.address); if (pr.error) return json(res, 400, pr); if (memberId) await accounts.setPositionMember(r.address, memberId); @@ -1641,7 +1654,9 @@ const server = http.createServer(async (req, res) => { } if (p === '/api/admin/members' && req.method === 'GET') { if (!isAdmin(req)) return json(res, 401, { error: 'auth' }); - return json(res, 200, { members: await accounts.listAll(500) }); + const members = await accounts.listAll(500); + for (const m of members) { try { const ps = await accounts.positions(m.email); m.positions = ps.length; m.positionIds = ps.map(p => p.memberId).filter(Boolean); } catch (e) { m.positions = 0; } } + return json(res, 200, { members }); } if (p === '/api/admin/members' && req.method === 'PATCH') { if (!isAdmin(req)) return json(res, 401, { error: 'auth' });