Reorganize and enhance scripts with improved startup and configuration tools

This commit is contained in:
courtmanr@gmail.com
2025-03-12 10:11:44 +00:00
parent f325184523
commit 47d28bc2ac
28 changed files with 2801 additions and 185 deletions
+70 -5
View File
@@ -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.
- **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.
+336
View File
@@ -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=<env> Environment (prod, dev, or dev:no-cluster, default: prod)
* --lines=<number> 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);
}
}
+111
View File
@@ -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);
}
+236
View File
@@ -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);
+71
View File
@@ -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;
+100
View File
@@ -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)'}`);
+3 -3
View File
@@ -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 "╔════════════════════════════════════════════════════════════╗"
+2 -2
View File
@@ -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';
+2 -2
View File
@@ -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.');
}
+107
View File
@@ -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
+177
View File
@@ -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}"
+238
View File
@@ -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=<level> Filter by log level (error, warn, info, debug)
* --component=<name> Filter by component name (e.g., ProxmoxClient, NodeManager)
* --search=<text> Search for specific text in logs
* --follow Follow log file in real-time (default: true)
* --lines=<number> Number of lines to show initially (default: 50)
* --file=<filename> 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;
}
+98
View File
@@ -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=<text> Search for specific text in logs
* --level=<level> Filter by log level (error, warn, info, debug)
* --component=<name> 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);
+98
View File
@@ -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
-84
View File
@@ -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);
});
+143
View File
@@ -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
+77
View File
@@ -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
-84
View File
@@ -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);
});
+119
View File
@@ -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
+58
View File
@@ -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');
+71
View File
@@ -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
)
+71
View File
@@ -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
+137
View File
@@ -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();
+96
View File
@@ -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);
});
+119
View File
@@ -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);
});
+123
View File
@@ -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();
+18 -5
View File
@@ -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
+120
View File
@@ -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.');