Major refactor from simple HTML/JS app to modern full-stack TypeScript application: ## Architecture Changes - Migrated to monorepo structure with workspaces (backend, frontend, shared) - Backend: Node.js + Express + TypeScript + Socket.IO - Frontend: Next.js 15.5 + React 19 + TypeScript + Tailwind CSS - Shared: Common types and utilities across packages ## Key Features Implemented - Real-time WebSocket collaboration via Socket.IO - Virtual canvas with chunked loading for performance - Modern UI with dark mode and responsive design - Mock database system for easy development (Redis/PostgreSQL compatible) - Comprehensive error handling and rate limiting - User presence and cursor tracking - Infinite canvas support with zoom/pan controls ## Performance Optimizations - Canvas virtualization - only renders visible viewport - Chunked pixel data loading (64x64 pixel chunks) - Optimized WebSocket protocol - Memory-efficient state management with Zustand ## Development Experience - Full TypeScript support across all packages - Hot reload for both frontend and backend - Docker support for production deployment - Comprehensive linting and formatting - Automated development server startup ## Fixed Issues - Corrected start script paths - Updated environment configuration - Fixed ESLint configuration issues - Ensured all dependencies are properly installed - Verified build process works correctly
128 lines
No EOL
3.4 KiB
JavaScript
128 lines
No EOL
3.4 KiB
JavaScript
#!/usr/bin/env node
|
||
|
||
const { spawn, exec } = require('child_process');
|
||
const path = require('path');
|
||
const util = require('util');
|
||
const execPromise = util.promisify(exec);
|
||
|
||
console.log('🎨 Starting GaPlace Development Environment...\n');
|
||
|
||
// Function to check if port is in use
|
||
async function isPortInUse(port) {
|
||
try {
|
||
const { stdout } = await execPromise(`netstat -ano | findstr :${port}`);
|
||
return stdout.trim().length > 0;
|
||
} catch (error) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// Function to kill process on port
|
||
async function killPort(port) {
|
||
try {
|
||
await execPromise(`npx kill-port ${port}`);
|
||
console.log(`✅ Cleared port ${port}`);
|
||
} catch (error) {
|
||
console.log(`ℹ️ Port ${port} was already free`);
|
||
}
|
||
}
|
||
|
||
// Function to start a process and handle output
|
||
function startProcess(name, command, args, cwd, color) {
|
||
console.log(`${color}[${name}]${'\x1b[0m'} Starting: ${command} ${args.join(' ')}`);
|
||
|
||
const isWindows = process.platform === 'win32';
|
||
const proc = spawn(command, args, {
|
||
cwd,
|
||
stdio: 'pipe',
|
||
shell: true,
|
||
windowsHide: true
|
||
});
|
||
|
||
proc.stdout.on('data', (data) => {
|
||
const lines = data.toString().split('\n').filter(line => line.trim());
|
||
lines.forEach(line => {
|
||
console.log(`${color}[${name}]${'\x1b[0m'} ${line}`);
|
||
});
|
||
});
|
||
|
||
proc.stderr.on('data', (data) => {
|
||
const lines = data.toString().split('\n').filter(line => line.trim());
|
||
lines.forEach(line => {
|
||
console.log(`${color}[${name}]${'\x1b[0m'} ${line}`);
|
||
});
|
||
});
|
||
|
||
proc.on('close', (code) => {
|
||
if (code !== 0) {
|
||
console.log(`${color}[${name}]${'\x1b[0m'} ❌ Process exited with code ${code}`);
|
||
}
|
||
});
|
||
|
||
proc.on('error', (error) => {
|
||
console.log(`${color}[${name}]${'\x1b[0m'} ❌ Error: ${error.message}`);
|
||
});
|
||
|
||
return proc;
|
||
}
|
||
|
||
async function startDevelopment() {
|
||
try {
|
||
// Clear ports first
|
||
console.log('🧹 Clearing ports...');
|
||
await killPort(3001);
|
||
await killPort(3000);
|
||
|
||
// Wait a moment for ports to be fully cleared
|
||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||
|
||
console.log('🔧 Starting backend server...');
|
||
const backend = startProcess(
|
||
'Backend',
|
||
'npm',
|
||
['run', 'dev'],
|
||
path.join(__dirname, '..', 'backend'),
|
||
'\x1b[34m' // Blue
|
||
);
|
||
|
||
// Wait for backend to start
|
||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||
|
||
console.log('🎨 Starting frontend server...');
|
||
const frontend = startProcess(
|
||
'Frontend',
|
||
'npm',
|
||
['run', 'dev'],
|
||
path.join(__dirname, '..', 'frontend'),
|
||
'\x1b[32m' // Green
|
||
);
|
||
|
||
console.log('\n📱 Frontend: http://localhost:3000');
|
||
console.log('🔌 Backend: http://localhost:3001');
|
||
console.log('🩺 Health Check: http://localhost:3001/health');
|
||
console.log('💡 Press Ctrl+C to stop all servers\n');
|
||
|
||
// Handle Ctrl+C
|
||
process.on('SIGINT', async () => {
|
||
console.log('\n🛑 Shutting down development servers...');
|
||
backend.kill('SIGTERM');
|
||
frontend.kill('SIGTERM');
|
||
|
||
// Wait a moment then force kill if needed
|
||
setTimeout(() => {
|
||
backend.kill('SIGKILL');
|
||
frontend.kill('SIGKILL');
|
||
process.exit(0);
|
||
}, 2000);
|
||
});
|
||
|
||
// Keep the script running
|
||
setInterval(() => {}, 1000);
|
||
|
||
} catch (error) {
|
||
console.error('❌ Failed to start development environment:', error.message);
|
||
process.exit(1);
|
||
}
|
||
}
|
||
|
||
startDevelopment(); |