/** * Build Verification Script * * Extracts and validates environment variables embedded in the built bundle. * Ensures redirect URIs match the expected deployment target. * * Usage: * BUILD_TARGET=docker-local npm run build:verify * BUILD_TARGET=production npm run build:verify */ import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Expected redirect URIs for each deployment target const EXPECTED_URIS = { 'docker-local': 'http://localhost:8099/oauth/callback', production: 'https://app.pokedex.online/oauth/callback' }; // ANSI colors const colors = { red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', reset: '\x1b[0m' }; function log(color, symbol, message) { console.log(`${colors[color]}${symbol}${colors.reset} ${message}`); } function findBuiltAssets() { const distPath = path.resolve(__dirname, '../dist/assets'); if (!fs.existsSync(distPath)) { throw new Error('dist/assets directory not found. Run build first.'); } const files = fs.readdirSync(distPath); const jsFiles = files.filter( f => f.endsWith('.js') && f.startsWith('index-') ); if (jsFiles.length === 0) { throw new Error('No built JavaScript files found in dist/assets/'); } return jsFiles.map(f => path.join(distPath, f)); } function extractRedirectUri(content) { // Look for the Discord redirect URI in the built bundle const patterns = [ /VITE_DISCORD_REDIRECT_URI[\"']?\s*[:=]\s*[\"']([^\"']+)[\"']/, /discord_redirect_uri[\"']?\s*[:=]\s*[\"']([^\"']+)[\"']/i, /redirectUri[\"']?\s*[:=]\s*[\"']([^\"']+oauth\/callback)[\"']/, /(https?:\/\/[^\"'\s]+\/oauth\/callback)/ ]; for (const pattern of patterns) { const match = content.match(pattern); if (match && match[1]) { return match[1]; } } return null; } function verifyBuild() { console.log('\nšŸ” Build Verification\n'); // Get target from environment variable const target = process.env.BUILD_TARGET || 'local'; if (!EXPECTED_URIS[target]) { log('red', 'āŒ', `Invalid BUILD_TARGET: ${target}`); log('blue', 'ℹ', 'Valid targets: docker-local, production'); process.exit(1); } const expectedUri = EXPECTED_URIS[target]; log('blue', 'ℹ', `Deployment target: ${target}`); log('blue', 'ℹ', `Expected redirect URI: ${expectedUri}`); try { // Find built assets const assetFiles = findBuiltAssets(); log('green', 'āœ…', `Found ${assetFiles.length} built asset(s)`); // Search for redirect URI in all assets let foundUri = null; for (const assetFile of assetFiles) { const content = fs.readFileSync(assetFile, 'utf8'); const uri = extractRedirectUri(content); if (uri) { foundUri = uri; break; } } if (!foundUri) { log('yellow', '⚠', 'Could not find Discord redirect URI in bundle'); log('blue', 'ℹ', 'This may be OK if OAuth is not used'); process.exit(0); } log('blue', 'ℹ', `Found redirect URI: ${foundUri}`); // Validate URI matches expected if (foundUri === expectedUri) { log('green', 'āœ…', 'Redirect URI matches expected value!'); console.log('\nāœ… Build verification passed\n'); process.exit(0); } else { log('red', 'āŒ', 'Redirect URI MISMATCH!'); log('blue', 'ℹ', `Expected: ${expectedUri}`); log('blue', 'ℹ', `Found: ${foundUri}`); console.log('\nāŒ Build verification failed\n'); console.log( 'This usually means the wrong .env.{mode} file was used during build.' ); console.log("Check that you're using the correct Vite mode flag:\n"); console.log( ' vite build --mode docker-local (for local Docker deployment)' ); console.log( ' vite build --mode production (for Synology deployment)\n' ); process.exit(1); } } catch (error) { log('red', 'āŒ', `Verification error: ${error.message}`); process.exit(1); } } // Run verification verifyBuild();