// ecosystem.dev.config.cjs // ============================================================================ // DEVELOPMENT PM2 CONFIGURATION // ============================================================================ // This file mirrors the production ecosystem.config.cjs but with settings // appropriate for the development environment inside the dev container. // // Key differences from production: // - Single instance for API (not cluster mode) to allow debugger attachment // - tsx watch mode for hot reloading // - Development-specific environment variables // - Logs written to /var/log/pm2 for Logstash integration (ADR-050) // // Usage: // pm2 start ecosystem.dev.config.cjs // pm2 logs # View all logs // pm2 logs flyer-crawler-api-dev # View API logs only // pm2 restart all # Restart all processes // pm2 delete all # Stop all processes // // Related: // - ecosystem.config.cjs (production configuration) // - docs/adr/0014-linux-only-platform.md // - docs/adr/0050-postgresql-function-observability.md // ============================================================================ // --- Environment Variable Validation --- // In dev, we warn but don't fail - variables come from compose.dev.yml const requiredVars = ['DB_HOST', 'JWT_SECRET']; const missingVars = requiredVars.filter((key) => !process.env[key]); if (missingVars.length > 0) { console.warn( '\n[ecosystem.dev.config.cjs] WARNING: The following environment variables are MISSING:', ); missingVars.forEach((key) => console.warn(` - ${key}`)); console.warn( '[ecosystem.dev.config.cjs] These should be set in compose.dev.yml or .env.local\n', ); } else { console.log('[ecosystem.dev.config.cjs] Required environment variables are present.'); } // --- Shared Environment Variables --- // These come from compose.dev.yml environment section const sharedEnv = { // Timezone: PST (America/Los_Angeles) for consistent log timestamps TZ: process.env.TZ || 'America/Los_Angeles', NODE_ENV: 'development', DB_HOST: process.env.DB_HOST || 'postgres', DB_PORT: process.env.DB_PORT || '5432', DB_USER: process.env.DB_USER || 'postgres', DB_PASSWORD: process.env.DB_PASSWORD || 'postgres', DB_NAME: process.env.DB_NAME || 'flyer_crawler_dev', REDIS_URL: process.env.REDIS_URL || 'redis://redis:6379', REDIS_HOST: process.env.REDIS_HOST || 'redis', REDIS_PORT: process.env.REDIS_PORT || '6379', FRONTEND_URL: process.env.FRONTEND_URL || 'https://localhost', JWT_SECRET: process.env.JWT_SECRET || 'dev-jwt-secret-change-in-production', WORKER_LOCK_DURATION: process.env.WORKER_LOCK_DURATION || '120000', // Sentry/Bugsink (ADR-015) SENTRY_DSN: process.env.SENTRY_DSN, SENTRY_ENVIRONMENT: process.env.SENTRY_ENVIRONMENT || 'development', SENTRY_ENABLED: process.env.SENTRY_ENABLED || 'true', SENTRY_DEBUG: process.env.SENTRY_DEBUG || 'true', // Optional API keys (may not be set in dev) GEMINI_API_KEY: process.env.GEMINI_API_KEY, GOOGLE_MAPS_API_KEY: process.env.GOOGLE_MAPS_API_KEY, GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET, }; module.exports = { apps: [ // ========================================================================= // API SERVER (Development) // ========================================================================= // Single instance with tsx watch for hot reloading. // Unlike production, we don't use cluster mode to allow: // - Debugger attachment // - Simpler log output // - Hot reloading via tsx watch { name: 'flyer-crawler-api-dev', script: './node_modules/.bin/tsx', args: 'watch server.ts', cwd: '/app', watch: false, // tsx watch handles this instances: 1, // Single instance for debugging exec_mode: 'fork', // Fork mode (not cluster) for debugging max_memory_restart: '1G', // More generous in dev kill_timeout: 5000, // PM2 log configuration (ADR-050 - Logstash integration) output: '/var/log/pm2/api-out.log', error: '/var/log/pm2/api-error.log', log_date_format: 'YYYY-MM-DD HH:mm:ss Z', merge_logs: true, // Restart configuration max_restarts: 10, exp_backoff_restart_delay: 500, min_uptime: '5s', // Environment env: { ...sharedEnv, PORT: '3001', }, }, // ========================================================================= // BACKGROUND WORKER (Development) // ========================================================================= // Processes background jobs (flyer processing, cleanup, etc.) // Runs as a separate process like in production. { name: 'flyer-crawler-worker-dev', script: './node_modules/.bin/tsx', args: 'watch src/services/worker.ts', cwd: '/app', watch: false, // tsx watch handles this instances: 1, exec_mode: 'fork', max_memory_restart: '1G', kill_timeout: 10000, // Workers need more time to finish jobs // PM2 log configuration (ADR-050) output: '/var/log/pm2/worker-out.log', error: '/var/log/pm2/worker-error.log', log_date_format: 'YYYY-MM-DD HH:mm:ss Z', merge_logs: true, // Restart configuration max_restarts: 10, exp_backoff_restart_delay: 500, min_uptime: '5s', // Environment env: { ...sharedEnv, }, }, // ========================================================================= // VITE FRONTEND DEV SERVER (Development) // ========================================================================= // Vite dev server for frontend hot module replacement. // Listens on 5173 and is proxied by nginx to 443. { name: 'flyer-crawler-vite-dev', script: './node_modules/.bin/vite', args: '--host', cwd: '/app', watch: false, // Vite handles its own watching instances: 1, exec_mode: 'fork', max_memory_restart: '2G', // Vite can use significant memory kill_timeout: 5000, // PM2 log configuration (ADR-050) output: '/var/log/pm2/vite-out.log', error: '/var/log/pm2/vite-error.log', log_date_format: 'YYYY-MM-DD HH:mm:ss Z', merge_logs: true, // Restart configuration max_restarts: 10, exp_backoff_restart_delay: 500, min_uptime: '5s', // Environment env: { // Timezone: PST (America/Los_Angeles) for consistent log timestamps TZ: process.env.TZ || 'America/Los_Angeles', NODE_ENV: 'development', // Vite-specific env vars (VITE_ prefix) VITE_SENTRY_DSN: process.env.VITE_SENTRY_DSN, VITE_SENTRY_ENVIRONMENT: process.env.VITE_SENTRY_ENVIRONMENT || 'development', VITE_SENTRY_ENABLED: process.env.VITE_SENTRY_ENABLED || 'true', VITE_SENTRY_DEBUG: process.env.VITE_SENTRY_DEBUG || 'true', }, }, ], };