/** * Portal passwordless auth — talks to targo-hub /portal/*. * * Flow: * 1. Customer lands on /#/login (no token, no session). * 2. Types email or phone, hits "Recevoir mon lien". * 3. requestPortalLink() POSTs the identifier to the hub. * 4. Hub looks up the Customer, mints a 24h JWT, sends via SMS + email. * 5. Customer clicks the link in their inbox → portal.gigafibre.ca/#/?token=JWT. * 6. useMagicToken() decodes it on page load, hydrates the customer store. * * The hub always returns 200 OK (anti-enumeration), so the only * non-success response the UI should handle is 429 (rate limit). */ const HUB = location.hostname === 'localhost' ? 'http://localhost:3300' : 'https://msg.gigafibre.ca' export async function requestPortalLink (identifier) { const r = await fetch(HUB + '/portal/request-link', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ identifier }), }) const data = await r.json().catch(() => ({})) if (r.status === 429) { const err = new Error(data.message || 'Trop de tentatives. Réessayez plus tard.') err.code = 'rate_limit' err.retryAfterSec = data.retry_after_sec || 900 throw err } if (!r.ok) throw new Error(data.error || `Hub ${r.status}`) return data }