// Debug helper: post a JWT token to BroadcastPanel and wait for StudioPortal to react // Usage: E2E_TOKEN=... node debug-post-token.js const puppeteer = require('puppeteer') const fs = require('fs') const path = require('path') require('dotenv').config() const BROWSERLESS_WS = process.env.BROWSERLESS_WS || process.env.BROWSERLESS || '' const CHROME_PATH = process.env.CHROME_PATH || '/usr/bin/chromium' const BROADCAST_URL = process.env.VITE_BROADCASTPANEL_URL || 'https://avanzacast-broadcastpanel.bfzqqk.easypanel.host' let TOKEN = process.env.E2E_TOKEN || process.env.TOKEN || '' const OUT = process.cwd() const TOKEN_SERVER = (process.env.VITE_TOKEN_SERVER_URL || process.env.TOKEN_SERVER_URL || '').replace(/\/$/, '') async function fetchTokenFromServer(room = 'e2e-room', username = 'cli-run') { if (!TOKEN_SERVER) return null try { console.info('Requesting token from token server:', TOKEN_SERVER) const res = await fetch(`${TOKEN_SERVER}/api/session`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ room, username }) }) const text = await res.text() let json = null try { json = JSON.parse(text) } catch (e) { console.warn('Token server returned non-json:', text) } if (!res.ok) { console.warn('Token server returned', res.status, text) return null } if (json) { if (json.token) return json.token if (json.redirectUrl) { try { const u = new URL(json.redirectUrl) const t = u.searchParams.get('token') if (t) return t } catch (e) {} } if (json.id) { // try GET /api/session/:id try { const r2 = await fetch(`${TOKEN_SERVER}/api/session/${encodeURIComponent(json.id)}`) if (r2.ok) { const j2 = await r2.json() if (j2.token) return j2.token } } catch (e) {} } } return null } catch (e) { console.warn('Failed to fetch token from server', e && e.message ? e.message : e) return null } } async function getBrowser() { if (BROWSERLESS_WS) { try { console.info('Connecting to browserless:', BROWSERLESS_WS) return await puppeteer.connect({ browserWSEndpoint: BROWSERLESS_WS, defaultViewport: { width: 1280, height: 800 } }) } catch (e) { console.warn('browserless connect failed', e && e.message) } } console.info('Launching local Chromium at', CHROME_PATH) return await puppeteer.launch({ executablePath: CHROME_PATH, headless: 'new', args: ['--no-sandbox','--disable-setuid-sandbox','--disable-dev-shm-usage'], defaultViewport: { width: 1280, height: 800 } }) } async function run() { // if no token, try to fetch one from token server automatically if (!TOKEN) { console.log('No E2E token provided, attempting to fetch from token server...') const fetched = await fetchTokenFromServer() if (fetched) { TOKEN = fetched console.log('Fetched token length=', String(TOKEN).length) } else { console.error('No token provided and token server fetch failed. Set E2E_TOKEN env var or configure VITE_TOKEN_SERVER_URL') process.exit(2) } } if (!TOKEN) { console.error('No token provided. Set E2E_TOKEN env var.') process.exit(2) } const browser = await getBrowser() const page = await browser.newPage() try { page.on('console', msg => console.log('[page]', msg.type(), msg.text())) page.on('pageerror', err => console.error('[pageerr]', err && err.stack ? err.stack : err)) console.log('Navigating to', BROADCAST_URL) await page.goto(BROADCAST_URL, { waitUntil: 'networkidle2', timeout: 60000 }) await page.waitForTimeout(1000) // clear local state that may contain old tokens await page.evaluate(() => { try { localStorage.removeItem('lk-user-choices') } catch(e) {} try { sessionStorage.clear() } catch(e) {} }) const shotBefore = path.join(OUT, 'debug-before.png') await page.screenshot({ path: shotBefore, fullPage: true }) console.log('Saved screenshot', shotBefore) const payload = { type: 'LIVEKIT_TOKEN', token: TOKEN, url: process.env.VITE_LIVEKIT_WS_URL || '', room: 'e2e-room' } console.log('Posting token to page... token length=', String(TOKEN).length) // Attempt to post into iframe if present, else to window await page.evaluate((payload) => { try { const iframe = document.querySelector('iframe#studio-portal') if (iframe && iframe.contentWindow) { iframe.contentWindow.postMessage(payload, '*') window.__e2e_posted = 'iframe' } else { window.postMessage(payload, '*') window.__e2e_posted = 'self' } } catch (e) { try { window.postMessage(payload, '*'); window.__e2e_posted = 'self' } catch(_) { window.__e2e_posted = 'failed' } } }, payload) // Wait for StudioPortal to show token received or show error/connected const start = Date.now() const timeout = 25000 let state = { status: 'unknown' } while (Date.now() - start < timeout) { const r = await page.evaluate(() => { const out = { tokenFlag: !!(window as any).__e2e_posted, tokenTarget: (window as any).__e2e_posted || null } try { const tokenNotice = Array.from(document.querySelectorAll('div, span')) .map(n => (n.textContent||'').toLowerCase()) .find(t => t.includes('token recibido') || t.includes('esperando token') || t.includes('token')) if (tokenNotice) out.tokenNotice = tokenNotice } catch(e) {} // detect studio connected element (common selectors) const connected = !!document.querySelector('.studio-status[data-status="connected"]') if (connected) out.connected = true // detect error modal const err = document.querySelector('.studio-error-modal') if (err) out.error = (err.textContent||'').trim() return out }) if (r.connected) { state = { status: 'connected', detail: r }; break } if (r.error) { state = { status: 'error', detail: r }; break } if (r.tokenFlag) state = { status: 'posted', detail: r } await page.waitForTimeout(600) } const shotAfter = path.join(OUT, 'debug-after.png') await page.screenshot({ path: shotAfter, fullPage: true }) console.log('Saved screenshot', shotAfter) const outPath = path.join(OUT, 'debug-post-result.json') await fs.promises.writeFile(outPath, JSON.stringify({ state, timestamp: Date.now() }, null, 2), 'utf8') console.log('Wrote result to', outPath) console.log('STATE:', JSON.stringify(state, null, 2)) await browser.close() return state } catch (err) { console.error('Runner error', err && err.stack ? err.stack : err) try { await browser.close() } catch(e) {} process.exit(3) } } run().catch(e=>{ console.error(e); process.exit(5) })