PolHunter hand-off, nav entry, and the ledger embed
/api/my/polhunter signs a member across to polhunter.com with a five-minute single-use HMAC token (memberId, email, wallet, username); PolHunter has no sign-up and no mailer, this is the only way in. A PolHunter entry in the dashboard nav. The PolHunter embed on the public ledger with one slot beside the payout feed; CSP allows polhunter.com. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -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.' });
|
||||
|
||||
Reference in New Issue
Block a user