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 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
43 lines
No EOL
1.4 KiB
TypeScript
43 lines
No EOL
1.4 KiB
TypeScript
import dotenv from 'dotenv';
|
|
|
|
dotenv.config();
|
|
|
|
export const config = {
|
|
port: parseInt(process.env.PORT || '3001'),
|
|
host: process.env.HOST || 'localhost',
|
|
nodeEnv: process.env.NODE_ENV || 'development',
|
|
jwtSecret: process.env.JWT_SECRET || 'your-super-secret-jwt-key-change-in-production',
|
|
corsOrigin: process.env.CORS_ORIGIN ? process.env.CORS_ORIGIN.split(',') : ['http://localhost:3000'],
|
|
|
|
// Rate limiting
|
|
rateLimits: {
|
|
pixelsPerMinute: parseInt(process.env.RATE_LIMIT_PIXELS_PER_MINUTE || '60'),
|
|
pixelsPerHour: parseInt(process.env.RATE_LIMIT_PIXELS_PER_HOUR || '1000'),
|
|
cursorUpdatesPerSecond: parseInt(process.env.RATE_LIMIT_CURSOR_PER_SECOND || '10'),
|
|
},
|
|
|
|
// Canvas settings
|
|
canvas: {
|
|
maxSize: parseInt(process.env.MAX_CANVAS_SIZE || '10000'),
|
|
defaultSize: parseInt(process.env.DEFAULT_CANVAS_SIZE || '1000'),
|
|
chunkSize: parseInt(process.env.CHUNK_SIZE || '64'),
|
|
},
|
|
|
|
// Redis settings
|
|
redis: {
|
|
url: process.env.REDIS_URL || 'redis://localhost:6379',
|
|
keyPrefix: process.env.REDIS_KEY_PREFIX || 'gaplace:',
|
|
},
|
|
|
|
// Logging
|
|
logLevel: process.env.LOG_LEVEL || 'info',
|
|
} as const;
|
|
|
|
export function validateConfig(): void {
|
|
const required = ['JWT_SECRET'];
|
|
const missing = required.filter(key => !process.env[key]);
|
|
|
|
if (missing.length > 0 && config.nodeEnv === 'production') {
|
|
throw new Error(`Missing required environment variables: ${missing.join(', ')}`);
|
|
}
|
|
} |