diff --git a/scripts/README.md b/scripts/README.md index b28924924..72937f67c 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -6,14 +6,79 @@ This directory contains various scripts used for development, testing, and maint These scripts are for development and testing purposes only. They are not part of the main Pulse application and should not be used in production environments. -For more information about the development tools, see [README-dev-tools.md](README-dev-tools.md). - ## Available Scripts -- **generate-mock-data.js** - Generates simulated data for development and testing +### Core Scripts + +- **start.js** - Main launcher script that provides a menu to select which environment to start +- **install.sh** - Interactive installation and setup script +- **check-config.js** - Validates the configuration and checks for common issues +- **monitor-logs.js** - Real-time log monitoring with filtering capabilities +- **run-with-logs.js** - Runs the application and monitors logs in the same terminal + +### Development Scripts + +- **start-dev.sh/bat** - Start the application in development mode with mock data +- **start-mock-dev.sh/bat** - Start the application in development mode with mock data (alias for start-dev.sh) +- **start-mock-server.js** - Starts only the mock data server - **debug-socket.js** - Debug proxy for troubleshooting socket communication - **debug-proxy.sh** - Runs the application with the debug proxy -## Production Scripts +### Production Scripts -There are currently no production scripts in this directory. \ No newline at end of file +- **start-prod.sh/bat** - Start the application in production mode +- **docker-prod.sh** - Start the application in production mode with Docker + +### Utility Scripts + +- **check-connections.js** - Tests various connection methods to help diagnose issues +- **clear-data.js** - Clears cached data and resets the application state +- **configure-env.js** - Interactive script to configure the .env file +- **verify-cluster-config.js** - Verifies the cluster configuration + +## Which Script Should I Use? + +Instead of calling these scripts directly, you can use the npm scripts in the root directory: + +```bash +# Development mode with mock data (local) +npm run dev + +# Development mode with mock data (Docker) +npm run dev:docker + +# Production mode with real Proxmox data (local) +npm run prod + +# Production mode with real Proxmox data (Docker) +npm run prod:docker + +# Monitor logs +npm run logs + +# Check container status +npm run status + +# Restart the application +npm run restart + +# Stop the application +npm run stop + +# Clean up (remove containers, images, volumes) +npm run cleanup +``` + +## Using the Launcher + +You can also use the launcher in the root directory: + +```bash +# On Unix/Linux/macOS +./start.sh + +# On Windows +start.bat +``` + +The launcher provides a menu to select which environment to start, making it easier for users to choose the right option. \ No newline at end of file diff --git a/scripts/ai-helper.js b/scripts/ai-helper.js new file mode 100755 index 000000000..9a19aeea4 --- /dev/null +++ b/scripts/ai-helper.js @@ -0,0 +1,336 @@ +#!/usr/bin/env node + +/** + * AI Helper Script + * + * This script is designed to help AI assistants run commands and immediately see their output + * without getting stuck waiting for long-running processes to complete. + * + * It runs a command in the background, captures the initial output, and returns control to the terminal. + * + * Usage: + * node scripts/ai-helper.js [command] [options] + * + * Commands: + * start-server Start the server in the background + * check-logs Check the most recent logs + * check-cluster Check cluster-related logs + * check-status Check if the server is running + * stop-server Stop the server + * verify-config Verify the current environment configuration + * + * Options: + * --env= Environment (prod, dev, or dev:no-cluster, default: prod) + * --lines= Number of log lines to show (default: 20) + * --help Show this help message + * + * Examples: + * node scripts/ai-helper.js start-server --env=prod + * node scripts/ai-helper.js check-logs --lines=50 + * node scripts/ai-helper.js check-cluster + * node scripts/ai-helper.js verify-config + */ + +const { execSync, spawn } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +// Parse command line arguments +const args = process.argv.slice(2); +const command = args[0]; +const options = { + env: getArgValue(args, '--env') || 'prod', + lines: parseInt(getArgValue(args, '--lines') || '20', 10), + help: args.includes('--help') || args.includes('-h') +}; + +// Show help if requested or no command provided +if (options.help || !command) { + console.log(fs.readFileSync(__filename, 'utf8') + .split('\n') + .filter(line => line.startsWith(' *')) + .map(line => line.substring(3)) + .join('\n')); + process.exit(0); +} + +// Validate environment +if (!['prod', 'prod:no-cluster', 'dev', 'dev:no-cluster'].includes(options.env)) { + console.error(`Error: Unknown environment "${options.env}". Use "prod", "prod:no-cluster", "dev", or "dev:no-cluster".`); + process.exit(1); +} + +// Ensure logs directory exists +if (!fs.existsSync('logs')) { + fs.mkdirSync('logs'); +} + +// Create a unique log file for this run +const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); +const logFile = `logs/ai-helper-${timestamp}.log`; + +// Execute the requested command +switch (command) { + case 'start-server': + startServer(options.env); + break; + case 'check-logs': + checkLogs(options.lines); + break; + case 'check-cluster': + checkClusterLogs(options.lines); + break; + case 'check-status': + checkStatus(); + break; + case 'stop-server': + stopServer(); + break; + case 'verify-config': + verifyConfig(); + break; + default: + console.error(`Unknown command: ${command}`); + console.log('Run with --help to see available commands'); + process.exit(1); +} + +// Helper function to get argument value +function getArgValue(args, name) { + const arg = args.find(a => a.startsWith(`${name}=`)); + return arg ? arg.split('=')[1] : null; +} + +// Start the server in the background +function startServer(env) { + console.log(`Starting server in ${env} mode...`); + + try { + // First, configure the environment + execSync(`node scripts/configure-env.js ${env}`, { stdio: 'inherit' }); + + // Build the application if needed + if (env === 'prod') { + console.log('Building the application...'); + execSync('npm run build', { stdio: 'inherit' }); + } + + // Start the server in the background + const serverProcess = spawn('node', [ + env === 'prod' ? 'dist/server.js' : 'scripts/start.js', + env === 'prod' ? '' : 'dev:mock' + ].filter(Boolean), { + detached: true, + stdio: ['ignore', fs.openSync(logFile, 'w'), fs.openSync(logFile, 'w')] + }); + + // Don't wait for the server to exit + serverProcess.unref(); + + console.log(`Server started with PID ${serverProcess.pid}`); + console.log(`Logs are being written to ${logFile}`); + + // Wait a moment for the server to start + setTimeout(() => { + // Show the initial logs + console.log('\nInitial server logs:'); + try { + const initialLogs = execSync(`tail -n 20 ${logFile}`).toString(); + console.log(initialLogs || 'No logs yet'); + } catch (error) { + console.log('No logs available yet'); + } + + console.log('\nTo check logs later, run:'); + console.log(` node scripts/ai-helper.js check-logs`); + console.log('To stop the server, run:'); + console.log(` node scripts/ai-helper.js stop-server`); + + // For development mode, remind the user to access the application on port 7654 + if (env !== 'prod') { + console.log('\nAccess the application at:'); + console.log(' http://localhost:7654'); + } + }, 2000); + } catch (error) { + console.error('Error starting server:', error.message); + process.exit(1); + } +} + +// Check the most recent logs +function checkLogs(lines) { + console.log(`Checking the most recent logs (${lines} lines)...`); + + try { + // Find the most recent log file + const logDir = path.resolve(process.cwd(), 'logs'); + const logFiles = fs.readdirSync(logDir) + .filter(file => file.endsWith('.log')) + .map(file => ({ + name: file, + time: fs.statSync(path.join(logDir, file)).mtime.getTime() + })) + .sort((a, b) => b.time - a.time); + + if (logFiles.length === 0) { + console.log('No log files found'); + return; + } + + const recentLogFile = path.join(logDir, logFiles[0].name); + console.log(`Most recent log file: ${recentLogFile}`); + + // Show the logs + const logs = execSync(`tail -n ${lines} ${recentLogFile}`).toString(); + console.log('\nRecent logs:'); + console.log(logs || 'No logs found'); + } catch (error) { + console.error('Error checking logs:', error.message); + } +} + +// Check cluster-related logs +function checkClusterLogs(lines) { + console.log('Checking cluster-related logs...'); + + try { + // Find the most recent log file + const logDir = path.resolve(process.cwd(), 'logs'); + const logFiles = fs.readdirSync(logDir) + .filter(file => file.endsWith('.log')) + .map(file => ({ + name: file, + time: fs.statSync(path.join(logDir, file)).mtime.getTime() + })) + .sort((a, b) => b.time - a.time); + + if (logFiles.length === 0) { + console.log('No log files found'); + return; + } + + const recentLogFile = path.join(logDir, logFiles[0].name); + console.log(`Searching in: ${recentLogFile}`); + + // Search for cluster-related logs + const grepCommand = `grep -i "cluster\\|clusterMode\\|autoDetectCluster" ${recentLogFile} | tail -n ${lines}`; + try { + const clusterLogs = execSync(grepCommand).toString(); + console.log('\nCluster-related logs:'); + console.log(clusterLogs || 'No cluster-related logs found'); + } catch (error) { + // grep returns exit code 1 if no matches found + console.log('No cluster-related logs found'); + } + } catch (error) { + console.error('Error checking cluster logs:', error.message); + } +} + +// Check if the server is running +function checkStatus() { + console.log('Checking server status...'); + + try { + const psOutput = execSync('ps aux | grep "[n]ode.*server.js\\|[n]ode.*start.js"').toString(); + console.log('\nRunning server processes:'); + console.log(psOutput || 'No server processes found'); + + // Check if the frontend is responding + try { + console.log('\nChecking frontend on port 7654:'); + // Just check if we get a 200 response, don't try to parse the HTML + const frontendResponse = execSync('curl -s -o /dev/null -w "%{http_code}" http://localhost:7654/').toString(); + if (frontendResponse.trim() === '200') { + console.log('✅ Frontend is responding (HTTP 200)'); + } else { + console.log(`❌ Frontend returned HTTP ${frontendResponse}`); + } + } catch (error) { + console.log('❌ Frontend is not responding'); + } + + // Check if the mock server is responding (in dev mode) + try { + console.log('\nChecking mock server on port 7656:'); + const mockResponse = execSync('curl -s -o /dev/null -w "%{http_code}" http://localhost:7656/').toString(); + if (mockResponse.trim() === '200') { + console.log('✅ Mock server is responding (HTTP 200)'); + } else { + console.log(`❌ Mock server returned HTTP ${mockResponse}`); + } + } catch (error) { + console.log('❌ Mock server is not responding (this is normal in production mode)'); + } + + // Check if the API is responding + try { + console.log('\nChecking API on port 7654 (via frontend proxy):'); + const apiResponse = execSync('curl -s http://localhost:7654/api/nodes | head -n 5').toString(); + if (apiResponse && apiResponse.includes('nodes')) { + console.log('✅ API is responding via frontend proxy'); + console.log('First few lines of API response:'); + console.log(apiResponse); + } else { + console.log('❌ API response via frontend proxy is invalid'); + console.log(apiResponse); + } + } catch (error) { + console.log('❌ API is not responding via frontend proxy'); + + // Try direct API access on port 7656 (for development mode) + try { + console.log('\nTrying direct API access on port 7656:'); + const directApiResponse = execSync('curl -s http://localhost:7656/api/nodes | head -n 5').toString(); + console.log(directApiResponse); + console.log('✅ API is responding directly on port 7656'); + } catch (error) { + console.log('❌ API is not responding on port 7656 either'); + } + } + + console.log('\nServer information:'); + console.log('- The frontend is running on port 7654 (accessible in browser)'); + console.log('- The backend API is running on port 7656 (internal only)'); + console.log('- The mock data server is running on port 7656 (in development mode)'); + + } catch (error) { + console.log('No server processes found'); + } +} + +// Stop the server +function stopServer() { + console.log('Stopping server processes...'); + + try { + // Kill processes by pattern + execSync('pkill -f "node.*server.js\\|node.*start.js\\|vite"'); + console.log('Server processes stopped by pattern'); + } catch (error) { + console.log('No matching processes found to stop by pattern'); + } + + try { + // Kill processes by port + execSync('npx kill-port 7654 7656 3000'); + console.log('Processes on ports 7654, 7656, and 3000 stopped'); + } catch (error) { + console.log('No processes found on ports 7654, 7656, or 3000'); + } + + console.log('All server processes have been stopped'); +} + +// Verify the current environment configuration +function verifyConfig() { + console.log('Verifying environment configuration...'); + + try { + execSync('node scripts/verify-cluster-config.js', { stdio: 'inherit' }); + } catch (error) { + console.error('Error verifying configuration:', error.message); + process.exit(1); + } +} \ No newline at end of file diff --git a/scripts/check-config.js b/scripts/check-config.js new file mode 100755 index 000000000..f988b4ff3 --- /dev/null +++ b/scripts/check-config.js @@ -0,0 +1,111 @@ +#!/usr/bin/env node + +/** + * Check Configuration Script + * + * This script checks if the Proxmox configuration is valid before running in production mode. + * If the configuration is missing or invalid, it provides guidance to the user. + */ + +const fs = require('fs'); +const path = require('path'); +const readline = require('readline'); +const dotenv = require('dotenv'); + +// Load environment variables +dotenv.config(); + +// Path to the .env file +const envFilePath = path.join(process.cwd(), '.env'); + +// Check if .env file exists +if (!fs.existsSync(envFilePath)) { + console.error('\x1b[31mError: .env file not found.\x1b[0m'); + console.log('Please create a .env file with your Proxmox configuration.'); + process.exit(1); +} + +// Check if Proxmox configuration is valid +// We'll check for the default configuration or additional nodes +const defaultVars = [ + 'PROXMOX_HOST', + 'PROXMOX_NODE', + 'PROXMOX_TOKEN_ID', + 'PROXMOX_TOKEN_SECRET' +]; + +// Check if we have the default configuration +const hasDefaultConfig = defaultVars.every(varName => + process.env[varName] && process.env[varName] !== 'your-token-secret-here' && process.env[varName] !== 'your-token-secret' +); + +// Check for additional nodes (with numeric suffix) +let hasAdditionalNodes = false; +for (let i = 2; i <= 10; i++) { + const hostKey = `PROXMOX_HOST_${i}`; + const nodeNameKey = `PROXMOX_NODE_${i}`; + const tokenIdKey = `PROXMOX_TOKEN_ID_${i}`; + const tokenSecretKey = `PROXMOX_TOKEN_SECRET_${i}`; + + const hasNodeConfig = [hostKey, nodeNameKey, tokenIdKey, tokenSecretKey].every(key => + process.env[key] && process.env[key] !== 'your-token-secret-here' && process.env[key] !== 'your-token-secret' + ); + + if (hasNodeConfig) { + hasAdditionalNodes = true; + break; + } +} + +// If mock data is enabled, we don't need Proxmox configuration +const mockDataEnabled = process.env.USE_MOCK_DATA === 'true' || process.env.MOCK_DATA_ENABLED === 'true'; + +if (!hasDefaultConfig && !hasAdditionalNodes && !mockDataEnabled) { + console.error('\x1b[31mError: Missing or invalid Proxmox configuration.\x1b[0m'); + console.log('You need to configure at least one Proxmox node:'); + console.log('\nDefault node configuration:'); + defaultVars.forEach(varName => { + console.log(` - ${varName}`); + }); + + console.log('\nYou have two options:'); + console.log('1. Update your .env file with valid Proxmox details'); + console.log('2. Use development mode with mock data instead'); + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + + rl.question('\nWould you like to continue with mock data instead? (y/n): ', (answer) => { + rl.close(); + + if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') { + console.log('\n\x1b[33mSwitching to development mode with mock data...\x1b[0m'); + + // Update the current .env file with development settings + let envContent = fs.readFileSync(envFilePath, 'utf8'); + envContent = envContent.replace(/NODE_ENV=.*/g, 'NODE_ENV=development'); + envContent = envContent.replace(/USE_MOCK_DATA=.*/g, 'USE_MOCK_DATA=true'); + envContent = envContent.replace(/MOCK_DATA_ENABLED=.*/g, 'MOCK_DATA_ENABLED=true'); + fs.writeFileSync(envFilePath, envContent); + console.log('\x1b[32mUpdated .env file with development settings and mock data enabled.\x1b[0m'); + + console.log('\x1b[33mPlease run "npm run dev" to start in development mode.\x1b[0m'); + process.exit(0); + } else { + console.log('\nPlease update your .env file with valid Proxmox configuration and try again.'); + process.exit(1); + } + }); +} else { + // Configuration is valid + if (hasDefaultConfig) { + console.log('\x1b[32mDefault Proxmox configuration is valid. Continuing with production mode.\x1b[0m'); + } else if (hasAdditionalNodes) { + console.log('\x1b[32mAdditional Proxmox node configuration is valid. Continuing with production mode.\x1b[0m'); + } else if (mockDataEnabled) { + console.log('\x1b[32mMock data is enabled. No Proxmox configuration needed. Continuing with production mode.\x1b[0m'); + } + process.exit(0); +} \ No newline at end of file diff --git a/scripts/check-connections.js b/scripts/check-connections.js new file mode 100644 index 000000000..a53dfeaf6 --- /dev/null +++ b/scripts/check-connections.js @@ -0,0 +1,236 @@ +#!/usr/bin/env node + +/** + * Script to check connections between frontend, backend, and mock server + * Usage: node scripts/check-connections.js + */ + +const http = require('http'); +const net = require('net'); +const fs = require('fs'); +const path = require('path'); +const dotenv = require('dotenv'); + +// Load environment variables +dotenv.config(); + +console.log('Pulse Connection Checker'); +console.log('======================='); +console.log(`Environment: ${process.env.NODE_ENV || 'Not set'}`); +console.log(`USE_MOCK_DATA: ${process.env.USE_MOCK_DATA || 'Not set'}`); +console.log(`MOCK_DATA_ENABLED: ${process.env.MOCK_DATA_ENABLED || 'Not set'}`); + +// Check if a port is in use +function checkPort(port) { + return new Promise((resolve) => { + const server = net.createServer(); + + server.once('error', (err) => { + if (err.code === 'EADDRINUSE') { + resolve(true); // Port is in use + } else { + resolve(false); + } + }); + + server.once('listening', () => { + server.close(); + resolve(false); // Port is not in use + }); + + server.listen(port); + }); +} + +// Check if a server is responding on a given port +function checkServer(host, port, path = '/') { + return new Promise((resolve) => { + const req = http.request({ + host, + port, + path, + method: 'GET', + timeout: 3000 + }, (res) => { + let data = ''; + res.on('data', (chunk) => { + data += chunk; + }); + res.on('end', () => { + resolve({ + status: res.statusCode, + data: data.substring(0, 100) // Just get the first 100 chars + }); + }); + }); + + req.on('error', (err) => { + resolve({ + status: 'error', + error: err.message + }); + }); + + req.on('timeout', () => { + req.destroy(); + resolve({ + status: 'timeout', + error: 'Request timed out' + }); + }); + + req.end(); + }); +} + +// Check frontend socket.js file +function checkFrontendSocketConfig() { + const socketJsPath = path.join(process.cwd(), 'frontend', 'src', 'hooks', 'useSocket.js'); + + if (!fs.existsSync(socketJsPath)) { + return { + exists: false, + error: 'useSocket.js file not found' + }; + } + + const content = fs.readFileSync(socketJsPath, 'utf8'); + + // Check if we're using the dynamic port selection + if (content.includes('useMockData ?')) { + // We're using dynamic port selection + const isDevelopment = process.env.NODE_ENV === 'development'; + const useMockData = process.env.USE_MOCK_DATA === 'true' || process.env.MOCK_DATA_ENABLED === 'true'; + + if (isDevelopment) { + const port = useMockData ? '7656' : '7654'; + return { + exists: true, + port, + isDynamic: true, + useMockData + }; + } else { + // In production, we use window.location.origin + return { + exists: true, + port: '7654', // Production always uses 7654 + isDynamic: true, + useMockData: false + }; + } + } else { + // Extract the port from the socketUrl (old method) + const portMatch = content.match(/socketUrl\s*=\s*`http:\/\/\${currentHost}:(\d+)`/); + const port = portMatch ? portMatch[1] : null; + + return { + exists: true, + port, + isDynamic: false + }; + } +} + +async function main() { + // Check if backend server is running (port 7654) + const backendRunning = await checkPort(7654); + console.log(`\nBackend server (port 7654): ${backendRunning ? 'RUNNING' : 'NOT RUNNING'}`); + + if (backendRunning) { + const backendResponse = await checkServer('localhost', 7654); + console.log(` Response: ${typeof backendResponse.status === 'number' ? backendResponse.status : backendResponse.status}`); + if (backendResponse.error) { + console.log(` Error: ${backendResponse.error}`); + } + } + + // Check if mock server is running (port 7656) + const mockRunning = await checkPort(7656); + console.log(`\nMock server (port 7656): ${mockRunning ? 'RUNNING' : 'NOT RUNNING'}`); + + if (mockRunning) { + const mockResponse = await checkServer('localhost', 7656); + console.log(` Response: ${typeof mockResponse.status === 'number' ? mockResponse.status : mockResponse.status}`); + if (mockResponse.error) { + console.log(` Error: ${mockResponse.error}`); + } + } + + // Check frontend socket configuration + const socketConfig = checkFrontendSocketConfig(); + console.log('\nFrontend Socket Configuration:'); + if (socketConfig.exists) { + if (socketConfig.isDynamic) { + console.log(' Dynamic port selection: ENABLED'); + console.log(` Current environment: ${process.env.NODE_ENV || 'Not set'}`); + console.log(` Mock data enabled: ${socketConfig.useMockData ? 'Yes' : 'No'}`); + console.log(` Will connect to port: ${socketConfig.port}`); + + if (socketConfig.port === '7654') { + console.log(' Frontend is configured to connect to the BACKEND server'); + } else if (socketConfig.port === '7656') { + console.log(' Frontend is configured to connect to the MOCK server'); + } + } else { + console.log(` Socket port: ${socketConfig.port || 'Not found'}`); + if (socketConfig.port === '7654') { + console.log(' Frontend is configured to connect to the BACKEND server'); + } else if (socketConfig.port === '7656') { + console.log(' Frontend is configured to connect to the MOCK server'); + } else { + console.log(` Frontend is configured to connect to an UNKNOWN port: ${socketConfig.port}`); + } + } + } else { + console.log(` Error: ${socketConfig.error}`); + } + + // Check if frontend dev server is running + const frontendRunning = await checkPort(5173); + console.log(`\nFrontend dev server (port 5173): ${!frontendRunning ? 'RUNNING' : 'NOT RUNNING'}`); + + // Summary and recommendations + console.log('\nSummary:'); + + if (process.env.NODE_ENV === 'production') { + console.log('- Running in PRODUCTION mode'); + + if (process.env.USE_MOCK_DATA === 'true' || process.env.MOCK_DATA_ENABLED === 'true') { + console.log(' WARNING: Mock data is enabled in production mode!'); + } + + if (socketConfig.port === '7656') { + console.log(' WARNING: Frontend is configured to connect to the mock server (port 7656) but you are in production mode!'); + console.log(' SOLUTION: Edit frontend/src/hooks/useSocket.js to use port 7654 instead of 7656'); + } + } else { + console.log('- Running in DEVELOPMENT mode'); + + if (process.env.USE_MOCK_DATA !== 'true' && process.env.MOCK_DATA_ENABLED !== 'true') { + console.log(' NOTE: Mock data is disabled in development mode'); + } + + if (socketConfig.port === '7654' && (process.env.USE_MOCK_DATA === 'true' || process.env.MOCK_DATA_ENABLED === 'true')) { + console.log(' WARNING: Frontend is configured to connect to the backend server (port 7654) but mock data is enabled!'); + console.log(' SOLUTION: Edit frontend/src/hooks/useSocket.js to use port 7656 instead of 7654'); + } + } + + if (backendRunning && mockRunning && socketConfig.port === '7656' && process.env.NODE_ENV === 'production') { + console.log('\nPROBLEM DETECTED: You are running in production mode but the frontend is connecting to the mock server!'); + console.log('SOLUTION: Kill all servers, edit frontend/src/hooks/useSocket.js to use port 7654, and restart with npm run prod'); + } + + if (!backendRunning && process.env.NODE_ENV === 'production') { + console.log('\nPROBLEM DETECTED: Production backend server is not running!'); + console.log('SOLUTION: Start the production server with npm run prod'); + } + + if (!mockRunning && (process.env.USE_MOCK_DATA === 'true' || process.env.MOCK_DATA_ENABLED === 'true')) { + console.log('\nPROBLEM DETECTED: Mock data is enabled but the mock server is not running!'); + console.log('SOLUTION: Start the mock server with npm run mock'); + } +} + +main().catch(console.error); \ No newline at end of file diff --git a/scripts/clear-data.js b/scripts/clear-data.js new file mode 100644 index 000000000..8a9f8548e --- /dev/null +++ b/scripts/clear-data.js @@ -0,0 +1,71 @@ +/** + * Script to clear persisted data files when switching between environments + * This script is called by the start-dev.sh and start-prod.sh scripts + */ + +const fs = require('fs'); +const path = require('path'); + +// Directories to clear +const dataDirs = [ + 'data', + 'logs', + 'tmp' +]; + +// Function to clear a directory +function clearDirectory(dirPath) { + if (!fs.existsSync(dirPath)) { + console.log(`Directory ${dirPath} does not exist, creating it...`); + fs.mkdirSync(dirPath, { recursive: true }); + return; + } + + try { + const files = fs.readdirSync(dirPath); + + for (const file of files) { + const filePath = path.join(dirPath, file); + const stat = fs.statSync(filePath); + + if (stat.isDirectory()) { + // Skip .git and node_modules directories + if (file !== '.git' && file !== 'node_modules') { + clearDirectory(filePath); + } + } else { + // Delete the file + fs.unlinkSync(filePath); + console.log(`Deleted file: ${filePath}`); + } + } + + console.log(`Cleared directory: ${dirPath}`); + } catch (error) { + console.error(`Error clearing directory ${dirPath}:`, error.message); + } +} + +// Main function +function clearData() { + console.log('Clearing persisted data files...'); + + // Get the project root directory + const rootDir = path.resolve(__dirname, '..'); + + // Clear each directory + for (const dir of dataDirs) { + const dirPath = path.join(rootDir, dir); + clearDirectory(dirPath); + } + + console.log('Data clearing complete.'); +} + +// Run the script if called directly +if (require.main === module) { + clearData(); +} + +// Export for use in other scripts +module.exports = clearData; \ No newline at end of file diff --git a/scripts/configure-env.js b/scripts/configure-env.js new file mode 100755 index 000000000..534ba0e98 --- /dev/null +++ b/scripts/configure-env.js @@ -0,0 +1,100 @@ +#!/usr/bin/env node + +/** + * Script to configure environment variables in .env file + * Usage: node scripts/configure-env.js [prod|dev] + */ + +const fs = require('fs'); +const path = require('path'); +const dotenv = require('dotenv'); + +// Get the environment from command line arguments +const args = process.argv.slice(2); +const env = args[0] || 'dev'; // Default to dev if no argument provided + +// Path to the .env file +const envFilePath = path.resolve(process.cwd(), '.env'); + +// Check if .env file exists +if (!fs.existsSync(envFilePath)) { + console.error('Error: .env file not found. Please create one by copying .env.example'); + process.exit(1); +} + +// Load current .env file +const currentEnv = dotenv.parse(fs.readFileSync(envFilePath)); + +// Define environment-specific values +const envConfigs = { + prod: { + NODE_ENV: 'production', + LOG_LEVEL: 'info', + USE_MOCK_DATA: 'false', + MOCK_DATA_ENABLED: 'false', + PROXMOX_AUTO_DETECT_CLUSTER: 'true', + PROXMOX_CLUSTER_MODE: 'true', + MOCK_CLUSTER_ENABLED: 'false', + DOCKERFILE: 'docker/Dockerfile' + }, + 'prod:no-cluster': { + NODE_ENV: 'production', + LOG_LEVEL: 'info', + USE_MOCK_DATA: 'false', + MOCK_DATA_ENABLED: 'false', + PROXMOX_AUTO_DETECT_CLUSTER: 'false', + PROXMOX_CLUSTER_MODE: 'false', + MOCK_CLUSTER_ENABLED: 'false', + DOCKERFILE: 'docker/Dockerfile' + }, + dev: { + NODE_ENV: 'development', + LOG_LEVEL: 'info', + USE_MOCK_DATA: 'true', + MOCK_DATA_ENABLED: 'true', + PROXMOX_AUTO_DETECT_CLUSTER: 'true', + PROXMOX_CLUSTER_MODE: 'true', + MOCK_CLUSTER_ENABLED: 'true', + DOCKERFILE: 'docker/Dockerfile.dev' + }, + 'dev:no-cluster': { + NODE_ENV: 'development', + LOG_LEVEL: 'info', + USE_MOCK_DATA: 'true', + MOCK_DATA_ENABLED: 'true', + PROXMOX_AUTO_DETECT_CLUSTER: 'false', + PROXMOX_CLUSTER_MODE: 'false', + MOCK_CLUSTER_ENABLED: 'false', + DOCKERFILE: 'docker/Dockerfile.dev' + } +}; + +// Get the config for the specified environment +const config = envConfigs[env]; +if (!config) { + console.error(`Error: Unknown environment "${env}". Use "prod", "prod:no-cluster", "dev", or "dev:no-cluster".`); + process.exit(1); +} + +// Update the .env file +let envContent = fs.readFileSync(envFilePath, 'utf8'); + +// Update each environment variable +Object.entries(config).forEach(([key, value]) => { + // Check if the key exists in the .env file + const regex = new RegExp(`^${key}=.*$`, 'm'); + if (regex.test(envContent)) { + // Replace the existing value + envContent = envContent.replace(regex, `${key}=${value}`); + console.log(`Updated ${key}=${value}`); + } else { + // Add the key if it doesn't exist + envContent += `\n${key}=${value}`; + console.log(`Added ${key}=${value}`); + } +}); + +// Write the updated content back to the .env file +fs.writeFileSync(envFilePath, envContent); + +console.log(`\nEnvironment configured for ${env === 'prod' ? 'production' : env === 'prod:no-cluster' ? 'production (no cluster)' : env === 'dev' ? 'development' : 'development (no cluster)'}`); \ No newline at end of file diff --git a/scripts/debug-proxy.sh b/scripts/debug-proxy.sh index ebfaeb155..8fff1d514 100755 --- a/scripts/debug-proxy.sh +++ b/scripts/debug-proxy.sh @@ -12,9 +12,9 @@ RED='\033[0;31m' NC='\033[0m' # No Color # Port configuration -MOCK_PORT=7655 -PROXY_PORT=7656 -FRONTEND_PORT=3000 +MOCK_PORT=7656 +PROXY_PORT=7657 +FRONTEND_PORT=7654 echo -e "${BLUE}" echo "╔════════════════════════════════════════════════════════════╗" diff --git a/scripts/debug-socket.js b/scripts/debug-socket.js index fb49496a6..9c196a6f0 100644 --- a/scripts/debug-socket.js +++ b/scripts/debug-socket.js @@ -22,8 +22,8 @@ const io = new SocketServer(server, { }); // Configuration -const PROXY_PORT = 7656; -const BACKEND_PORT = 7655; +const PROXY_PORT = 7657; +const BACKEND_PORT = 7656; const BACKEND_URL = `http://localhost:${BACKEND_PORT}`; const LOG_FILE = './socket-debug.log'; diff --git a/scripts/dev-cleanup.js b/scripts/dev-cleanup.js index b82748b95..171308bd3 100644 --- a/scripts/dev-cleanup.js +++ b/scripts/dev-cleanup.js @@ -29,14 +29,14 @@ try { // Backend ports try { - execSync('npx kill-port 7654 7655', { stdio: 'inherit' }); + execSync('npx kill-port 7654 7656', { stdio: 'inherit' }); } catch (error) { console.log('No processes using backend ports.'); } // Frontend ports try { - execSync('npx kill-port 3000 9513', { stdio: 'inherit' }); + execSync('npx kill-port 7654 9513', { stdio: 'inherit' }); } catch (error) { console.log('No processes using frontend ports.'); } diff --git a/scripts/docker-prod.sh b/scripts/docker-prod.sh new file mode 100755 index 000000000..750ba2f47 --- /dev/null +++ b/scripts/docker-prod.sh @@ -0,0 +1,107 @@ +#!/bin/bash + +# Make script executable if it isn't already +chmod +x "$0" + +# Default values +BUILD=false +RUN=false +DETACHED=false +CLEANUP=false +HELP=false + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case "$1" in + --build) + BUILD=true + shift + ;; + --run) + RUN=true + shift + ;; + --detached) + DETACHED=true + shift + ;; + --cleanup) + CLEANUP=true + shift + ;; + --help) + HELP=true + shift + ;; + *) + echo "Unknown option: $1" + HELP=true + shift + ;; + esac +done + +# Display help +if [ "$HELP" = true ]; then + echo "Usage: $0 [options]" + echo "Options:" + echo " --build Build the Docker image" + echo " --run Run the Docker container" + echo " --detached Run in detached mode (background)" + echo " --cleanup Remove existing containers and images" + echo " --help Display this help message" + exit 0 +fi + +# Ensure we're in the project root directory +if [ ! -f "docker-compose.yml" ]; then + echo "Error: docker-compose.yml not found. Please run this script from the project root directory." + exit 1 +fi + +# Ensure .env file exists +if [ ! -f ".env" ]; then + if [ -f ".env.example" ]; then + echo "Creating .env file from .env.example..." + cp .env.example .env + else + echo "Error: .env file not found and .env.example is missing." + exit 1 + fi +fi + +# Cleanup if requested +if [ "$CLEANUP" = true ]; then + echo "Cleaning up Docker resources..." + docker compose down --rmi all --volumes --remove-orphans + exit 0 +fi + +# Build if requested +if [ "$BUILD" = true ]; then + echo "Building Docker image..." + docker compose build +fi + +# Run if requested +if [ "$RUN" = true ]; then + # Stop any existing containers + echo "Stopping any existing containers..." + docker compose down + + # Run the container + if [ "$DETACHED" = true ]; then + echo "Starting container in detached mode..." + docker compose up -d + else + echo "Starting container..." + docker compose up + fi +fi + +# If no action specified, show help +if [ "$BUILD" = false ] && [ "$RUN" = false ] && [ "$CLEANUP" = false ]; then + echo "No action specified. Use --build, --run, or --cleanup." + echo "Run with --help for more information." + exit 1 +fi \ No newline at end of file diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 000000000..daf7b5035 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,177 @@ +#!/bin/bash + +# Colors for output +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Function to check if a command exists +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +# Function to print a section header +print_header() { + echo -e "\n${BLUE}==== $1 ====${NC}\n" +} + +# Function to print a success message +print_success() { + echo -e "${GREEN}✓ $1${NC}" +} + +# Function to print a warning message +print_warning() { + echo -e "${YELLOW}⚠ $1${NC}" +} + +# Function to print an error message +print_error() { + echo -e "${RED}✗ $1${NC}" +} + +# Function to print a step +print_step() { + echo -e "${BLUE}$1${NC}" +} + +# Welcome message +clear +echo -e "${BLUE}" +echo " _____ _ _____ _ _ _ " +echo " | __ \ | | |_ _| | | | | | " +echo " | |__) | | |___ ___ | | _ __ ___| |_ __ _| | | ___ _ __ " +echo " | ___/ | | / __|/ _ \ | | | '_ \/ __| __/ _\` | | |/ _ \ '__|" +echo " | | | |_| \__ \ __/ _| |_| | | \__ \ || (_| | | | __/ | " +echo " |_| \__,_|___/\___| |_____|_| |_|___/\__\__,_|_|_|\___|_| " +echo -e "${NC}" +echo -e "Welcome to the Pulse installation script!\n" + +# Ask for installation type +print_header "Installation Type" +echo "Please select the type of installation:" +echo "1) Production - connect to a real Proxmox server" +echo "2) Development - use with mock data (no Proxmox server needed)" +read -p "Enter your choice (1/2) [1]: " install_type +install_type=${install_type:-1} + +# Set environment variables based on installation type +if [ "$install_type" = "1" ]; then + # Production with real Proxmox + NODE_ENV="production" + USE_MOCK_DATA="false" + MOCK_DATA_ENABLED="false" + echo "Set up for production with real Proxmox server" +elif [ "$install_type" = "2" ]; then + # Development with mock data + NODE_ENV="development" + USE_MOCK_DATA="true" + MOCK_DATA_ENABLED="true" + echo "Set up for development with mock data" +else + print_error "Invalid choice. Exiting." + exit 1 +fi + +# Check for Docker +print_header "Checking System Requirements" +if command_exists docker; then + print_success "Docker is installed ($(docker --version))" +else + print_error "Docker is not installed. Please install Docker before continuing." + echo "Visit https://docs.docker.com/get-docker/ for installation instructions." + exit 1 +fi + +# Check for Docker Compose +if command_exists docker-compose; then + print_success "Docker Compose is installed ($(docker-compose --version))" +elif docker compose version >/dev/null 2>&1; then + print_success "Docker Compose plugin is installed ($(docker compose version))" +else + print_warning "Docker Compose is not installed. It's recommended for easier management." + echo "Visit https://docs.docker.com/compose/install/ for installation instructions." +fi + +# Create .env file if it doesn't exist +print_header "Environment Configuration" +if [ -f ".env" ]; then + print_warning "An existing .env file was found." + read -p "Do you want to create a new one? This will overwrite the existing file. (y/n) [n]: " create_new_env + create_new_env=${create_new_env:-n} +else + create_new_env="y" +fi + +if [ "$create_new_env" = "y" ] || [ "$create_new_env" = "Y" ]; then + echo "Creating new .env file..." + + # Copy the example file + cp .env.example .env + + # Update the .env file with the installation type + sed -i.bak "s/NODE_ENV=.*/NODE_ENV=$NODE_ENV/" .env + sed -i.bak "s/USE_MOCK_DATA=.*/USE_MOCK_DATA=$USE_MOCK_DATA/" .env + sed -i.bak "s/MOCK_DATA_ENABLED=.*/MOCK_DATA_ENABLED=$MOCK_DATA_ENABLED/" .env + rm -f .env.bak + + print_success "Created new .env file with $NODE_ENV configuration" + + # If using real Proxmox, ask for server details + if [ "$install_type" = "1" ]; then + print_step "Please enter your Proxmox server details:" + + read -p "Proxmox Host URL (e.g., https://proxmox.local:8006): " proxmox_host + read -p "Proxmox Node Name (e.g., pve): " proxmox_node + read -p "Proxmox API Token ID (e.g., root@pam!pulse): " proxmox_token_id + read -p "Proxmox API Token Secret: " proxmox_token_secret + + # Update the .env file with Proxmox details if provided + if [ -n "$proxmox_host" ]; then + sed -i.bak "s|PROXMOX_HOST=.*|PROXMOX_HOST=$proxmox_host|" .env + fi + + if [ -n "$proxmox_node" ]; then + sed -i.bak "s/PROXMOX_NODE=.*/PROXMOX_NODE=$proxmox_node/" .env + fi + + if [ -n "$proxmox_token_id" ]; then + sed -i.bak "s/PROXMOX_TOKEN_ID=.*/PROXMOX_TOKEN_ID=$proxmox_token_id/" .env + fi + + if [ -n "$proxmox_token_secret" ]; then + sed -i.bak "s/PROXMOX_TOKEN_SECRET=.*/PROXMOX_TOKEN_SECRET=$proxmox_token_secret/" .env + fi + + rm -f .env.bak + print_success "Updated .env file with Proxmox configuration" + fi +else + print_success "Using existing .env file" +fi + +# Ask if the user wants to run the setup script +print_header "Setup" +read -p "Do you want to run the application now? (y/n) [y]: " run_setup +run_setup=${run_setup:-y} + +if [ "$run_setup" = "y" ] || [ "$run_setup" = "Y" ]; then + if [ "$install_type" = "1" ]; then + echo "Starting Pulse with Proxmox connection..." + npm run prod:docker + else + echo "Starting Pulse with mock data..." + npm run dev:docker + fi +else + echo -e "\nTo start Pulse later, run one of the following commands:" + echo -e " - For production: ${GREEN}npm run prod:docker${NC}" + echo -e " - For development with mock data: ${GREEN}npm run dev:docker${NC}" +fi + +print_header "Installation Complete" +echo "Thank you for installing Pulse!" +echo "Access the dashboard at http://localhost:7654" +echo -e "For more information, see the ${BLUE}README.md${NC} file or visit ${BLUE}https://github.com/rcourtman/pulse${NC}" \ No newline at end of file diff --git a/scripts/monitor-logs.js b/scripts/monitor-logs.js new file mode 100755 index 000000000..4860fe70c --- /dev/null +++ b/scripts/monitor-logs.js @@ -0,0 +1,238 @@ +#!/usr/bin/env node + +/** + * Log Monitor Script + * + * This script provides real-time monitoring of application logs with filtering capabilities. + * It makes troubleshooting easier by allowing you to focus on specific log types or components. + * + * Usage: + * node scripts/monitor-logs.js [options] + * + * Options: + * --level= Filter by log level (error, warn, info, debug) + * --component= Filter by component name (e.g., ProxmoxClient, NodeManager) + * --search= Search for specific text in logs + * --follow Follow log file in real-time (default: true) + * --lines= Number of lines to show initially (default: 50) + * --file= Log file to monitor (default: latest combined log) + * --color Use colored output (default: true) + * --help Show this help message + * + * Examples: + * node scripts/monitor-logs.js --level=error + * node scripts/monitor-logs.js --component=ProxmoxClient + * node scripts/monitor-logs.js --search=cluster + * node scripts/monitor-logs.js --level=info --component=NodeManager + */ + +const fs = require('fs'); +const path = require('path'); +const { spawn } = require('child_process'); +const readline = require('readline'); + +// Parse command line arguments +const args = process.argv.slice(2); +const options = { + level: getArgValue(args, '--level'), + component: getArgValue(args, '--component'), + search: getArgValue(args, '--search'), + follow: getArgValue(args, '--follow') !== 'false', + lines: parseInt(getArgValue(args, '--lines') || '50', 10), + file: getArgValue(args, '--file'), + color: getArgValue(args, '--color') !== 'false', + help: args.includes('--help') || args.includes('-h') +}; + +// Show help if requested +if (options.help) { + console.log(fs.readFileSync(__filename, 'utf8') + .split('\n') + .filter(line => line.startsWith(' *')) + .map(line => line.substring(3)) + .join('\n')); + process.exit(0); +} + +// Find the log directory +const logDir = path.resolve(process.cwd(), 'logs'); + +// Find the most recent log file if not specified +if (!options.file) { + try { + const logFiles = fs.readdirSync(logDir) + .filter(file => file.startsWith('combined') && file.endsWith('.log')) + .map(file => ({ + name: file, + time: fs.statSync(path.join(logDir, file)).mtime.getTime() + })) + .sort((a, b) => b.time - a.time); + + if (logFiles.length > 0) { + options.file = logFiles[0].name; + } else { + options.file = 'combined.log'; + } + } catch (error) { + console.error('Error finding log files:', error.message); + options.file = 'combined.log'; + } +} + +const logFile = path.join(logDir, options.file); + +// ANSI color codes +const colors = { + reset: '\x1b[0m', + bright: '\x1b[1m', + dim: '\x1b[2m', + red: '\x1b[31m', + green: '\x1b[32m', + yellow: '\x1b[33m', + blue: '\x1b[34m', + magenta: '\x1b[35m', + cyan: '\x1b[36m', + white: '\x1b[37m', + bgRed: '\x1b[41m', + bgGreen: '\x1b[42m', + bgYellow: '\x1b[43m', + bgBlue: '\x1b[44m' +}; + +// Color mapping for log levels +const levelColors = { + error: colors.red, + warn: colors.yellow, + info: colors.green, + debug: colors.blue, + silly: colors.magenta +}; + +// Check if the log file exists +if (!fs.existsSync(logFile)) { + console.error(`Log file not found: ${logFile}`); + process.exit(1); +} + +console.log(`Monitoring log file: ${logFile}`); +console.log(`Filters: ${Object.entries(options) + .filter(([key, value]) => ['level', 'component', 'search'].includes(key) && value) + .map(([key, value]) => `${key}=${value}`) + .join(', ') || 'none'}`); +console.log('Press Ctrl+C to exit\n'); + +// Build the tail command +const tailArgs = ['-n', options.lines.toString()]; +if (options.follow) { + tailArgs.push('-f'); +} +tailArgs.push(logFile); + +// Start the tail process +const tail = spawn('tail', tailArgs); + +// Process each line of output +tail.stdout.setEncoding('utf8'); +const rl = readline.createInterface({ + input: tail.stdout, + terminal: false +}); + +rl.on('line', (line) => { + try { + // Try to parse the line as JSON + const logEntry = JSON.parse(line); + + // Apply filters + if (options.level && logEntry.level !== options.level) { + return; + } + + if (options.component && + (!logEntry.component || !logEntry.component.includes(options.component))) { + return; + } + + if (options.search && + !line.toLowerCase().includes(options.search.toLowerCase())) { + return; + } + + // Format the output + let formattedLine = ''; + + if (options.color) { + const levelColor = levelColors[logEntry.level] || colors.white; + + // Timestamp + formattedLine += `${colors.dim}${new Date(logEntry.timestamp).toISOString()}${colors.reset} `; + + // Level + formattedLine += `${levelColor}${logEntry.level.toUpperCase().padEnd(5)}${colors.reset} `; + + // Component + if (logEntry.component) { + formattedLine += `${colors.cyan}[${logEntry.component}]${colors.reset} `; + } + + // Message + formattedLine += logEntry.message; + + // Highlight search term if specified + if (options.search) { + const regex = new RegExp(options.search, 'gi'); + formattedLine = formattedLine.replace(regex, match => + `${colors.bgYellow}${colors.bright}${match}${colors.reset}`); + } + + // Add metadata if present + if (logEntry.meta && Object.keys(logEntry.meta).length > 0) { + formattedLine += `\n${colors.dim}${JSON.stringify(logEntry.meta, null, 2)}${colors.reset}`; + } + } else { + // Simple formatting without colors + formattedLine = `${new Date(logEntry.timestamp).toISOString()} ${logEntry.level.toUpperCase().padEnd(5)} `; + + if (logEntry.component) { + formattedLine += `[${logEntry.component}] `; + } + + formattedLine += logEntry.message; + + // Add metadata if present + if (logEntry.meta && Object.keys(logEntry.meta).length > 0) { + formattedLine += `\n${JSON.stringify(logEntry.meta, null, 2)}`; + } + } + + console.log(formattedLine); + } catch (error) { + // If not JSON, just print the line as is + if (options.search && !line.toLowerCase().includes(options.search.toLowerCase())) { + return; + } + + if (options.color && options.search) { + const regex = new RegExp(options.search, 'gi'); + console.log(line.replace(regex, match => + `${colors.bgYellow}${colors.bright}${match}${colors.reset}`)); + } else { + console.log(line); + } + } +}); + +// Handle errors +tail.stderr.on('data', (data) => { + console.error(`Error: ${data}`); +}); + +tail.on('close', (code) => { + console.log(`Monitoring ended with code ${code}`); +}); + +// Helper function to get argument value +function getArgValue(args, name) { + const arg = args.find(a => a.startsWith(`${name}=`)); + return arg ? arg.split('=')[1] : null; +} \ No newline at end of file diff --git a/scripts/run-with-logs.js b/scripts/run-with-logs.js new file mode 100755 index 000000000..1f4838352 --- /dev/null +++ b/scripts/run-with-logs.js @@ -0,0 +1,98 @@ +#!/usr/bin/env node + +/** + * Run With Logs Script + * + * This script runs the application and monitors logs in a split terminal. + * It makes it easier to see what's happening in the application in real-time. + * + * Usage: + * node scripts/run-with-logs.js [dev|prod] [options] + * + * Options: + * --search= Search for specific text in logs + * --level= Filter by log level (error, warn, info, debug) + * --component= Filter by component name + * --no-cluster Disable cluster mode + * --help Show this help message + * + * Examples: + * node scripts/run-with-logs.js dev + * node scripts/run-with-logs.js prod --search=cluster + * node scripts/run-with-logs.js dev --no-cluster + */ + +const { spawn } = require('child_process'); +const fs = require('fs'); + +// Parse command line arguments +const args = process.argv.slice(2); +const env = args[0] === 'prod' ? 'prod' : 'dev'; +const noCluster = args.includes('--no-cluster'); +const help = args.includes('--help') || args.includes('-h'); + +// Show help if requested +if (help) { + console.log(fs.readFileSync(__filename, 'utf8') + .split('\n') + .filter(line => line.startsWith(' *')) + .map(line => line.substring(3)) + .join('\n')); + process.exit(0); +} + +// Extract log options +const logOptions = args + .filter(arg => arg.startsWith('--') && !arg.startsWith('--no-')) + .join(' '); + +// Determine which npm script to run +const npmScript = noCluster ? `${env}:no-cluster` : env; + +console.log(`Starting application in ${env} mode${noCluster ? ' with cluster mode disabled' : ''}...`); + +// Create a unique log file for this run +const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); +const logFile = `logs/app-${env}-${timestamp}.log`; + +// Ensure logs directory exists +if (!fs.existsSync('logs')) { + fs.mkdirSync('logs'); +} + +// Start the application and redirect output to the log file +const app = spawn('npm', ['run', npmScript], { + stdio: ['ignore', fs.openSync(logFile, 'w'), fs.openSync(logFile, 'w')], + detached: true +}); + +// Don't wait for the app to exit +app.unref(); + +console.log(`Application started with PID ${app.pid}`); +console.log(`Logs are being written to ${logFile}`); +console.log('Starting log monitor...\n'); + +// Wait a moment for the app to start writing logs +setTimeout(() => { + // Start the log monitor + const logMonitor = spawn('node', ['scripts/monitor-logs.js', `--file=${logFile.split('/')[1]}`, '--follow=true', ...logOptions.split(' ').filter(Boolean)], { + stdio: 'inherit' + }); + + // Handle log monitor exit + logMonitor.on('exit', (code) => { + console.log(`\nLog monitor exited with code ${code}`); + console.log(`The application is still running with PID ${app.pid}`); + console.log('To stop the application, run:'); + console.log(` kill ${app.pid}`); + process.exit(0); + }); + + // Handle SIGINT (Ctrl+C) + process.on('SIGINT', () => { + console.log('\nStopping application and log monitor...'); + process.kill(-app.pid, 'SIGINT'); + process.exit(0); + }); +}, 1000); \ No newline at end of file diff --git a/scripts/start-dev.bat b/scripts/start-dev.bat new file mode 100644 index 000000000..170675379 --- /dev/null +++ b/scripts/start-dev.bat @@ -0,0 +1,98 @@ +@echo off +setlocal + +REM Stop any running Pulse Docker containers first +echo Stopping any running Pulse Docker containers... +where docker >nul 2>&1 +if %ERRORLEVEL% EQU 0 ( + for /f "tokens=*" %%i in ('docker ps -q --filter "name=pulse"') do ( + docker stop %%i + ) +) else ( + echo Docker not found, skipping container cleanup... +) + +REM Kill any existing servers +echo Killing any existing servers... +taskkill /f /im "node.exe" /fi "WINDOWTITLE eq node dist/server.js" 2>nul +call npx kill-port 7654 7656 3000 + +REM Clear any existing data files that might persist between sessions +echo Clearing any persisted data from previous sessions... +node scripts/clear-data.js + +REM Set environment to development +set NODE_ENV=development + +REM Load environment variables from .env if it exists +if exist .env ( + echo Loading environment from .env + for /f "tokens=*" %%a in (.env) do ( + set "%%a" + ) +) + +REM Override with development settings +set USE_MOCK_DATA=true +set MOCK_DATA_ENABLED=true +set MOCK_SERVER_PORT=7656 + +REM Start the mock data server on port 7656 +echo Starting mock data server on port 7656... +start /b cmd /c "npx ts-node src/mock/run-server.ts" + +REM Wait a moment for the mock server to start +timeout /t 3 /nobreak > nul + +REM Verify mock server is running +call :check_server_running 7656 "Mock server" +if %ERRORLEVEL% NEQ 0 ( + echo ERROR: Mock server failed to start on port 7656 + exit /b 1 +) + +REM Start the backend server on port 7654 +echo Starting backend server on port 7654... +start /b cmd /c "set PORT=7654 && npm run dev:server" + +REM Wait a moment for the server to start +timeout /t 3 /nobreak > nul + +REM Verify backend server is running +call :check_server_running 7654 "Backend server" +if %ERRORLEVEL% NEQ 0 ( + echo ERROR: Backend server failed to start on port 7654 + exit /b 1 +) + +REM Set host IP to bind to all interfaces +set HOST_IP=0.0.0.0 + +echo. +echo Pulse is now running in development mode with mock data! +echo - Mock Data Server: http://localhost:7656 (internal only) +echo - Backend API: http://localhost:7654 (internal only) +echo - Frontend UI: http://localhost:3000 (use this for development) +echo. +echo Access the application at: http://localhost:3000 +echo. + +REM Start the frontend Vite dev server +echo Starting frontend development server on port 3000... +cd frontend && npm run dev -- --host %HOST_IP% --port 3000 --strict-port + +REM When the frontend exits, also kill the backend and mock servers +taskkill /f /im "node.exe" /fi "WINDOWTITLE eq npm run dev:server" 2>nul +taskkill /f /im "node.exe" /fi "WINDOWTITLE eq npx ts-node src/mock/run-server.ts" 2>nul +exit /b 0 + +:check_server_running +set PORT=%~1 +set SERVER_NAME=%~2 +netstat -ano | findstr ":%PORT% " | findstr "LISTENING" > nul +if %ERRORLEVEL% NEQ 0 ( + echo %SERVER_NAME% is not running on port %PORT% + exit /b 1 +) +echo %SERVER_NAME% is running on port %PORT% +exit /b 0 \ No newline at end of file diff --git a/scripts/start-dev.js b/scripts/start-dev.js deleted file mode 100644 index 8df9a7a75..000000000 --- a/scripts/start-dev.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Cross-platform script to start the development environment - * Detects the platform and runs the appropriate script - */ - -const { spawn } = require('child_process'); -const os = require('os'); -const path = require('path'); -const fs = require('fs'); - -// Ensure logo files are properly copied to frontend/public/logos -function ensureLogoFiles() { - console.log('Ensuring logo files are properly available in frontend...'); - - const sourceDir = path.join(process.cwd(), 'public', 'logos'); - const targetDir = path.join(process.cwd(), 'frontend', 'public', 'logos'); - - // Create target directory if it doesn't exist - if (!fs.existsSync(targetDir)) { - console.log('Creating frontend/public/logos directory...'); - fs.mkdirSync(targetDir, { recursive: true }); - } - - // Copy all PNG files from source to target - if (fs.existsSync(sourceDir)) { - const files = fs.readdirSync(sourceDir); - let copyCount = 0; - - for (const file of files) { - if (file.endsWith('.png')) { - const sourcePath = path.join(sourceDir, file); - const targetPath = path.join(targetDir, file); - - fs.copyFileSync(sourcePath, targetPath); - copyCount++; - } - } - - console.log(`Copied ${copyCount} logo files to frontend/public/logos`); - } else { - console.warn('Warning: public/logos directory not found. Logo files may not be available.'); - } -} - -// Ensure logo files are available before starting the development environment -ensureLogoFiles(); - -// Detect the platform -const isWindows = os.platform() === 'win32'; - -console.log(`Detected platform: ${os.platform()}`); -console.log(`Starting development environment on ${isWindows ? 'Windows' : 'Unix-like'} system...`); - -// Define the command to run based on the platform -let command, args; - -if (isWindows) { - console.log('Running Windows start script...'); - command = path.join(process.cwd(), 'start-dev.bat'); - args = []; -} else { - console.log('Running Unix start script...'); - command = './start-dev.sh'; - args = []; -} - -// Spawn the process -const child = spawn(command, args, { - stdio: 'inherit', - shell: true, - cwd: process.cwd() -}); - -// Handle process exit -child.on('exit', (code) => { - console.log(`Development environment exited with code ${code}`); - process.exit(code); -}); - -// Handle errors -child.on('error', (err) => { - console.error('Failed to start development environment:', err); - process.exit(1); -}); \ No newline at end of file diff --git a/scripts/start-dev.sh b/scripts/start-dev.sh new file mode 100755 index 000000000..49f5c7621 --- /dev/null +++ b/scripts/start-dev.sh @@ -0,0 +1,143 @@ +#!/bin/bash + +# Make script executable if it isn't already +chmod +x "$0" + +# Check for dry run flag from command line or environment variable +DRY_RUN=false +if [ "$DRY_RUN" = "true" ]; then + echo "Dry run mode enabled via environment variable - will not actually start the server" +else + for arg in "$@"; do + if [ "$arg" == "--dry-run" ]; then + DRY_RUN=true + echo "Dry run mode enabled via command line flag - will not actually start the server" + fi + done +fi + +# Check if running in Docker +if [ -n "$DOCKER_CONTAINER" ]; then + echo "Running in Docker container" +else + # Stop any running Pulse Docker containers first if not in Docker + echo "Stopping any running Pulse Docker containers..." + if command -v docker &> /dev/null; then + docker ps -q --filter "name=pulse" | xargs -r docker stop + else + echo "Docker not found, skipping container cleanup..." + fi +fi + +# Kill any existing servers +echo "Killing any existing servers..." +pkill -f "node dist/server.js" || true +pkill -f "ts-node src/mock/run-server.ts" || true +npx kill-port 7654 7656 3000 + +# Clear any existing data files that might persist between sessions +echo "Clearing any persisted data from previous sessions..." +node scripts/clear-data.js + +# Set environment to development +export NODE_ENV=development + +# Load environment variables from .env if it exists +if [ -f .env ]; then + echo "Loading environment from .env" + set -a + source .env + set +a +fi + +# Override with development settings +export USE_MOCK_DATA=true +export MOCK_DATA_ENABLED=true +export MOCK_SERVER_PORT=7656 + +# Check if we should use mock data +if [ "$USE_MOCK_DATA" = "true" ] || [ "$MOCK_DATA_ENABLED" = "true" ]; then + echo "Starting development environment with mock data..." + + # Start the mock data server + echo "Starting mock data server on port 7656..." + + if [ "$DRY_RUN" = false ]; then + # If running in Docker, we need to bind to 0.0.0.0 instead of localhost + if [ -n "$DOCKER_CONTAINER" ]; then + # Create a modified version of the run-server.ts file that binds to 0.0.0.0 + echo "global.HOST = '0.0.0.0';" > /tmp/mock-server-config.js + # Start the mock server with the modified config + NODE_OPTIONS="--require /tmp/mock-server-config.js" MOCK_SERVER_PORT=7656 ts-node src/mock/run-server.ts > /tmp/pulse-mock-server.log 2>&1 & + else + # Start the mock server normally + MOCK_SERVER_PORT=7656 ts-node src/mock/run-server.ts > /tmp/pulse-mock-server.log 2>&1 & + fi + + MOCK_SERVER_PID=$! + + # Wait a moment for the mock server to start + sleep 2 + + # Verify mock server is running + if curl -s http://localhost:7656 > /dev/null; then + echo "✅ Mock server is running on port 7656" + else + echo "❌ Mock server failed to start" + cat /tmp/pulse-mock-server.log + fi + else + echo "[DRY RUN] Would start mock data server" + fi +else + echo "Starting development environment with real data..." +fi + +# Start the backend server with hot reloading on port 7654 +echo "Starting backend server on port 7654..." +if [ "$DRY_RUN" = false ]; then + PORT=7654 ts-node-dev --respawn --transpile-only src/server.ts & + BACKEND_PID=$! + + # Wait a moment for the server to start + sleep 3 + + # Verify backend server is running + if curl -s http://localhost:7654/api/status > /dev/null; then + echo "✅ Backend server is running on port 7654" + else + echo "❌ Backend server failed to start" + fi +else + echo "[DRY RUN] Would start backend server with: PORT=7654 ts-node-dev --respawn --transpile-only src/server.ts" +fi + +# Get the host IP (use 0.0.0.0 in Docker, otherwise use localhost) +HOST_IP="0.0.0.0" +if [ -z "$DOCKER_CONTAINER" ]; then + # Not in Docker, use 0.0.0.0 to bind to all interfaces + HOST_IP="0.0.0.0" +fi + +echo "" +echo "Pulse is now running in development mode!" +echo "- Backend API: http://localhost:7654" +echo "- Mock Server: http://localhost:7656" +echo "- Frontend UI: http://localhost:3000 (use this for development)" +echo "" +echo "Access the application at: http://localhost:3000" +echo "" + +# Start the frontend Vite dev server with hot reloading on port 3000 +echo "Starting frontend development server on port 3000..." +if [ "$DRY_RUN" = false ]; then + cd frontend && npm run dev -- --host "$HOST_IP" --port 3000 --strict-port + + # When the frontend exits, also kill the backend and mock server + kill $BACKEND_PID + if [ -n "$MOCK_SERVER_PID" ]; then + kill $MOCK_SERVER_PID + fi +else + echo "[DRY RUN] Would start frontend with: cd frontend && npm run dev -- --host \"$HOST_IP\" --port 3000 --strict-port" +fi \ No newline at end of file diff --git a/scripts/start-mock-dev.bat b/scripts/start-mock-dev.bat new file mode 100644 index 000000000..e6d75ea57 --- /dev/null +++ b/scripts/start-mock-dev.bat @@ -0,0 +1,77 @@ +@echo off +setlocal + +REM Stop any running Pulse Docker containers first +echo Stopping any running Pulse Docker containers... +where docker >nul 2>&1 +if %ERRORLEVEL% EQU 0 ( + for /f "tokens=*" %%i in ('docker ps -q --filter "name=pulse"') do ( + docker stop %%i + ) +) else ( + echo Docker not found, skipping container cleanup... +) + +REM Kill any existing servers +echo Killing any existing servers... +taskkill /f /im "node.exe" /fi "WINDOWTITLE eq node dist/server.js" 2>nul +call npx kill-port 7654 7655 3000 + +REM Set environment to development with mock data +set NODE_ENV=development +set USE_MOCK_DATA=true +set MOCK_DATA_ENABLED=true + +REM Load environment variables from .env if it exists +if exist .env ( + echo Loading environment from .env + for /f "tokens=*" %%a in (.env) do ( + set "%%a" + ) +) + +REM Override with mock data settings +set USE_MOCK_DATA=true +set MOCK_DATA_ENABLED=true + +REM Start the mock data server +echo Starting mock data server... +start /b cmd /c "ts-node src/mock/run-server.ts > %TEMP%\pulse-mock-server.log 2>&1" + +REM Wait a moment for the mock server to start +timeout /t 2 /nobreak > nul + +REM Start the backend server with mock data +echo Starting backend server with mock data on port 7655... +start /b cmd /c "set USE_MOCK_DATA=true && set MOCK_DATA_ENABLED=true && set PORT=7655 && npm run dev:server" + +REM Wait a moment for the server to start +timeout /t 3 /nobreak > nul + +REM Set host IP to bind to all interfaces +set HOST_IP=0.0.0.0 + +REM Verify mock data is enabled +echo Verifying mock data is enabled... +curl -s "http://localhost:7655/api/status" | findstr "mockDataEnabled" > nul +if %ERRORLEVEL% EQU 0 ( + echo ✅ Mock data is enabled +) else ( + echo ❌ Mock data is NOT enabled +) + +echo. +echo Pulse is now running with mock data! +echo - Backend API: http://localhost:7655 (internal only) +echo - Frontend UI: http://localhost:7654 (use this for development) +echo. +echo Access the application at: http://localhost:7654 +echo. + +REM Start the frontend Vite dev server +echo Starting Pulse interface with mock data on port 7654... +cd frontend && set USE_MOCK_DATA=true && set MOCK_DATA_ENABLED=true && npm run dev -- --host %HOST_IP% --port 7654 + +REM When the frontend exits, also kill the backend and mock server +taskkill /f /im "node.exe" /fi "WINDOWTITLE eq npm run dev:server" 2>nul +taskkill /f /im "node.exe" /fi "WINDOWTITLE eq ts-node src/mock/run-server.ts" 2>nul \ No newline at end of file diff --git a/scripts/start-mock-dev.js b/scripts/start-mock-dev.js deleted file mode 100644 index e915e2af6..000000000 --- a/scripts/start-mock-dev.js +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Cross-platform script to start the mock development environment - * Detects the platform and runs the appropriate script - */ - -const { spawn } = require('child_process'); -const os = require('os'); -const path = require('path'); -const fs = require('fs'); - -// Ensure logo files are properly copied to frontend/public/logos -function ensureLogoFiles() { - console.log('Ensuring logo files are properly available in frontend...'); - - const sourceDir = path.join(process.cwd(), 'public', 'logos'); - const targetDir = path.join(process.cwd(), 'frontend', 'public', 'logos'); - - // Create target directory if it doesn't exist - if (!fs.existsSync(targetDir)) { - console.log('Creating frontend/public/logos directory...'); - fs.mkdirSync(targetDir, { recursive: true }); - } - - // Copy all PNG files from source to target - if (fs.existsSync(sourceDir)) { - const files = fs.readdirSync(sourceDir); - let copyCount = 0; - - for (const file of files) { - if (file.endsWith('.png')) { - const sourcePath = path.join(sourceDir, file); - const targetPath = path.join(targetDir, file); - - fs.copyFileSync(sourcePath, targetPath); - copyCount++; - } - } - - console.log(`Copied ${copyCount} logo files to frontend/public/logos`); - } else { - console.warn('Warning: public/logos directory not found. Logo files may not be available.'); - } -} - -// Ensure logo files are available before starting the development environment -ensureLogoFiles(); - -// Detect the platform -const isWindows = os.platform() === 'win32'; - -console.log(`Detected platform: ${os.platform()}`); -console.log(`Starting mock development environment on ${isWindows ? 'Windows' : 'Unix-like'} system...`); - -// Define the command to run based on the platform -let command, args; - -if (isWindows) { - console.log('Running Windows mock start script...'); - command = path.join(process.cwd(), 'start-mock-dev.bat'); - args = []; -} else { - console.log('Running Unix mock start script...'); - command = './start-mock-dev.sh'; - args = []; -} - -// Spawn the process -const child = spawn(command, args, { - stdio: 'inherit', - shell: true, - cwd: process.cwd() -}); - -// Handle process exit -child.on('exit', (code) => { - console.log(`Mock development environment exited with code ${code}`); - process.exit(code); -}); - -// Handle errors -child.on('error', (err) => { - console.error('Failed to start mock development environment:', err); - process.exit(1); -}); \ No newline at end of file diff --git a/scripts/start-mock-dev.sh b/scripts/start-mock-dev.sh new file mode 100755 index 000000000..42c4a9280 --- /dev/null +++ b/scripts/start-mock-dev.sh @@ -0,0 +1,119 @@ +#!/bin/bash + +# Make script executable if it isn't already +chmod +x "$0" + +# Check for dry run flag from command line or environment variable +DRY_RUN=false +if [ "$DRY_RUN" = "true" ]; then + echo "Dry run mode enabled via environment variable - will not actually start the server" +else + for arg in "$@"; do + if [ "$arg" == "--dry-run" ]; then + DRY_RUN=true + echo "Dry run mode enabled via command line flag - will not actually start the server" + fi + done +fi + +# Stop any running Pulse Docker containers first +echo "Stopping any running Pulse Docker containers..." +if command -v docker &> /dev/null; then + docker ps -q --filter "name=pulse" | xargs -r docker stop +else + echo "Docker not found, skipping container cleanup..." +fi + +# Kill any existing servers +echo "Killing any existing servers..." +pkill -f "node dist/server.js" || true +pkill -f "ts-node src/mock/run-server.ts" || true +npx kill-port 7654 7655 7656 5173 + +# Set environment to development with mock data +export NODE_ENV=development +export USE_MOCK_DATA=true +export MOCK_DATA_ENABLED=true +export MOCK_SERVER_PORT=7656 + +# Load environment variables from .env if it exists +if [ -f .env ]; then + echo "Loading environment from .env" + set -a + source .env + set +a + # Override mock data settings + export USE_MOCK_DATA=true + export MOCK_DATA_ENABLED=true + export MOCK_SERVER_PORT=7656 +fi + +# Start the mock data server +echo "Starting mock data server on port 7656..." +if [ "$DRY_RUN" = false ]; then + MOCK_SERVER_PORT=7656 ts-node src/mock/run-server.ts > /tmp/pulse-mock-server.log 2>&1 & + MOCK_SERVER_PID=$! + + # Wait a moment for the mock server to start + sleep 2 + + # Check if the mock data server is running + if ! ps -p $MOCK_SERVER_PID > /dev/null; then + echo "Error: Mock data server failed to start." + echo "Check the logs at /tmp/pulse-mock-server.log for details." + exit 1 + fi + + # Verify mock server is running + if curl -s http://localhost:7656 > /dev/null; then + echo "✅ Mock server is running on port 7656" + else + echo "❌ Mock server failed to start" + cat /tmp/pulse-mock-server.log + fi +else + echo "[DRY RUN] Would start mock data server with: MOCK_SERVER_PORT=7656 ts-node src/mock/run-server.ts" +fi + +# Start the backend server with mock data on port 7654 +echo "Starting backend server with mock data on port 7654..." +if [ "$DRY_RUN" = false ]; then + USE_MOCK_DATA=true MOCK_DATA_ENABLED=true PORT=7654 npm run dev:server & + BACKEND_PID=$! + + # Wait a moment for the server to start + sleep 3 + + # Verify mock data is enabled - check port 7654 where the backend is running + echo "Verifying mock data is enabled..." + curl -s "http://localhost:7654/api/status" | grep -q "mockDataEnabled" && echo "✅ Mock data is enabled" || echo "❌ Mock data is NOT enabled" +else + echo "[DRY RUN] Would start backend server with: USE_MOCK_DATA=true MOCK_DATA_ENABLED=true PORT=7654 npm run dev:server" +fi + +# Get the host IP (use 0.0.0.0 in Docker, otherwise use localhost or your local IP) +HOST_IP="0.0.0.0" +if [[ -z "${DOCKER_CONTAINER}" ]]; then + # Not in Docker, still use 0.0.0.0 to bind to all interfaces + HOST_IP="0.0.0.0" +fi + +echo "" +echo "Pulse is now running with mock data!" +echo "- Backend API: http://localhost:7654" +echo "- Mock Server: http://localhost:7656" +echo "- Frontend UI: http://localhost:5173 (use this for development)" +echo "" +echo "Access the application at: http://localhost:5173" +echo "" + +# Start the frontend Vite dev server on port 5173 +echo "Starting Pulse interface with mock data on port 5173..." +if [ "$DRY_RUN" = false ]; then + cd frontend && USE_MOCK_DATA=true MOCK_DATA_ENABLED=true npm run dev -- --host "${HOST_IP}" --port 5173 + + # When the frontend exits, also kill the backend and mock server + kill $BACKEND_PID $MOCK_SERVER_PID +else + echo "[DRY RUN] Would start frontend with: cd frontend && USE_MOCK_DATA=true MOCK_DATA_ENABLED=true npm run dev -- --host \"${HOST_IP}\" --port 5173" +fi \ No newline at end of file diff --git a/scripts/start-mock-server.js b/scripts/start-mock-server.js new file mode 100755 index 000000000..989d1e2e0 --- /dev/null +++ b/scripts/start-mock-server.js @@ -0,0 +1,58 @@ +#!/usr/bin/env node + +/** + * Script to start just the mock server + * Usage: node scripts/start-mock-server.js + */ + +const { spawn } = require('child_process'); +const path = require('path'); +const fs = require('fs'); +const dotenv = require('dotenv'); + +// Load environment variables +dotenv.config(); + +// Use port 7656 to avoid conflicts with the backend server +const MOCK_PORT = 7656; + +console.log(`Starting mock server on port ${MOCK_PORT}...`); + +// Set environment variables +process.env.NODE_ENV = 'development'; +process.env.USE_MOCK_DATA = 'true'; +process.env.MOCK_DATA_ENABLED = 'true'; +process.env.MOCK_SERVER_PORT = MOCK_PORT.toString(); + +// Start the mock server +const mockServer = spawn('npx', ['ts-node', 'src/mock/run-server.ts'], { + stdio: 'inherit', + env: { + ...process.env, + NODE_ENV: 'development', + USE_MOCK_DATA: 'true', + MOCK_DATA_ENABLED: 'true', + PORT: MOCK_PORT.toString(), + MOCK_SERVER_PORT: MOCK_PORT.toString() + } +}); + +// Handle mock server exit +mockServer.on('exit', (code) => { + console.log(`Mock server exited with code ${code}`); + process.exit(code); +}); + +// Handle process termination +process.on('SIGINT', () => { + console.log('Stopping mock server...'); + mockServer.kill(); +}); + +process.on('SIGTERM', () => { + console.log('Stopping mock server...'); + mockServer.kill(); +}); + +console.log(`Mock server started on port ${MOCK_PORT}`); +console.log('Press Ctrl+C to stop'); \ No newline at end of file diff --git a/scripts/start-prod.bat b/scripts/start-prod.bat new file mode 100644 index 000000000..fec0e5cac --- /dev/null +++ b/scripts/start-prod.bat @@ -0,0 +1,71 @@ +@echo off +setlocal enabledelayedexpansion + +REM Check for dry run flag +set DRY_RUN=false +for %%a in (%*) do ( + if "%%a"=="--dry-run" ( + set DRY_RUN=true + echo Dry run mode enabled - will not actually start the server + ) +) + +REM Kill any existing server processes +taskkill /f /im "node.exe" /fi "WINDOWTITLE eq node dist/server.js" 2>nul +taskkill /f /im "node.exe" /fi "WINDOWTITLE eq ts-node src/mock/run-server.ts" 2>nul +call npx kill-port 7654 7655 7656 5173 + +REM Clear any existing data files that might persist between sessions +echo Clearing any persisted data from previous sessions... +node scripts/clear-data.js + +REM Set environment to production +set NODE_ENV=production + +REM Load environment variables from .env if it exists +if exist .env ( + echo Loading environment from .env + for /f "tokens=*" %%a in (.env) do ( + set "line=%%a" + if not "!line:~0,1!"=="#" ( + if not "!line!"=="" ( + set "%%a" + ) + ) + ) +) + +REM Force mock data to be disabled for production +set USE_MOCK_DATA=false +set MOCK_DATA_ENABLED=false + +REM Build the backend +echo Building the backend... +if "%DRY_RUN%"=="false" ( + call npm run build +) else ( + echo [DRY RUN] Would run: npm run build +) + +REM Build the frontend +echo Building the frontend... +if "%DRY_RUN%"=="false" ( + cd frontend + call npm run build + cd .. +) else ( + echo [DRY RUN] Would run: cd frontend ^&^& npm run build ^&^& cd .. +) + +REM Start the production server +echo Starting production server... +if "%DRY_RUN%"=="false" ( + node dist/server.js +) else ( + echo [DRY RUN] Would run: node dist/server.js +) + +REM When the server exits, also kill the mock server if it's running +if "%USE_MOCK_DATA%"=="true" ( + taskkill /f /im "node.exe" /fi "WINDOWTITLE eq ts-node src/mock/run-server.ts" 2>nul +) \ No newline at end of file diff --git a/scripts/start-prod.sh b/scripts/start-prod.sh new file mode 100755 index 000000000..e414855e4 --- /dev/null +++ b/scripts/start-prod.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +# Make script executable if it isn't already +chmod +x "$0" + +# Check for dry run flag from command line or environment variable +DRY_RUN=false +if [ "$DRY_RUN" = "true" ]; then + echo "Dry run mode enabled via environment variable - will not actually start the server" +else + for arg in "$@"; do + if [ "$arg" == "--dry-run" ]; then + DRY_RUN=true + echo "Dry run mode enabled via command line flag - will not actually start the server" + fi + done +fi + +# Kill any existing servers +echo "Killing any existing servers..." +pkill -f "node dist/server.js" || true +pkill -f "ts-node src/mock/run-server.ts" || true +npx kill-port 7654 7655 7656 5173 + +# Clear any existing data files that might persist between sessions +echo "Clearing any persisted data from previous sessions..." +node scripts/clear-data.js + +# Set environment to production and load the environment file +export NODE_ENV=production + +# Load environment variables from .env if it exists +if [ -f .env ]; then + echo "Loading environment from .env" + set -a + source .env + set +a +fi + +# Force mock data to be disabled for production +export USE_MOCK_DATA=false +export MOCK_DATA_ENABLED=false + +# Build the backend +echo "Building backend..." +if [ "$DRY_RUN" = false ]; then + npm run build +else + echo "[DRY RUN] Would run: npm run build" +fi + +# Build the frontend +echo "Building frontend..." +if [ "$DRY_RUN" = false ]; then + cd frontend && npm run build && cd .. +else + echo "[DRY RUN] Would run: cd frontend && npm run build && cd .." +fi + +# Start the production server +echo "Starting production server..." +if [ "$DRY_RUN" = false ]; then + node dist/server.js +else + echo "[DRY RUN] Would run: node dist/server.js" +fi + +# When the server exits, also kill the mock server if it's running +if [ -n "$MOCK_SERVER_PID" ]; then + kill $MOCK_SERVER_PID +fi \ No newline at end of file diff --git a/scripts/start.js b/scripts/start.js new file mode 100644 index 000000000..96f58ca7d --- /dev/null +++ b/scripts/start.js @@ -0,0 +1,137 @@ +/** + * Unified cross-platform script to start Pulse in different environments + * Usage: node start.js [dev|prod] [--dry-run] + */ + +const { spawn } = require('child_process'); +const os = require('os'); +const path = require('path'); +const fs = require('fs'); +const dotenv = require('dotenv'); + +// Parse command line arguments +const args = process.argv.slice(2); +const mode = args[0] || 'dev'; // Default to dev mode if not specified +const isDryRun = args.includes('--dry-run'); + +// Detect the platform +const isWindows = os.platform() === 'win32'; + +console.log(`Detected platform: ${os.platform()}`); +console.log(`Starting Pulse in ${mode} mode on ${isWindows ? 'Windows' : 'Unix-like'} system...`); +if (isDryRun) { + console.log('Dry run mode enabled - commands will be shown but not executed'); +} + +// Load environment variables +function loadEnvironment() { + // Load from .env file + dotenv.config(); + + // For development mode, override with development settings if not already set + if (mode === 'dev' && process.env.NODE_ENV !== 'development') { + process.env.NODE_ENV = 'development'; + process.env.USE_MOCK_DATA = 'true'; + process.env.MOCK_DATA_ENABLED = 'true'; + process.env.MOCK_SERVER_PORT = '7656'; + console.log('Using development settings with mock data'); + } else if (mode === 'prod' && process.env.NODE_ENV !== 'production') { + process.env.NODE_ENV = 'production'; + process.env.USE_MOCK_DATA = 'false'; + process.env.MOCK_DATA_ENABLED = 'false'; + console.log('Using production settings with real data'); + } +} + +// Ensure logo files are properly copied to frontend/public/logos +function ensureLogoFiles() { + console.log('Ensuring logo files are properly available in frontend...'); + + const sourceDir = path.join(process.cwd(), 'public', 'logos'); + const targetDir = path.join(process.cwd(), 'frontend', 'public', 'logos'); + + // Create target directory if it doesn't exist + if (!fs.existsSync(targetDir)) { + console.log('Creating frontend/public/logos directory...'); + fs.mkdirSync(targetDir, { recursive: true }); + } + + // Copy all logo files + try { + const files = fs.readdirSync(sourceDir); + for (const file of files) { + if (file.endsWith('.png') || file.endsWith('.svg')) { + const sourcePath = path.join(sourceDir, file); + const targetPath = path.join(targetDir, file); + + // Only copy if the file doesn't exist or is older + if (!fs.existsSync(targetPath) || + fs.statSync(sourcePath).mtime > fs.statSync(targetPath).mtime) { + console.log(`Copying ${file} to frontend/public/logos...`); + fs.copyFileSync(sourcePath, targetPath); + } + } + } + console.log('Logo files are up to date.'); + } catch (error) { + console.warn(`Warning: Could not copy logo files: ${error.message}`); + console.warn('This is not critical, continuing with startup...'); + } +} + +// Start the appropriate script based on the platform and mode +function startScript() { + let scriptPath; + + if (isWindows) { + // Windows scripts + if (mode === 'prod') { + scriptPath = path.join(process.cwd(), 'scripts', 'start-prod.bat'); + } else { + scriptPath = path.join(process.cwd(), 'scripts', 'start-dev.bat'); + } + } else { + // Unix-like scripts + if (mode === 'prod') { + scriptPath = path.join(process.cwd(), 'scripts', 'start-prod.sh'); + } else { + scriptPath = path.join(process.cwd(), 'scripts', 'start-dev.sh'); + } + + // Make the script executable + try { + fs.chmodSync(scriptPath, '755'); + } catch (error) { + console.error(`Error making script executable: ${error.message}`); + process.exit(1); + } + } + + console.log(`Starting script: ${scriptPath}`); + + if (isDryRun) { + console.log('Dry run mode - not actually executing the script'); + return; + } + + // Execute the script + const scriptArgs = args.filter(arg => arg !== mode && arg !== '--dry-run'); + const child = isWindows + ? spawn('cmd.exe', ['/c', scriptPath, ...scriptArgs], { stdio: 'inherit' }) + : spawn(scriptPath, scriptArgs, { stdio: 'inherit' }); + + child.on('error', (error) => { + console.error(`Error starting script: ${error.message}`); + process.exit(1); + }); + + child.on('close', (code) => { + console.log(`Script exited with code ${code}`); + process.exit(code); + }); +} + +// Main execution +loadEnvironment(); +ensureLogoFiles(); +startScript(); \ No newline at end of file diff --git a/scripts/test-cluster-api.js b/scripts/test-cluster-api.js new file mode 100644 index 000000000..0a4982b48 --- /dev/null +++ b/scripts/test-cluster-api.js @@ -0,0 +1,96 @@ +/** + * Test script to check the Proxmox cluster API response + * This will help diagnose why standalone nodes are being detected as part of a cluster + */ + +const axios = require('axios'); +const https = require('https'); +require('dotenv').config(); + +// Create an axios instance with SSL verification disabled +const createClient = (host, tokenId, tokenSecret) => { + return axios.create({ + baseURL: `${host}/api2/json`, + headers: { + 'Authorization': `PVEAPIToken=${tokenId}=${tokenSecret}` + }, + httpsAgent: new https.Agent({ + rejectUnauthorized: false + }) + }); +}; + +// Test function to check cluster status +const testClusterStatus = async (host, tokenId, tokenSecret, nodeName) => { + try { + console.log(`Testing cluster status for node: ${nodeName} (${host})`); + const client = createClient(host, tokenId, tokenSecret); + + // Try to access the cluster status endpoint + const response = await client.get('/cluster/status'); + + console.log(`Response status: ${response.status}`); + console.log(`Response data: ${JSON.stringify(response.data, null, 2)}`); + + // Check if there's a cluster type in the response + if (response.data && response.data.data && Array.isArray(response.data.data)) { + const clusterInfo = response.data.data.find(item => item.type === 'cluster'); + if (clusterInfo) { + console.log(`Found cluster info: ${JSON.stringify(clusterInfo, null, 2)}`); + console.log(`This node IS part of a cluster named: ${clusterInfo.name}`); + } else { + console.log('No cluster type found in the response'); + console.log('This node is NOT part of a cluster, but the cluster API is available'); + } + } else { + console.log('No data array in response'); + console.log('This node is NOT part of a cluster'); + } + + console.log('-----------------------------------'); + } catch (error) { + console.error(`Error testing cluster status for ${nodeName}:`, error.message); + if (error.response) { + console.log(`Response status: ${error.response.status}`); + console.log(`Response data: ${JSON.stringify(error.response.data, null, 2)}`); + } + console.log('This node is NOT part of a cluster (error response)'); + console.log('-----------------------------------'); + } +}; + +// Main function to test all nodes +const testAllNodes = async () => { + // Node 1 + await testClusterStatus( + process.env.PROXMOX_HOST, + process.env.PROXMOX_TOKEN_ID, + process.env.PROXMOX_TOKEN_SECRET, + process.env.PROXMOX_NODE + ); + + // Node 2 + if (process.env.PROXMOX_HOST_2) { + await testClusterStatus( + process.env.PROXMOX_HOST_2, + process.env.PROXMOX_TOKEN_ID_2, + process.env.PROXMOX_TOKEN_SECRET_2, + process.env.PROXMOX_NODE_2 + ); + } + + // Node 3 + if (process.env.PROXMOX_HOST_3) { + await testClusterStatus( + process.env.PROXMOX_HOST_3, + process.env.PROXMOX_TOKEN_ID_3, + process.env.PROXMOX_TOKEN_SECRET_3, + process.env.PROXMOX_NODE_3 + ); + } +}; + +// Run the tests +testAllNodes().catch(error => { + console.error('Error running tests:', error); +}); \ No newline at end of file diff --git a/scripts/test-cluster-detection.js b/scripts/test-cluster-detection.js new file mode 100644 index 000000000..b25e50660 --- /dev/null +++ b/scripts/test-cluster-detection.js @@ -0,0 +1,119 @@ +/** + * Test script to verify our fix for cluster detection + * This script directly tests the fixed isNodeInCluster function + */ + +const axios = require('axios'); +const https = require('https'); +require('dotenv').config(); + +// Create an axios instance with SSL verification disabled +const createClient = (host, tokenId, tokenSecret) => { + return axios.create({ + baseURL: `${host}/api2/json`, + headers: { + 'Authorization': `PVEAPIToken=${tokenId}=${tokenSecret}` + }, + httpsAgent: new https.Agent({ + rejectUnauthorized: false + }) + }); +}; + +// Our fixed isNodeInCluster function +async function isNodeInCluster(client, nodeName) { + try { + console.log(`Testing cluster detection for node: ${nodeName}`); + + // Try to access the cluster status endpoint + const response = await client.get('/cluster/status'); + + console.log(`Response status: ${response.status}`); + console.log(`Response data: ${JSON.stringify(response.data, null, 2)}`); + + if (response.data && response.data.data && Array.isArray(response.data.data)) { + // Only consider it a cluster if we find an item with type: "cluster" + const clusterInfo = response.data.data.find(item => item.type === 'cluster'); + + console.log(`Cluster info: ${JSON.stringify(clusterInfo, null, 2)}`); + + if (clusterInfo && clusterInfo.type === 'cluster') { + const clusterName = clusterInfo.name || 'proxmox-cluster'; + console.log(`Node IS part of cluster: ${clusterName}`); + return { isCluster: true, clusterName }; + } else { + console.log('Node has cluster API but no cluster type found - NOT part of a cluster'); + return { isCluster: false, clusterName: '' }; + } + } else { + console.log('Node is not part of a cluster (empty response data)'); + return { isCluster: false, clusterName: '' }; + } + } catch (error) { + // If we get a 404 error, it means the cluster endpoint doesn't exist, so the node is not part of a cluster + if (error.response && error.response.status === 404) { + console.log('Node is not part of a cluster (404 response from cluster endpoint)'); + return { isCluster: false, clusterName: '' }; + } + + // For other errors, log them but assume the node is not in a cluster + console.error('Error checking if node is in cluster:', error.message); + if (error.response) { + console.log(`Response status: ${error.response.status}`); + console.log(`Response data: ${JSON.stringify(error.response.data, null, 2)}`); + } + return { isCluster: false, clusterName: '' }; + } +} + +// Test function for a single node +async function testNode(host, tokenId, tokenSecret, nodeName) { + console.log(`\n=== Testing node: ${nodeName} (${host}) ===\n`); + try { + const client = createClient(host, tokenId, tokenSecret); + const result = await isNodeInCluster(client, nodeName); + console.log(`\nFinal result: Node ${nodeName} ${result.isCluster ? 'IS' : 'is NOT'} part of a cluster`); + if (result.isCluster) { + console.log(`Cluster name: ${result.clusterName}`); + } + } catch (error) { + console.error(`Error testing node ${nodeName}:`, error.message); + } + console.log('\n=== Test complete ===\n'); +} + +// Main function to test all nodes +async function testAllNodes() { + // Node 1 + await testNode( + process.env.PROXMOX_HOST, + process.env.PROXMOX_TOKEN_ID, + process.env.PROXMOX_TOKEN_SECRET, + process.env.PROXMOX_NODE + ); + + // Node 2 + if (process.env.PROXMOX_HOST_2) { + await testNode( + process.env.PROXMOX_HOST_2, + process.env.PROXMOX_TOKEN_ID_2, + process.env.PROXMOX_TOKEN_SECRET_2, + process.env.PROXMOX_NODE_2 + ); + } + + // Node 3 + if (process.env.PROXMOX_HOST_3) { + await testNode( + process.env.PROXMOX_HOST_3, + process.env.PROXMOX_TOKEN_ID_3, + process.env.PROXMOX_TOKEN_SECRET_3, + process.env.PROXMOX_NODE_3 + ); + } +} + +// Run the tests +testAllNodes().catch(error => { + console.error('Error running tests:', error); +}); \ No newline at end of file diff --git a/scripts/test-mock-cluster.ts b/scripts/test-mock-cluster.ts new file mode 100644 index 000000000..582c0ef6f --- /dev/null +++ b/scripts/test-mock-cluster.ts @@ -0,0 +1,123 @@ +/** + * Test script to demonstrate mock cluster mode + * + * This script creates mock clients for multiple nodes and shows how + * the VM and container IDs are generated with and without cluster mode. + * + * Run with: + * npx ts-node scripts/test-mock-cluster.ts + */ + +import { MockClient } from '../src/api/mock-client'; +import { NodeConfig } from '../src/types'; + +// Create mock node configs +const nodeConfigs: NodeConfig[] = [ + { + id: 'node-1', + name: 'pve-1', + host: 'http://localhost:7656', + tokenId: 'mock-token', + tokenSecret: 'mock-secret' + }, + { + id: 'node-2', + name: 'pve-2', + host: 'http://localhost:7656', + tokenId: 'mock-token', + tokenSecret: 'mock-secret' + }, + { + id: 'node-3', + name: 'pve-3', + host: 'http://localhost:7656', + tokenId: 'mock-token', + tokenSecret: 'mock-secret' + } +]; + +// Test with cluster mode enabled +async function testWithClusterMode() { + console.log('\n=== Testing with Cluster Mode Enabled ==='); + + // Set environment variables for cluster mode + process.env.MOCK_CLUSTER_ENABLED = 'true'; + process.env.MOCK_CLUSTER_NAME = 'test-cluster'; + + const clients = nodeConfigs.map(config => new MockClient(config)); + + // Get VMs and containers from each client + for (let i = 0; i < clients.length; i++) { + const client = clients[i]; + const vms = await client.getVMs(); + const containers = await client.getContainers(); + + console.log(`\nNode: ${nodeConfigs[i].name} (${nodeConfigs[i].id})`); + console.log('VMs:'); + vms.forEach(vm => { + console.log(` ${vm.id} (VMID: ${vm.vmid}, Name: ${vm.name}, Node: ${vm.node})`); + }); + + console.log('Containers:'); + containers.forEach(container => { + console.log(` ${container.id} (VMID: ${container.vmid}, Name: ${container.name}, Node: ${container.node})`); + }); + } + + // Clean up + clients.forEach(client => client.disconnect()); +} + +// Test with cluster mode disabled +async function testWithoutClusterMode() { + console.log('\n=== Testing with Cluster Mode Disabled ==='); + + // Set environment variables to disable cluster mode + process.env.MOCK_CLUSTER_ENABLED = 'false'; + + const clients = nodeConfigs.map(config => new MockClient(config)); + + // Get VMs and containers from each client + for (let i = 0; i < clients.length; i++) { + const client = clients[i]; + const vms = await client.getVMs(); + const containers = await client.getContainers(); + + console.log(`\nNode: ${nodeConfigs[i].name} (${nodeConfigs[i].id})`); + console.log('VMs:'); + vms.forEach(vm => { + console.log(` ${vm.id} (VMID: ${vm.vmid}, Name: ${vm.name}, Node: ${vm.node})`); + }); + + console.log('Containers:'); + containers.forEach(container => { + console.log(` ${container.id} (VMID: ${container.vmid}, Name: ${container.name}, Node: ${container.node})`); + }); + } + + // Clean up + clients.forEach(client => client.disconnect()); +} + +// Run the tests +async function runTests() { + try { + await testWithClusterMode(); + await testWithoutClusterMode(); + + console.log('\n=== Summary ==='); + console.log('With cluster mode enabled:'); + console.log('- VMs and containers have IDs like: test-cluster-vm-100, test-cluster-ct-200'); + console.log('- The same VMID across different nodes will have the same ID'); + console.log('- This results in deduplication in the UI'); + + console.log('\nWith cluster mode disabled:'); + console.log('- VMs and containers have IDs like: node-1-vm-100, node-2-vm-100'); + console.log('- The same VMID across different nodes will have different IDs'); + console.log('- This results in showing all VMs/containers from all nodes in the UI'); + } catch (error) { + console.error('Error running tests:', error); + } +} + +runTests(); \ No newline at end of file diff --git a/scripts/update-screenshots.sh b/scripts/update-screenshots.sh index 52ef1af4a..9a854fd4d 100755 --- a/scripts/update-screenshots.sh +++ b/scripts/update-screenshots.sh @@ -47,6 +47,19 @@ echo "🚀 Starting mock data server..." export NODE_ENV=development export USE_MOCK_DATA=true export MOCK_DATA_ENABLED=true + +# Load environment variables from .env if it exists +if [ -f "$PROJECT_ROOT/.env" ]; then + echo "Loading environment from .env" + set -a + source "$PROJECT_ROOT/.env" + set +a + # Override with mock data settings + export NODE_ENV=development + export USE_MOCK_DATA=true + export MOCK_DATA_ENABLED=true +fi + cd "$PROJECT_ROOT" && ts-node src/mock/run-server.ts > /tmp/pulse-mock-server.log 2>&1 & MOCK_SERVER_PID=$! echo "Mock server started with PID: $MOCK_SERVER_PID" @@ -73,7 +86,7 @@ if ! ps -p $BACKEND_PID > /dev/null; then fi echo "🚀 Starting frontend server..." -cd "$PROJECT_ROOT/frontend" && USE_MOCK_DATA=true MOCK_DATA_ENABLED=true npm run dev -- --host "0.0.0.0" --port 3000 > /tmp/pulse-frontend.log 2>&1 & +cd "$PROJECT_ROOT/frontend" && USE_MOCK_DATA=true MOCK_DATA_ENABLED=true npm run dev -- --host "0.0.0.0" --port 7654 > /tmp/pulse-frontend.log 2>&1 & FRONTEND_PID=$! echo "Frontend server started with PID: $FRONTEND_PID" @@ -82,15 +95,15 @@ echo "⏳ Waiting for servers to start..." sleep 15 # Verify services are running correctly -if ! curl -s http://localhost:3000 > /dev/null; then - echo "❌ Error: Frontend server is not running on port 3000" +if ! curl -s http://localhost:7654 > /dev/null; then + echo "❌ Error: Frontend server is not running on port 7654" cat /tmp/pulse-frontend.log exit 1 fi # Check if mock data server is responding -if ! curl -s http://localhost:7655/status > /dev/null; then - echo "❌ Error: Mock data server is not responding on port 7655" +if ! curl -s http://localhost:7656/status > /dev/null; then + echo "❌ Error: Mock data server is not responding on port 7656" cat /tmp/pulse-mock-server.log exit 1 fi diff --git a/scripts/verify-cluster-config.js b/scripts/verify-cluster-config.js new file mode 100755 index 000000000..a0454a9ae --- /dev/null +++ b/scripts/verify-cluster-config.js @@ -0,0 +1,120 @@ +#!/usr/bin/env node + +/** + * Verify Cluster Configuration Script + * + * This script checks the current environment configuration for cluster detection settings + * and verifies that they are correctly applied. + * + * Usage: node scripts/verify-cluster-config.js + */ + +const fs = require('fs'); +const path = require('path'); +const dotenv = require('dotenv'); + +// Load environment variables from .env file +const envFilePath = path.resolve(process.cwd(), '.env'); +if (!fs.existsSync(envFilePath)) { + console.error('Error: .env file not found'); + process.exit(1); +} + +const envConfig = dotenv.parse(fs.readFileSync(envFilePath)); + +// Check cluster configuration +console.log('=== Cluster Configuration ==='); +console.log(`PROXMOX_AUTO_DETECT_CLUSTER: ${envConfig.PROXMOX_AUTO_DETECT_CLUSTER || 'not set'}`); +console.log(`PROXMOX_CLUSTER_MODE: ${envConfig.PROXMOX_CLUSTER_MODE || 'not set'}`); +console.log(`MOCK_CLUSTER_ENABLED: ${envConfig.MOCK_CLUSTER_ENABLED || 'not set'}`); + +// Check environment mode +console.log('\n=== Environment Mode ==='); +console.log(`NODE_ENV: ${envConfig.NODE_ENV || 'not set'}`); +console.log(`LOG_LEVEL: ${envConfig.LOG_LEVEL || 'not set'}`); +console.log(`USE_MOCK_DATA: ${envConfig.USE_MOCK_DATA || 'not set'}`); +console.log(`MOCK_DATA_ENABLED: ${envConfig.MOCK_DATA_ENABLED || 'not set'}`); + +// Check if the configuration is consistent +console.log('\n=== Configuration Analysis ==='); + +// Check if cluster detection is enabled +const isClusterDetectionEnabled = + envConfig.PROXMOX_AUTO_DETECT_CLUSTER === 'true' && + envConfig.PROXMOX_CLUSTER_MODE === 'true'; + +// Check if we're in development mode +const isDevMode = envConfig.NODE_ENV === 'development'; + +// Check if we're using mock data +const isMockDataEnabled = + envConfig.USE_MOCK_DATA === 'true' && + envConfig.MOCK_DATA_ENABLED === 'true'; + +// Check if mock cluster is enabled +const isMockClusterEnabled = envConfig.MOCK_CLUSTER_ENABLED === 'true'; + +// Check if log level is set to info +const isLogLevelInfo = envConfig.LOG_LEVEL === 'info'; + +console.log(`Cluster Detection: ${isClusterDetectionEnabled ? 'Enabled' : 'Disabled'}`); +console.log(`Development Mode: ${isDevMode ? 'Yes' : 'No'}`); +console.log(`Log Level: ${envConfig.LOG_LEVEL || 'not set'}`); +console.log(`Mock Data: ${isMockDataEnabled ? 'Enabled' : 'Disabled'}`); +console.log(`Mock Cluster: ${isMockClusterEnabled ? 'Enabled' : 'Disabled'}`); + +// Verify configuration consistency +console.log('\n=== Configuration Consistency ==='); + +if (isDevMode) { + if (isMockDataEnabled) { + console.log('✓ Development mode is correctly using mock data'); + + if (isLogLevelInfo) { + console.log('✓ Log level is correctly set to info for development mode'); + } else { + console.log('✗ Log level should be set to info for development mode'); + } + + if (isClusterDetectionEnabled) { + console.log('✓ Cluster detection is correctly enabled for development mode'); + + if (isMockClusterEnabled) { + console.log('✓ Mock cluster is correctly enabled for development mode with cluster detection'); + } else { + console.log('✗ Mock cluster should be enabled for development mode with cluster detection'); + } + } else { + console.log('✗ Cluster detection should be enabled for development mode'); + + if (!isMockClusterEnabled) { + console.log('✓ Mock cluster is correctly disabled for no-cluster mode'); + } else { + console.log('✗ Mock cluster should be disabled for no-cluster mode'); + } + } + } else { + console.log('✗ Development mode should use mock data'); + } +} else { + // Production mode + if (!isMockDataEnabled) { + console.log('✓ Production mode is correctly not using mock data'); + + if (isLogLevelInfo) { + console.log('✓ Log level is correctly set to info for production mode'); + } else { + console.log('✗ Log level should be set to info for production mode'); + } + + if (isClusterDetectionEnabled) { + console.log('✓ Cluster detection is correctly enabled for production mode'); + } else { + console.log('✗ Cluster detection should be enabled for production mode'); + } + } else { + console.log('✗ Production mode should not use mock data'); + } +} + +console.log('\nVerification complete.'); \ No newline at end of file