#!/usr/bin/env node /** * Verification Script for Navbar Login Fix * * This script verifies that the WebSocket authentication state synchronization * is working correctly after the navbar login fix deployment. * * Usage: * node scripts/verify-login-fix.js */ const https = require('https'); const http = require('http'); const API_URL = process.env.VITE_API_URL || 'https://api.turbotrades.dev'; const WS_URL = process.env.VITE_WS_URL || 'wss://api.turbotrades.dev/ws'; const colors = { reset: '\x1b[0m', bright: '\x1b[1m', green: '\x1b[32m', red: '\x1b[31m', yellow: '\x1b[33m', blue: '\x1b[34m', cyan: '\x1b[36m', }; function log(message, color = colors.reset) { console.log(`${color}${message}${colors.reset}`); } function success(message) { log(`✅ ${message}`, colors.green); } function error(message) { log(`❌ ${message}`, colors.red); } function warning(message) { log(`⚠️ ${message}`, colors.yellow); } function info(message) { log(`ℹ️ ${message}`, colors.cyan); } function section(message) { log(`\n${colors.bright}${message}${colors.reset}`); log('='.repeat(60)); } // Check if endpoint is reachable function checkEndpoint(url, description) { return new Promise((resolve) => { const urlObj = new URL(url); const client = urlObj.protocol === 'https:' ? https : http; const options = { hostname: urlObj.hostname, port: urlObj.port || (urlObj.protocol === 'https:' ? 443 : 80), path: urlObj.pathname, method: 'GET', timeout: 5000, }; const req = client.request(options, (res) => { if (res.statusCode >= 200 && res.statusCode < 400) { success(`${description}: Reachable (Status: ${res.statusCode})`); resolve(true); } else { warning(`${description}: Returned status ${res.statusCode}`); resolve(false); } }); req.on('error', (err) => { error(`${description}: ${err.message}`); resolve(false); }); req.on('timeout', () => { error(`${description}: Request timed out`); req.destroy(); resolve(false); }); req.end(); }); } // Check WebSocket endpoint function checkWebSocket(url) { return new Promise((resolve) => { try { const WebSocket = require('ws'); const ws = new WebSocket(url); const timeout = setTimeout(() => { ws.close(); error('WebSocket: Connection timeout'); resolve(false); }, 5000); ws.on('open', () => { clearTimeout(timeout); success('WebSocket: Connection successful'); ws.close(); resolve(true); }); ws.on('error', (err) => { clearTimeout(timeout); error(`WebSocket: ${err.message}`); resolve(false); }); } catch (err) { error(`WebSocket: ws module not available (${err.message})`); warning('Install with: npm install ws'); resolve(false); } }); } // Read and verify frontend build files function checkFrontendBuild() { const fs = require('fs'); const path = require('path'); const distPath = path.join(__dirname, '..', 'frontend', 'dist'); const indexPath = path.join(distPath, 'index.html'); if (!fs.existsSync(distPath)) { error('Frontend dist directory not found'); warning('Run: cd frontend && npm run build'); return false; } if (!fs.existsSync(indexPath)) { error('Frontend index.html not found in dist/'); return false; } const indexContent = fs.readFileSync(indexPath, 'utf8'); // Check for recent build by looking for assets const hasAssets = indexContent.includes('/assets/'); const hasModernJS = indexContent.includes('type="module"'); if (hasAssets && hasModernJS) { success('Frontend build exists and looks valid'); // Get build timestamp from file modification time const stats = fs.statSync(indexPath); const buildTime = stats.mtime; const now = new Date(); const ageMinutes = Math.floor((now - buildTime) / 1000 / 60); if (ageMinutes < 60) { success(`Build age: ${ageMinutes} minutes (recent)`); } else if (ageMinutes < 1440) { info(`Build age: ${Math.floor(ageMinutes / 60)} hours`); } else { warning(`Build age: ${Math.floor(ageMinutes / 1440)} days (consider rebuilding)`); } return true; } else { error('Frontend build appears invalid'); return false; } } // Check source files for the fix function checkSourceCode() { const fs = require('fs'); const path = require('path'); const websocketStorePath = path.join(__dirname, '..', 'frontend', 'src', 'stores', 'websocket.js'); const authStorePath = path.join(__dirname, '..', 'frontend', 'src', 'stores', 'auth.js'); if (!fs.existsSync(websocketStorePath)) { error('WebSocket store not found'); return false; } if (!fs.existsSync(authStorePath)) { error('Auth store not found'); return false; } const websocketCode = fs.readFileSync(websocketStorePath, 'utf8'); const authCode = fs.readFileSync(authStorePath, 'utf8'); // Check for the fix in websocket store const hasConnectedHandler = websocketCode.includes('case "connected":'); const callsFetchUser = websocketCode.includes('authStore.fetchUser()'); const declaresAuthStore = websocketCode.includes('const authStore = useAuthStore()'); if (hasConnectedHandler && callsFetchUser && declaresAuthStore) { success('WebSocket store has the navbar login fix'); } else { error('WebSocket store missing the fix'); if (!hasConnectedHandler) warning(' - Missing "connected" case handler'); if (!callsFetchUser) warning(' - Not calling authStore.fetchUser()'); if (!declaresAuthStore) warning(' - Not declaring authStore'); return false; } // Check for debug logging (optional) const hasDebugLogs = websocketCode.includes('🟢 WebSocket received') || authCode.includes('🔵 fetchUser called'); if (hasDebugLogs) { info('Debug logging is present (helpful for troubleshooting)'); } else { info('Debug logging not present (clean for production)'); } return true; } // Main verification flow async function main() { log('\n' + '='.repeat(60), colors.bright); log(' NAVBAR LOGIN FIX - VERIFICATION SCRIPT', colors.bright); log('='.repeat(60) + '\n', colors.bright); let allChecks = true; // 1. Check source code section('1. Checking Source Code'); const sourceCheck = checkSourceCode(); allChecks = allChecks && sourceCheck; // 2. Check frontend build section('2. Checking Frontend Build'); const buildCheck = checkFrontendBuild(); allChecks = allChecks && buildCheck; // 3. Check API endpoints section('3. Checking API Endpoints'); const apiHealthCheck = await checkEndpoint( `${API_URL}/api/health`, 'API Health Endpoint' ); const apiAuthCheck = await checkEndpoint( `${API_URL}/api/auth/me`, 'Auth Endpoint (/api/auth/me)' ); const publicConfigCheck = await checkEndpoint( `${API_URL}/api/config/public`, 'Public Config Endpoint' ); allChecks = allChecks && apiHealthCheck && apiAuthCheck && publicConfigCheck; // 4. Check WebSocket section('4. Checking WebSocket Connection'); const wsCheck = await checkWebSocket(WS_URL); allChecks = allChecks && wsCheck; // Summary section('Verification Summary'); if (allChecks) { success('All checks passed! ✨'); log('\n📋 Next Steps:', colors.bright); log(' 1. Deploy the frontend build to production'); log(' 2. Clear browser cache (Ctrl+Shift+R)'); log(' 3. Test Steam login in browser'); log(' 4. Verify navbar updates after login\n'); } else { error('Some checks failed!'); log('\n📋 Recommended Actions:', colors.bright); if (!sourceCheck) { log(' • Verify the code changes are committed'); log(' • Pull latest changes: git pull origin main'); } if (!buildCheck) { log(' • Rebuild frontend: cd frontend && npm run build'); } if (!apiHealthCheck || !apiAuthCheck || !publicConfigCheck) { log(' • Check backend is running: pm2 status'); log(' • Check backend logs: pm2 logs turbotrades-backend'); log(' • Verify Nginx configuration'); } if (!wsCheck) { log(' • Check WebSocket endpoint in Nginx config'); log(' • Verify backend WebSocket service is running'); log(' • Check firewall rules for WebSocket connections'); } log(''); } process.exit(allChecks ? 0 : 1); } // Run the script main().catch((err) => { error(`Unexpected error: ${err.message}`); console.error(err); process.exit(1); });