// src/services/systemService.ts import { exec as nodeExec } from 'child_process'; import { promisify } from 'util'; import { logger } from './logger.server'; // Define a type for the exec function for better type safety and testability. // It matches the signature of a promisified child_process.exec. export type ExecAsync = (command: string) => Promise<{ stdout: string; stderr: string }>; export class SystemService { private execAsync: ExecAsync; constructor(execAsync: ExecAsync) { this.execAsync = execAsync; } async getPm2Status(): Promise<{ success: boolean; message: string }> { try { const { stdout, stderr } = await this.execAsync('pm2 describe flyer-crawler-api'); // If the command runs but produces output on stderr, treat it as an error. // This handles cases where pm2 might issue warnings but still exit 0. if (stderr) { throw new Error(`PM2 command produced an error: ${stderr}`); } const isOnline = /│\s*status\s*│\s*online\s*│/m.test(stdout); const message = isOnline ? 'Application is online and running under PM2.' : 'Application process exists but is not online.'; return { success: isOnline, message }; } catch (error: unknown) { // If the command fails (non-zero exit code), check if it's because the process doesn't exist. // This is a normal "not found" case, not a system error. // The error message can be in stdout or stderr depending on the pm2 version. const execError = error as { stdout?: string; stderr?: string; message?: string }; const output = execError.stdout || execError.stderr || ''; if (output.includes("doesn't exist")) { logger.warn('[SystemService] PM2 process "flyer-crawler-api" not found.'); return { success: false, message: 'Application process is not running under PM2.', }; } // For any other error, log it and re-throw to be handled as a 500. logger.error( { error: execError.stderr || execError.message }, '[SystemService] Error executing pm2 describe:', ); throw error; } } } // Instantiate the service with the real dependency for the application const realExecAsync = promisify(nodeExec); export const systemService = new SystemService(realExecAsync);