224 lines
5.7 KiB
JavaScript
Executable File
224 lines
5.7 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
const { spawn, execSync } = require('child_process');
|
|
const path = require('path');
|
|
|
|
const colors = {
|
|
reset: '\x1b[0m',
|
|
red: '\x1b[31m',
|
|
green: '\x1b[32m',
|
|
yellow: '\x1b[33m',
|
|
blue: '\x1b[34m',
|
|
magenta: '\x1b[35m',
|
|
cyan: '\x1b[36m',
|
|
white: '\x1b[37m',
|
|
bold: '\x1b[1m'
|
|
};
|
|
|
|
function log(message, color = colors.white) {
|
|
console.log(`${color}${message}${colors.reset}`);
|
|
}
|
|
|
|
function logStep(step, message) {
|
|
log(`[${step}] ${message}`, colors.cyan);
|
|
}
|
|
|
|
function logSuccess(message) {
|
|
log(`✅ ${message}`, colors.green);
|
|
}
|
|
|
|
function logError(message) {
|
|
log(`❌ ${message}`, colors.red);
|
|
}
|
|
|
|
function logWarning(message) {
|
|
log(`⚠️ ${message}`, colors.yellow);
|
|
}
|
|
|
|
async function checkDockerRunning() {
|
|
try {
|
|
execSync('docker info', { stdio: 'ignore' });
|
|
return true;
|
|
} catch (error) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function checkServiceRunning(containerName) {
|
|
try {
|
|
const result = execSync(`docker ps --filter "name=${containerName}" --filter "status=running" --format "{{.Names}}"`, { encoding: 'utf8' });
|
|
return result.trim() === containerName;
|
|
} catch (error) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function checkPortInUse(port) {
|
|
try {
|
|
execSync(`lsof -i:${port}`, { stdio: 'ignore' });
|
|
return true;
|
|
} catch (error) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async function waitForService(serviceName, checkCommand, maxAttempts = 30) {
|
|
logStep('WAIT', `Waiting for ${serviceName} to be ready...`);
|
|
|
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
try {
|
|
execSync(checkCommand, { stdio: 'ignore' });
|
|
logSuccess(`${serviceName} is ready!`);
|
|
return true;
|
|
} catch (error) {
|
|
if (attempt === maxAttempts) {
|
|
logError(`${serviceName} failed to start after ${maxAttempts} attempts`);
|
|
return false;
|
|
}
|
|
process.stdout.write(`⏳ Waiting for ${serviceName}... (${attempt}/${maxAttempts})\r`);
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
async function startServices() {
|
|
logStep('DOCKER', 'Checking if Docker is running...');
|
|
|
|
if (!await checkDockerRunning()) {
|
|
logError('Docker is not running. Please start Docker and try again.');
|
|
process.exit(1);
|
|
}
|
|
|
|
logSuccess('Docker is running');
|
|
|
|
// Check if services are already running
|
|
const pgRunning = await checkServiceRunning('portal-postgres');
|
|
const redisRunning = await checkServiceRunning('portal-redis');
|
|
|
|
if (pgRunning && redisRunning) {
|
|
logSuccess('PostgreSQL and Redis are already running');
|
|
} else {
|
|
logStep('SERVICES', 'Starting PostgreSQL and Redis...');
|
|
try {
|
|
execSync('docker-compose -f tools/deployment/docker-compose.yml up -d', { stdio: 'inherit' });
|
|
logSuccess('Services started');
|
|
} catch (error) {
|
|
logError(`Failed to start services: ${error.message}`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Wait for PostgreSQL
|
|
const pgReady = await waitForService(
|
|
'PostgreSQL',
|
|
'docker exec portal-postgres pg_isready -U app -d portal'
|
|
);
|
|
|
|
if (!pgReady) {
|
|
process.exit(1);
|
|
}
|
|
|
|
// Wait for Redis
|
|
const redisReady = await waitForService(
|
|
'Redis',
|
|
'docker exec portal-redis redis-cli ping'
|
|
);
|
|
|
|
if (!redisReady) {
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
async function startPrismaStudio() {
|
|
logStep('DB', 'Starting Prisma Studio...');
|
|
|
|
// Check if Prisma Studio port (5555) is already in use
|
|
const studioPortInUse = await checkPortInUse(5555);
|
|
|
|
if (studioPortInUse) {
|
|
logSuccess('Prisma Studio is already running on port 5555');
|
|
return null;
|
|
}
|
|
|
|
const studioProcess = spawn('pnpm', ['--filter', '@customer-portal/bff', 'run', 'db:studio'], {
|
|
stdio: 'pipe',
|
|
cwd: process.cwd(),
|
|
detached: true
|
|
});
|
|
|
|
// Wait a moment for Prisma Studio to start
|
|
setTimeout(() => {
|
|
logSuccess('Prisma Studio started on http://localhost:5555');
|
|
}, 3000);
|
|
|
|
return studioProcess;
|
|
}
|
|
|
|
async function startDevelopment() {
|
|
logStep('DEV', 'Starting frontend and backend in development mode...');
|
|
|
|
// Start Prisma Studio first
|
|
const studioProcess = await startPrismaStudio();
|
|
|
|
const devProcess = spawn('pnpm', ['--parallel', '--recursive', 'run', 'dev'], {
|
|
stdio: 'inherit',
|
|
cwd: process.cwd()
|
|
});
|
|
|
|
// Show ready message after services start
|
|
setTimeout(() => {
|
|
logSuccess('Development environment is fully ready!');
|
|
log(`
|
|
🎉 ${colors.bold}${colors.green}All services are running:${colors.reset}
|
|
🖥️ Frontend: ${colors.cyan}http://localhost:3000${colors.reset}
|
|
🔌 Backend: ${colors.cyan}http://localhost:4000${colors.reset}
|
|
🗄️ Database: ${colors.cyan}http://localhost:5555${colors.reset}
|
|
`, colors.white);
|
|
}, 8000); // Wait 8 seconds for Next.js to be ready
|
|
|
|
// Handle graceful shutdown
|
|
process.on('SIGINT', () => {
|
|
log('\n🛑 Shutting down development servers...', colors.yellow);
|
|
if (studioProcess) {
|
|
studioProcess.kill('SIGTERM');
|
|
}
|
|
devProcess.kill('SIGINT');
|
|
setTimeout(() => {
|
|
log('👋 Development servers stopped', colors.green);
|
|
process.exit(0);
|
|
}, 1000);
|
|
});
|
|
|
|
devProcess.on('close', (code) => {
|
|
if (studioProcess) {
|
|
studioProcess.kill('SIGTERM');
|
|
}
|
|
if (code !== 0) {
|
|
logError(`Development servers exited with code ${code}`);
|
|
}
|
|
process.exit(code);
|
|
});
|
|
}
|
|
|
|
async function main() {
|
|
log(`
|
|
${colors.bold}${colors.blue}🚀 Customer Portal Development Environment${colors.reset}
|
|
${colors.cyan}==============================================${colors.reset}
|
|
`, colors.bold);
|
|
|
|
try {
|
|
await startServices();
|
|
await startDevelopment();
|
|
} catch (error) {
|
|
logError(`Failed to start development environment: ${error.message}`);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
if (require.main === module) {
|
|
main();
|
|
}
|