#!/usr/bin/env node const { chromium } = require('playwright'); const fs = require('fs'); const path = require('path'); const readline = require('readline'); const DOWNLOAD_DIR = path.dirname(__filename); const SESSION_FILE = path.join(DOWNLOAD_DIR, '.session.json'); const ENV_FILE = path.join(DOWNLOAD_DIR, 'env'); // Read configuration from env file function loadConfig() { const content = fs.readFileSync(ENV_FILE, 'utf8'); const config = {}; // Match $VAR = "value" patterns const matches = content.matchAll(/\$(\w+)\s*=\s*"([^"]*)"/g); for (const match of matches) { config[match[1]] = match[2]; } return config; } const config = loadConfig(); const PORTAL_URL = config.PORTAL_URL || 'https://portal.lwv.org'; const MEMBERSHIP_URL = config.MEMBERSHIP_URL || 'https://portal.lwv.org/groups/43a93df1-901a-4676-88c3-f4ea430d4884/league_membership_state_view'; function waitForEnter() { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); return new Promise(resolve => rl.question('', () => { rl.close(); resolve(); })); } async function saveSession() { console.log('Starting browser for session save...'); console.log(''); console.log('INSTRUCTIONS:'); console.log('1. A browser window will open'); console.log('2. Log in using the magic link method (email)'); console.log('3. After logging in and seeing the home page, come back here'); console.log('4. Press Enter to save the session'); console.log(''); console.log(`Portal URL: ${PORTAL_URL}`); console.log(`Membership URL: ${MEMBERSHIP_URL}`); console.log(''); const browser = await chromium.launch({ headless: false, devtools: true }); const context = await browser.newContext(); const page = await context.newPage(); try { // Go to portal await page.goto(PORTAL_URL, { waitUntil: 'networkidle', timeout: 60000 }); console.log('Browser opened. Please log in using magic link...'); console.log('Press Enter after you have logged in and can see the home page.'); // Wait for user to press Enter await waitForEnter(); // Navigate to membership page to verify access console.log('Navigating to membership page...'); await page.goto(MEMBERSHIP_URL, { waitUntil: 'networkidle', timeout: 60000 }); await page.waitForTimeout(2000); console.log('Membership page loaded. Saving session...'); // Get cookies and storage state const cookies = await context.cookies(); const storageState = await context.storageState(); // Save session data - indefinite expiry (set to 10 years) const sessionData = { cookies: cookies, storageState: storageState, savedAt: new Date().toISOString(), expiresAt: new Date(Date.now() + 3650 * 24 * 60 * 60 * 1000).toISOString() // 10 years }; fs.writeFileSync(SESSION_FILE, JSON.stringify(sessionData, null, 2)); console.log(`Session saved to ${SESSION_FILE}`); console.log(`Saved ${cookies.length} cookies`); // Verify session works by checking membership page console.log('Verifying session...'); const testPage = await context.newPage(); await testPage.goto(MEMBERSHIP_URL, { waitUntil: 'networkidle', timeout: 30000 }); const testContent = await testPage.content(); if (testContent.includes('Local League Membership') || testContent.includes('submit-export')) { console.log('Session verified successfully!'); } else { console.log('Warning: Session may not have full access'); } await testPage.close(); console.log('\nSession save complete! You can now run download-roster.js'); } catch (error) { console.error('Error:', error.message); throw error; } finally { await browser.close(); } } saveSession().catch(err => { console.error('Failed:', err); process.exit(1); });