Files
flyer-crawler.projectium.com/ecosystem-test.config.cjs
Torben Sorensen f49f786c23 fix: Add .env file loading to ecosystem-test.config.cjs
Allows test environment PM2 processes to load environment variables
from /var/www/flyer-crawler-test.projectium.com/.env file, enabling
manual restarts without requiring CI/CD to inject variables.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-17 13:38:15 -08:00

159 lines
5.7 KiB
JavaScript

// ecosystem-test.config.cjs
// PM2 configuration for the TEST environment only.
// NOTE: The filename must end with `.config.cjs` for PM2 to recognize it as a config file.
// This file defines test-specific apps that run alongside production apps.
//
// Test apps: flyer-crawler-api-test, flyer-crawler-worker-test, flyer-crawler-analytics-worker-test
//
// These apps:
// - Run from /var/www/flyer-crawler-test.projectium.com
// - Use NODE_ENV='staging' (enables file logging in logger.server.ts)
// - Use Redis database 1 (isolated from production which uses database 0)
// - Have distinct PM2 process names to avoid conflicts with production
// --- Load Environment Variables from .env file ---
// This allows PM2 to start without requiring the CI/CD pipeline to inject variables.
// The .env file should be created on the server with the required secrets.
// NOTE: We implement a simple .env parser since dotenv may not be installed.
const path = require('path');
const fs = require('fs');
const envPath = path.join('/var/www/flyer-crawler-test.projectium.com', '.env');
if (fs.existsSync(envPath)) {
console.log('[ecosystem-test.config.cjs] Loading environment from:', envPath);
const envContent = fs.readFileSync(envPath, 'utf8');
const lines = envContent.split('\n');
for (const line of lines) {
// Skip comments and empty lines
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith('#')) continue;
// Parse KEY=value
const eqIndex = trimmed.indexOf('=');
if (eqIndex > 0) {
const key = trimmed.substring(0, eqIndex);
let value = trimmed.substring(eqIndex + 1);
// Remove quotes if present
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
}
// Only set if not already in environment (don't override CI/CD vars)
if (!process.env[key]) {
process.env[key] = value;
}
}
}
console.log('[ecosystem-test.config.cjs] Environment loaded successfully');
} else {
console.warn('[ecosystem-test.config.cjs] No .env file found at:', envPath);
console.warn(
'[ecosystem-test.config.cjs] Environment variables must be provided by the shell or CI/CD.'
);
}
// --- Environment Variable Validation ---
// NOTE: We only WARN about missing secrets, not exit.
// Calling process.exit(1) prevents PM2 from reading the apps array.
// The actual application will fail to start if secrets are missing,
// which PM2 will handle with its restart logic.
const requiredSecrets = ['DB_HOST', 'JWT_SECRET', 'GEMINI_API_KEY'];
const missingSecrets = requiredSecrets.filter(key => !process.env[key]);
if (missingSecrets.length > 0) {
console.warn('\n[ecosystem.config.test.cjs] WARNING: The following environment variables are MISSING:');
missingSecrets.forEach(key => console.warn(` - ${key}`));
console.warn('[ecosystem.config.test.cjs] The application may fail to start if these are required.\n');
} else {
console.log('[ecosystem.config.test.cjs] Critical environment variables are present.');
}
// --- Shared Environment Variables ---
const sharedEnv = {
DB_HOST: process.env.DB_HOST,
DB_USER: process.env.DB_USER,
DB_PASSWORD: process.env.DB_PASSWORD,
DB_NAME: process.env.DB_NAME,
REDIS_URL: process.env.REDIS_URL,
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
FRONTEND_URL: process.env.FRONTEND_URL,
JWT_SECRET: process.env.JWT_SECRET,
GEMINI_API_KEY: process.env.GEMINI_API_KEY,
GOOGLE_MAPS_API_KEY: process.env.GOOGLE_MAPS_API_KEY,
SMTP_HOST: process.env.SMTP_HOST,
SMTP_PORT: process.env.SMTP_PORT,
SMTP_SECURE: process.env.SMTP_SECURE,
SMTP_USER: process.env.SMTP_USER,
SMTP_PASS: process.env.SMTP_PASS,
SMTP_FROM_EMAIL: process.env.SMTP_FROM_EMAIL,
SENTRY_DSN: process.env.SENTRY_DSN,
SENTRY_ENVIRONMENT: process.env.SENTRY_ENVIRONMENT,
SENTRY_ENABLED: process.env.SENTRY_ENABLED,
};
module.exports = {
apps: [
// =========================================================================
// TEST APPS
// =========================================================================
{
// --- Test API Server ---
name: 'flyer-crawler-api-test',
script: './node_modules/.bin/tsx',
args: 'server.ts',
cwd: '/var/www/flyer-crawler-test.projectium.com',
max_memory_restart: '500M',
// Test environment: single instance (no cluster) to conserve resources
instances: 1,
exec_mode: 'fork',
kill_timeout: 5000,
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
max_restarts: 40,
exp_backoff_restart_delay: 100,
min_uptime: '10s',
env: {
NODE_ENV: 'staging',
PORT: 3002,
WORKER_LOCK_DURATION: '120000',
...sharedEnv,
},
},
{
// --- Test General Worker ---
name: 'flyer-crawler-worker-test',
script: './node_modules/.bin/tsx',
args: 'src/services/worker.ts',
cwd: '/var/www/flyer-crawler-test.projectium.com',
max_memory_restart: '1G',
kill_timeout: 10000,
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
max_restarts: 40,
exp_backoff_restart_delay: 100,
min_uptime: '10s',
env: {
NODE_ENV: 'staging',
...sharedEnv,
},
},
{
// --- Test Analytics Worker ---
name: 'flyer-crawler-analytics-worker-test',
script: './node_modules/.bin/tsx',
args: 'src/services/worker.ts',
cwd: '/var/www/flyer-crawler-test.projectium.com',
max_memory_restart: '1G',
kill_timeout: 10000,
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
max_restarts: 40,
exp_backoff_restart_delay: 100,
min_uptime: '10s',
env: {
NODE_ENV: 'staging',
...sharedEnv,
},
},
],
};