diff --git a/public/assets/site.css b/public/assets/site.css index f772af3..2bb907b 100644 --- a/public/assets/site.css +++ b/public/assets/site.css @@ -269,6 +269,9 @@ textarea{resize:vertical;font:inherit} .bo-menu button{display:flex;align-items:center;gap:11px;background:transparent;border:0;color:var(--muted); font:600 14px var(--disp);padding:11px 12px;border-radius:11px;cursor:pointer;text-align:left; border-left:3px solid transparent} +.bo-menu a{display:flex;align-items:center;gap:11px;color:var(--muted);font:600 14px var(--disp);padding:11px 12px;border-radius:11px;text-align:left;border-left:3px solid transparent;text-decoration:none} +.bo-menu a:hover{color:var(--ink);background:rgba(67,232,195,.05)} +.bo-menu a svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;flex:0 0 auto} .bo-menu button svg{width:18px;height:18px;stroke:currentColor;fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round;flex:0 0 auto} .bo-menu button:hover{color:var(--ink);background:rgba(67,232,195,.05)} .bo-menu button.on{color:var(--mint);background:rgba(67,232,195,.09);border-left-color:var(--mint)} diff --git a/public/ledger.html b/public/ledger.html index d751a07..7e5e0de 100644 --- a/public/ledger.html +++ b/public/ledger.html @@ -30,6 +30,7 @@
+
Loading recent history…
@@ -39,6 +40,7 @@ + diff --git a/public/my.html b/public/my.html index bb1d203..9715f6d 100644 --- a/public/my.html +++ b/public/my.html @@ -5,7 +5,7 @@ Member area | InstantAdPay - + @@ -141,6 +141,7 @@ + PolHunter diff --git a/server.js b/server.js index 56ccbd3..92472b2 100644 --- a/server.js +++ b/server.js @@ -483,7 +483,7 @@ const MIME = { '.html': 'text/html; charset=utf-8', '.css': 'text/css', '.js': ' '.png': 'image/png', '.jpg': 'image/jpeg', '.svg': 'image/svg+xml', '.webp': 'image/webp', '.ico': 'image/x-icon', '.json': 'application/json', '.mp4': 'video/mp4', '.woff2': 'font/woff2', '.gif': 'image/gif', '.webm': 'video/webm', '.txt': 'text/plain; charset=utf-8', '.xml': 'application/xml; charset=utf-8' }; -const CSP = "default-src 'self'; script-src 'self' https://cdn.jsdelivr.net https://www.networkadspace.com https://networkadspace.com 'sha256-NzvNrqk5jB9YZATwo5BF4JoRlJ02HsnFikbKXgEPdaQ='; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: blob: https:; media-src 'self' https: blob:; connect-src 'self' https://*.walletconnect.com wss://*.walletconnect.com https://*.walletconnect.org wss://*.walletconnect.org https://*.reown.com wss://*.reown.com https://*.reown.org wss://*.reown.org https://*.web3modal.org https://*.drpc.org https://*.publicnode.com https://*.coinbase.com; font-src 'self' data: https://fonts.gstatic.com https://fonts.reown.com; form-action 'self'; frame-src https: http:"; +const CSP = "default-src 'self'; script-src 'self' https://cdn.jsdelivr.net https://www.networkadspace.com https://networkadspace.com https://polhunter.com 'sha256-NzvNrqk5jB9YZATwo5BF4JoRlJ02HsnFikbKXgEPdaQ='; worker-src 'self' blob:; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: blob: https:; media-src 'self' https: blob:; connect-src 'self' https://*.walletconnect.com wss://*.walletconnect.com https://*.walletconnect.org wss://*.walletconnect.org https://*.reown.com wss://*.reown.com https://*.reown.org wss://*.reown.org https://*.web3modal.org https://*.drpc.org https://*.publicnode.com https://*.coinbase.com; font-src 'self' data: https://fonts.gstatic.com https://fonts.reown.com; form-action 'self'; frame-src https: http:"; function baseHeaders(extra) { return Object.assign({ 'Content-Security-Policy': CSP, 'X-Content-Type-Options': 'nosniff', 'Referrer-Policy': 'strict-origin-when-cross-origin' }, extra || {}); @@ -2100,6 +2100,20 @@ const server = http.createServer(async (req, res) => { earnTokens.delete(s.email); // single use return json(res, 200, await ads.recordView(s.email)); } + // -- PolHunter hand-off (2026-09-19): sign the member across; polhunter.com verifies with the shared secret + if (p === '/api/my/polhunter' && req.method === 'GET') { + const s = await auth.fromRequest(req); + if (!s || !s.email) { res.writeHead(302, { Location: '/my' }); return res.end(); } + const secret = String(process.env.HUNT_SSO_SECRET || '').trim(); const huntUrl = String(process.env.HUNT_URL || 'https://polhunter.com').replace(/\/+$/, ''); + if (secret.length < 32) return json(res, 503, { error: 'PolHunter is not connected yet.' }); + const acct = await accounts.byEmail(s.email); + const memberId = (acct && acct.memberId) || s.memberId || 0; + if (!memberId) { res.writeHead(302, { Location: '/my#wallet' }); return res.end(); } + const b64u = b => Buffer.from(b).toString('base64').replace(/=+$/, '').replace(/\+/g, '-').replace(/\//g, '_'); + const payload = JSON.stringify({ iat: Date.now(), exp: Date.now() + 5 * 60000, nonce: crypto.randomBytes(12).toString('hex'), memberId: Number(memberId), email: s.email, wallet: (acct && acct.address) || s.address || null, username: (acct && acct.username) || null }); + const tok = b64u(payload) + '.' + b64u(crypto.createHmac('sha256', secret).update(payload).digest()); + res.writeHead(302, { Location: huntUrl + '/auth?t=' + tok, 'Cache-Control': 'no-store' }); return res.end(); + } if (p === '/api/my/claim' && req.method === 'POST') { const s = await auth.fromRequest(req); if (!s || !s.email) return json(res, 401, { error: 'Sign in first.' });