diff --git a/README.md b/README.md index 99f32f2..53c0cd5 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ PGPASSWORD='postgres' psql -U postgres -c "CREATE DATABASE app_39215 OWNER app_3 ```bash cd backend yarn install -export $(cat .env | xargs) && NODE_ENV=production yarn start +npm run start-dev ``` Backend runs on **http://localhost:8080** diff --git a/backend/README.md b/backend/README.md index 82cae85..f7744ef 100644 --- a/backend/README.md +++ b/backend/README.md @@ -28,7 +28,7 @@ yarn install yarn db:create # Start server (runs migrations, seeds, and watches for changes) -export $(cat .env | xargs) && NODE_ENV=production yarn start +npm run start-dev ``` The server runs on **port 8080** by default. diff --git a/backend/package.json b/backend/package.json index 9535871..f674d05 100644 --- a/backend/package.json +++ b/backend/package.json @@ -3,6 +3,7 @@ "description": "Tour Builder Platform - template backend", "scripts": { "start": "npm run db:migrate && npm run db:seed && npm run watch", + "start-dev": "cross-env NODE_ENV=production LOG_PRETTY=true DOTENV_CONFIG_PATH=.env NODE_OPTIONS=\"-r dotenv/config\" npm run start", "lint": "eslint . --ext .js", "db:migrate": "sequelize-cli db:migrate", "db:migrate:undo": "sequelize-cli db:migrate:undo", diff --git a/backend/src/db/models/index.js b/backend/src/db/models/index.js index e326416..116aeac 100644 --- a/backend/src/db/models/index.js +++ b/backend/src/db/models/index.js @@ -9,7 +9,6 @@ const config = require('../db.config')[env]; const db = {}; let sequelize; -console.log(env); if (config.use_env_variable) { sequelize = new Sequelize(process.env[config.use_env_variable], config); } else { diff --git a/backend/src/middlewares/check-permissions.js b/backend/src/middlewares/check-permissions.js index 02345f8..54f2d5e 100644 --- a/backend/src/middlewares/check-permissions.js +++ b/backend/src/middlewares/check-permissions.js @@ -1,5 +1,6 @@ const ValidationError = require('../services/notifications/errors/validation'); const RolesDBApi = require('../db/api/roles'); +const { logger } = require('../utils/logger'); // Cache for the 'Public' role object let publicRoleCache = null; @@ -11,17 +12,21 @@ async function fetchAndCachePublicRole() { publicRoleCache = await RolesDBApi.findBy({ name: 'Public' }); if (!publicRoleCache) { - console.error( - "WARNING: Role 'Public' not found in database during middleware startup. Check your migrations.", + logger.warn( + { role: 'Public' }, + 'Role not found during permissions middleware startup', ); // The system might not function correctly without this role. May need to throw an error or use a fallback stub. } else { - console.log("'Public' role successfully loaded and cached."); + logger.info( + { role: 'Public', roleId: publicRoleCache.id }, + 'Role loaded and cached', + ); } } catch (error) { - console.error( - "Error fetching 'Public' role during middleware startup:", - error, + logger.error( + { err: error, role: 'Public' }, + 'Error fetching role during permissions middleware startup', ); // Handle the error during startup fetch throw error; // Important to know if the app can proceed without the Public role @@ -32,9 +37,9 @@ async function fetchAndCachePublicRole() { // This should happen during application startup when routes are being configured. fetchAndCachePublicRole().catch((error) => { // Handle the case where the fetchAndCachePublicRole promise is rejected - console.error( - 'Critical error during permissions middleware initialization:', - error, + logger.error( + { err: error }, + 'Critical error during permissions middleware initialization', ); }); @@ -81,8 +86,10 @@ function checkPermissions(permission) { if (!publicRoleCache) { // If the cache is unexpectedly empty (e.g., startup error caught), // we can try fetching the role again synchronously (less ideal) or just deny access. - console.error( - 'Public role cache is empty. Attempting synchronous fetch...', + const log = req.log || logger; + log.warn( + { role: 'Public' }, + 'Role cache is empty, attempting synchronous fetch', ); // Less efficient fallback option: effectiveRole = await RolesDBApi.findBy({ name: 'Public' }); // Could be slow @@ -117,9 +124,10 @@ function checkPermissions(permission) { } else if (Array.isArray(effectiveRole.permissions)) { rolePermissions = effectiveRole.permissions; // Or take from property if permissions are pre-loaded } else { - console.error( - 'Role object lacks getPermissions() method or permissions property:', - effectiveRole, + const log = req.log || logger; + log.error( + { roleId: effectiveRole.id, roleName: effectiveRole.name }, + 'Role object lacks getPermissions method or permissions property', ); return next( new Error('Internal Server Error: Invalid role object format.'), @@ -140,7 +148,8 @@ function checkPermissions(permission) { } } catch (e) { // Handle errors during role or permission fetching - console.error('Error during permission check:', e); + const log = req.log || logger; + log.error({ err: e, permission }, 'Error during permission check'); next(e); // Pass the error to the next middleware } }; diff --git a/backend/src/utils/logger.js b/backend/src/utils/logger.js index 7380316..e77047c 100644 --- a/backend/src/utils/logger.js +++ b/backend/src/utils/logger.js @@ -1,12 +1,21 @@ const pino = require('pino'); const crypto = require('crypto'); -const isDevelopment = process.env.NODE_ENV === 'development'; +const shouldPrettyPrint = + process.env.LOG_PRETTY === 'true' || process.env.NODE_ENV === 'development'; const logger = pino({ level: process.env.LOG_LEVEL || 'info', - transport: isDevelopment - ? { target: 'pino-pretty', options: { colorize: true } } + transport: shouldPrettyPrint + ? { + target: 'pino-pretty', + options: { + colorize: true, + ignore: 'pid,hostname', + messageFormat: '{service} | {msg}', + translateTime: 'SYS:yyyy-mm-dd HH:MM:ss.l', + }, + } : undefined, base: { service: 'tour-builder-api', @@ -27,7 +36,7 @@ function requestLogger(req, res, next) { method: req.method, url: req.originalUrl || req.url, status: res.statusCode, - duration, + durationMs: duration, userAgent: req.headers['user-agent'], }; diff --git a/backend/watcher.js b/backend/watcher.js index 9e5e5bc..143b743 100644 --- a/backend/watcher.js +++ b/backend/watcher.js @@ -1,21 +1,38 @@ const chokidar = require('chokidar'); const { exec } = require('child_process'); const nodemon = require('nodemon'); +const { logger } = require('./src/utils/logger'); const nodeEnv = process.env.NODE_ENV || 'dev_stage'; const childEnv = { ...process.env, NODE_ENV: nodeEnv }; +const log = logger.child({ module: 'watcher' }); + +function logCommandResult(error, stdout, stderr, successMessage) { + const output = stdout && stdout.trim(); + const errorOutput = stderr && stderr.trim(); + + if (output) { + log.info({ output }, successMessage); + } + + if (error) { + log.error( + { err: error, stderr: errorOutput }, + 'Watched database command failed', + ); + } else if (errorOutput) { + log.warn({ stderr: errorOutput }, 'Watched database command wrote stderr'); + } +} const migrationsWatcher = chokidar.watch('./src/db/migrations', { persistent: true, ignoreInitial: true }); migrationsWatcher.on('add', (filePath) => { - console.log(`[DEBUG] New migration file: ${filePath}`); + log.info({ filePath }, 'New migration file detected'); exec('npm run db:migrate', { env: childEnv }, (error, stdout, stderr) => { - console.log(stdout); - if (error) { - console.error(stderr); - } + logCommandResult(error, stdout, stderr, 'Migration command completed'); }); }); @@ -24,12 +41,9 @@ const seedersWatcher = chokidar.watch('./src/db/seeders', { ignoreInitial: true }); seedersWatcher.on('add', (filePath) => { - console.log(`[DEBUG] New seed file: ${filePath}`); + log.info({ filePath }, 'New seed file detected'); exec('npm run db:seed', { env: childEnv }, (error, stdout, stderr) => { - console.log(stdout); - if (error) { - console.error(stderr); - } + logCommandResult(error, stdout, stderr, 'Seeder command completed'); }); }); @@ -41,9 +55,13 @@ nodemon({ }); nodemon.on('start', () => { - console.log('Nodemon started'); + log.info({ nodeEnv }, 'Nodemon started'); }); nodemon.on('restart', (files) => { - console.log('Nodemon restarted due changes in:', files); + log.info({ files }, 'Nodemon restarted due to file changes'); +}); + +nodemon.on('crash', () => { + log.error('Nodemon app crashed'); });