Shatteredvoid/stop-game.js
MegaProxy e681c446b6 feat: implement comprehensive startup system and fix authentication
Major improvements:
- Created startup orchestration system with health monitoring and graceful shutdown
- Fixed user registration and login with simplified authentication flow
- Rebuilt authentication forms from scratch with direct API integration
- Implemented comprehensive debugging and error handling
- Added Redis fallback functionality for disabled environments
- Fixed CORS configuration for cross-origin frontend requests
- Simplified password validation to 6+ characters (removed complexity requirements)
- Added toast notifications at app level for better UX feedback
- Created comprehensive startup/shutdown scripts with OODA methodology
- Fixed database validation and connection issues
- Implemented TokenService memory fallback when Redis is disabled

Technical details:
- New SimpleLoginForm.tsx and SimpleRegisterForm.tsx components
- Enhanced CORS middleware with additional allowed origins
- Simplified auth validators and removed strict password requirements
- Added extensive logging and diagnostic capabilities
- Fixed authentication middleware token validation
- Implemented graceful Redis error handling throughout the stack
- Created modular startup system with configurable health checks

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 12:53:25 +00:00

377 lines
No EOL
14 KiB
JavaScript
Executable file

#!/usr/bin/env node
/**
* Shattered Void MMO Server Shutdown Script
* Gracefully stops all running game services
*/
const { spawn, exec } = require('child_process');
const { promisify } = require('util');
const execAsync = promisify(exec);
// Console colors for better output
const colors = {
reset: '\x1b[0m',
bright: '\x1b[1m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m',
white: '\x1b[37m',
};
function log(level, message, data = {}) {
const timestamp = new Date().toISOString();
const logData = Object.keys(data).length > 0 ? ` ${JSON.stringify(data, null, 2)}` : '';
let color = colors.white;
let levelStr = level.toUpperCase().padEnd(7);
switch (level.toLowerCase()) {
case 'info':
color = colors.cyan;
break;
case 'success':
color = colors.green;
break;
case 'warn':
color = colors.yellow;
break;
case 'error':
color = colors.red;
break;
}
console.log(`${colors.bright}[${timestamp}] [PID:${process.pid}] [${color}${levelStr}${colors.reset}${colors.bright}]${colors.reset} ${color}${message}${colors.reset}${logData}`);
}
function displayHeader() {
console.log(`${colors.cyan}╔═══════════════════════════════════════════════════════════════╗
║ ║
${colors.bright}SHATTERED VOID MMO SHUTDOWN${colors.reset}${colors.cyan}
${colors.white}Post-Collapse Galaxy Strategy Game${colors.reset}${colors.cyan}
║ ║
${colors.yellow}Gracefully stopping all running services...${colors.reset}${colors.cyan}
║ ║
╚═══════════════════════════════════════════════════════════════╝${colors.reset}`);
console.log();
}
async function findProcesses() {
log('info', 'Scanning for running game processes...');
const processes = [];
try {
// Look for the main startup script
const { stdout: startupProcs } = await execAsync('ps aux | grep "node.*start-game.js" | grep -v grep || true');
if (startupProcs.trim()) {
const lines = startupProcs.trim().split('\n');
for (const line of lines) {
const parts = line.trim().split(/\s+/);
const pid = parts[1];
processes.push({
pid,
command: 'start-game.js',
type: 'main',
description: 'Main startup orchestrator'
});
}
}
// Look for Node.js processes on our ports
const { stdout: nodeProcs } = await execAsync('ps aux | grep "node" | grep -E "(3000|5173)" | grep -v grep || true');
if (nodeProcs.trim()) {
const lines = nodeProcs.trim().split('\n');
for (const line of lines) {
const parts = line.trim().split(/\s+/);
const pid = parts[1];
if (!processes.find(p => p.pid === pid)) {
processes.push({
pid,
command: 'node (port 3000/5173)',
type: 'server',
description: 'Backend/Frontend server'
});
}
}
}
// Look for npm processes
const { stdout: npmProcs } = await execAsync('ps aux | grep "npm.*dev" | grep -v grep || true');
if (npmProcs.trim()) {
const lines = npmProcs.trim().split('\n');
for (const line of lines) {
const parts = line.trim().split(/\s+/);
const pid = parts[1];
if (!processes.find(p => p.pid === pid)) {
processes.push({
pid,
command: 'npm dev',
type: 'dev',
description: 'NPM development server'
});
}
}
}
// Look for Vite processes
const { stdout: viteProcs } = await execAsync('ps aux | grep "vite" | grep -v grep || true');
if (viteProcs.trim()) {
const lines = viteProcs.trim().split('\n');
for (const line of lines) {
const parts = line.trim().split(/\s+/);
const pid = parts[1];
if (!processes.find(p => p.pid === pid)) {
processes.push({
pid,
command: 'vite',
type: 'frontend',
description: 'Vite development server'
});
}
}
}
// Look for Python servers (static file serving)
const { stdout: pythonProcs } = await execAsync('ps aux | grep "python.*http.server" | grep -v grep || true');
if (pythonProcs.trim()) {
const lines = pythonProcs.trim().split('\n');
for (const line of lines) {
const parts = line.trim().split(/\s+/);
const pid = parts[1];
if (!processes.find(p => p.pid === pid)) {
processes.push({
pid,
command: 'python http.server',
type: 'static',
description: 'Static file server'
});
}
}
}
} catch (error) {
log('warn', 'Error scanning for processes:', { error: error.message });
}
return processes;
}
async function checkPorts() {
log('info', 'Checking port usage...');
const ports = [];
try {
const { stdout } = await execAsync('ss -tlnp | grep ":3000\\|:5173" || true');
if (stdout.trim()) {
const lines = stdout.trim().split('\n');
for (const line of lines) {
const match = line.match(/:(\d+)\s.*users:\(\("([^"]+)",pid=(\d+)/);
if (match) {
const [, port, process, pid] = match;
ports.push({ port, process, pid });
}
}
}
} catch (error) {
log('warn', 'Error checking ports:', { error: error.message });
}
return ports;
}
async function stopProcess(process) {
log('info', `Stopping ${process.description}`, { pid: process.pid, command: process.command });
try {
// Try graceful shutdown first (SIGTERM)
await execAsync(`kill -TERM ${process.pid}`);
// Wait a moment for graceful shutdown
await new Promise(resolve => setTimeout(resolve, 2000));
// Check if process is still running
try {
await execAsync(`kill -0 ${process.pid}`);
log('warn', `Process ${process.pid} still running, forcing shutdown...`);
await execAsync(`kill -KILL ${process.pid}`);
} catch (error) {
// Process already stopped
}
log('success', `Stopped ${process.description}`, { pid: process.pid });
return true;
} catch (error) {
if (error.message.includes('No such process')) {
log('info', `Process ${process.pid} already stopped`);
return true;
}
log('error', `Failed to stop process ${process.pid}:`, { error: error.message });
return false;
}
}
async function verifyShutdown() {
log('info', 'Verifying complete shutdown...');
const remainingProcesses = await findProcesses();
const remainingPorts = await checkPorts();
if (remainingProcesses.length === 0 && remainingPorts.length === 0) {
log('success', '✅ All game services successfully stopped');
return true;
} else {
if (remainingProcesses.length > 0) {
log('warn', `${remainingProcesses.length} processes still running:`, {
processes: remainingProcesses.map(p => ({ pid: p.pid, command: p.command }))
});
}
if (remainingPorts.length > 0) {
log('warn', `${remainingPorts.length} ports still in use:`, {
ports: remainingPorts.map(p => ({ port: p.port, process: p.process, pid: p.pid }))
});
}
return false;
}
}
async function main() {
const startTime = Date.now();
try {
displayHeader();
// Phase 1: Discovery
log('info', 'Phase 1: Discovering running services');
const processes = await findProcesses();
const ports = await checkPorts();
if (processes.length === 0 && ports.length === 0) {
log('info', '🎯 No running game services found');
log('success', '✅ System is already clean');
return;
}
log('info', `Found ${processes.length} processes and ${ports.length} active ports`);
if (processes.length > 0) {
console.log('\n📋 Processes to stop:');
processes.forEach(proc => {
console.log(`${colors.yellow}${proc.description}${colors.reset} (PID: ${proc.pid}) - ${proc.command}`);
});
}
if (ports.length > 0) {
console.log('\n🔌 Ports to free:');
ports.forEach(port => {
console.log(` • Port ${colors.cyan}${port.port}${colors.reset} used by ${port.process} (PID: ${port.pid})`);
});
}
console.log();
// Phase 2: Graceful shutdown
log('info', 'Phase 2: Graceful service shutdown');
let stopCount = 0;
let failCount = 0;
// Stop processes in order of importance (main process first)
const processOrder = ['main', 'server', 'dev', 'frontend', 'static'];
for (const type of processOrder) {
const processesOfType = processes.filter(p => p.type === type);
for (const process of processesOfType) {
const success = await stopProcess(process);
if (success) {
stopCount++;
} else {
failCount++;
}
}
}
// Phase 3: Verification
log('info', 'Phase 3: Verification and cleanup');
const cleanShutdown = await verifyShutdown();
// Final summary
const duration = Date.now() - startTime;
console.log();
if (cleanShutdown) {
console.log(`${colors.green}╔═══════════════════════════════════════════════════════════════╗
║ SHUTDOWN COMPLETE ║
╠═══════════════════════════════════════════════════════════════╣${colors.reset}
${colors.white}║ Duration: ${duration}ms${' '.repeat(50 - duration.toString().length)}
║ ║${colors.reset}
${colors.cyan}║ Services Stopped: ║${colors.reset}
${colors.white}║ ✅ All processes terminated ║
║ ✅ All ports freed ║
║ ✅ System clean ║${colors.reset}
${colors.green}║ ║
╚═══════════════════════════════════════════════════════════════╝${colors.reset}`);
log('info', '🎮 Game services stopped successfully');
log('info', '💡 Run "node start-game.js" to restart the game');
} else {
console.log(`${colors.yellow}╔═══════════════════════════════════════════════════════════════╗
║ SHUTDOWN INCOMPLETE ║
╠═══════════════════════════════════════════════════════════════╣${colors.reset}
${colors.white}║ Duration: ${duration}ms${' '.repeat(50 - duration.toString().length)}
║ ║${colors.reset}
${colors.white}║ Stopped: ${stopCount} processes${' '.repeat(42 - stopCount.toString().length)}
║ Failed: ${failCount} processes${' '.repeat(43 - failCount.toString().length)}${colors.reset}
${colors.yellow}║ ║
║ Some services may still be running. ║
║ Check the warnings above for details. ║
║ ║
╚═══════════════════════════════════════════════════════════════╝${colors.reset}`);
log('warn', '⚠️ Some services may still be running');
log('info', '💡 You may need to manually stop remaining processes');
process.exit(1);
}
} catch (error) {
const duration = Date.now() - startTime;
log('error', 'Shutdown script failed:', {
error: error.message,
stack: error.stack,
duration: `${duration}ms`
});
console.log(`${colors.red}╔═══════════════════════════════════════════════════════════════╗
║ SHUTDOWN FAILED ║
╠═══════════════════════════════════════════════════════════════╣${colors.reset}
${colors.white}║ Duration: ${duration}ms${' '.repeat(50 - duration.toString().length)}
║ ║${colors.reset}
${colors.red}║ An error occurred during shutdown. ║
║ Some services may still be running. ║
║ ║
╚═══════════════════════════════════════════════════════════════╝${colors.reset}`);
process.exit(1);
}
}
// Handle script interruption
process.on('SIGINT', () => {
log('warn', 'Shutdown script interrupted');
process.exit(1);
});
process.on('SIGTERM', () => {
log('warn', 'Shutdown script terminated');
process.exit(1);
});
// Run the shutdown script
if (require.main === module) {
main();
}
module.exports = { main };