Compare commits

...

2 Commits

Author SHA1 Message Date
Gitea Actions
b9a0e5b82c ci: Bump version to 0.9.100 [skip ci] 2026-01-13 05:35:11 +05:00
bb7fe8dc2c logging work - almost there
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 15m28s
2026-01-12 16:34:18 -08:00
6 changed files with 142 additions and 82 deletions

View File

@@ -171,7 +171,7 @@ jobs:
else
echo "Version mismatch (Running: $RUNNING_VERSION -> Deployed: $NEW_VERSION) or app not running. Reloading PM2..."
fi
pm2 startOrReload ecosystem.config.cjs --env production --update-env && pm2 save
pm2 startOrReload ecosystem.config.cjs --update-env && pm2 save
echo "Production backend server reloaded successfully."
else
echo "Version $NEW_VERSION is already running. Skipping PM2 reload."

View File

@@ -476,10 +476,11 @@ jobs:
echo "Cleaning up errored or stopped PM2 processes..."
node -e "const exec = require('child_process').execSync; try { const list = JSON.parse(exec('pm2 jlist').toString()); list.forEach(p => { if (p.pm2_env.status === 'errored' || p.pm2_env.status === 'stopped') { console.log('Deleting ' + p.pm2_env.status + ' process: ' + p.name + ' (' + p.pm2_env.pm_id + ')'); try { exec('pm2 delete ' + p.pm2_env.pm_id); } catch(e) { console.error('Failed to delete ' + p.pm2_env.pm_id); } } }); } catch (e) { console.error('Error cleaning up processes:', e); }"
# Use `startOrReload` with the ecosystem file. This is the standard, idempotent way to deploy.
# It will START the process if it's not running, or RELOAD it if it is.
# Use `startOrReload` with the TEST ecosystem file. This starts test-specific processes
# (flyer-crawler-api-test, flyer-crawler-worker-test, flyer-crawler-analytics-worker-test)
# that run separately from production processes.
# We also add `&& pm2 save` to persist the process list across server reboots.
pm2 startOrReload ecosystem.config.cjs --env test --update-env && pm2 save
pm2 startOrReload ecosystem.config.test.cjs --update-env && pm2 save
echo "Test backend server reloaded successfully."
# After a successful deployment, update the schema hash in the database.

View File

@@ -2,6 +2,13 @@
// This file is the standard way to configure applications for PM2.
// It allows us to define all the settings for our application in one place.
// The .cjs extension is required because the project's package.json has "type": "module".
//
// IMPORTANT: This file defines SEPARATE apps for production and test environments.
// Production apps: flyer-crawler-api, flyer-crawler-worker, flyer-crawler-analytics-worker
// Test apps: flyer-crawler-api-test, flyer-crawler-worker-test, flyer-crawler-analytics-worker-test
//
// Use ecosystem.config.test.cjs for test deployments (contains only test apps).
// Use this file (ecosystem.config.cjs) for production deployments.
// --- Environment Variable Validation ---
const requiredSecrets = ['DB_HOST', 'JWT_SECRET', 'GEMINI_API_KEY'];
@@ -35,125 +42,67 @@ const sharedEnv = {
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: [
// =========================================================================
// PRODUCTION APPS
// =========================================================================
{
// --- API Server ---
// --- Production API Server ---
name: 'flyer-crawler-api',
// Note: The process names below are referenced in .gitea/workflows/ for status checks.
script: './node_modules/.bin/tsx',
args: 'server.ts',
cwd: '/var/www/flyer-crawler.projectium.com',
max_memory_restart: '500M',
// Production Optimization: Run in cluster mode to utilize all CPU cores
instances: 'max',
exec_mode: 'cluster',
kill_timeout: 5000, // Allow 5s for graceful shutdown of API requests
kill_timeout: 5000,
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
// Restart Logic
max_restarts: 40,
exp_backoff_restart_delay: 100,
min_uptime: '10s',
// Production Environment Settings
env_production: {
env: {
NODE_ENV: 'production',
name: 'flyer-crawler-api',
cwd: '/var/www/flyer-crawler.projectium.com',
WORKER_LOCK_DURATION: '120000',
...sharedEnv,
},
// Test Environment Settings
env_test: {
NODE_ENV: 'test',
name: 'flyer-crawler-api-test',
cwd: '/var/www/flyer-crawler-test.projectium.com',
WORKER_LOCK_DURATION: '120000',
...sharedEnv,
},
// Development Environment Settings
env_development: {
NODE_ENV: 'development',
name: 'flyer-crawler-api-dev',
watch: true,
ignore_watch: ['node_modules', 'logs', '*.log', 'flyer-images', '.git'],
WORKER_LOCK_DURATION: '120000',
...sharedEnv,
},
},
{
// --- General Worker ---
// --- Production General Worker ---
name: 'flyer-crawler-worker',
script: './node_modules/.bin/tsx',
args: 'src/services/worker.ts',
cwd: '/var/www/flyer-crawler.projectium.com',
max_memory_restart: '1G',
kill_timeout: 10000, // Workers may need more time to complete a job
kill_timeout: 10000,
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
// Restart Logic
max_restarts: 40,
exp_backoff_restart_delay: 100,
min_uptime: '10s',
// Production Environment Settings
env_production: {
env: {
NODE_ENV: 'production',
name: 'flyer-crawler-worker',
cwd: '/var/www/flyer-crawler.projectium.com',
...sharedEnv,
},
// Test Environment Settings
env_test: {
NODE_ENV: 'test',
name: 'flyer-crawler-worker-test',
cwd: '/var/www/flyer-crawler-test.projectium.com',
...sharedEnv,
},
// Development Environment Settings
env_development: {
NODE_ENV: 'development',
name: 'flyer-crawler-worker-dev',
watch: true,
ignore_watch: ['node_modules', 'logs', '*.log', 'flyer-images', '.git'],
...sharedEnv,
},
},
{
// --- Analytics Worker ---
// --- Production Analytics Worker ---
name: 'flyer-crawler-analytics-worker',
script: './node_modules/.bin/tsx',
args: 'src/services/worker.ts',
cwd: '/var/www/flyer-crawler.projectium.com',
max_memory_restart: '1G',
kill_timeout: 10000,
log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
// Restart Logic
max_restarts: 40,
exp_backoff_restart_delay: 100,
min_uptime: '10s',
// Production Environment Settings
env_production: {
env: {
NODE_ENV: 'production',
name: 'flyer-crawler-analytics-worker',
cwd: '/var/www/flyer-crawler.projectium.com',
...sharedEnv,
},
// Test Environment Settings
env_test: {
NODE_ENV: 'test',
name: 'flyer-crawler-analytics-worker-test',
cwd: '/var/www/flyer-crawler-test.projectium.com',
...sharedEnv,
},
// Development Environment Settings
env_development: {
NODE_ENV: 'development',
name: 'flyer-crawler-analytics-worker-dev',
watch: true,
ignore_watch: ['node_modules', 'logs', '*.log', 'flyer-images', '.git'],
...sharedEnv,
},
},

110
ecosystem.config.test.cjs Normal file
View File

@@ -0,0 +1,110 @@
// ecosystem.config.test.cjs
// PM2 configuration for the TEST environment only.
// 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='test' (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
// --- Environment Variable Validation ---
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 crash if these are required for startup.\n');
process.exit(1);
} 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: 'test',
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: 'test',
...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: 'test',
...sharedEnv,
},
},
],
};

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "flyer-crawler",
"version": "0.9.99",
"version": "0.9.100",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "flyer-crawler",
"version": "0.9.99",
"version": "0.9.100",
"dependencies": {
"@bull-board/api": "^6.14.2",
"@bull-board/express": "^6.14.2",

View File

@@ -1,7 +1,7 @@
{
"name": "flyer-crawler",
"private": true,
"version": "0.9.99",
"version": "0.9.100",
"type": "module",
"scripts": {
"dev": "concurrently \"npm:start:dev\" \"vite\"",