All files / src/services systemService.ts

75.32% Statements 58/77
100% Branches 15/15
80% Functions 4/5
84.21% Lines 48/57

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 582x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 28x 2x 2x 2x 5x 5x 2x 2x 2x 4x 1x     3x 3x     5x         2x 2x 2x 1x 1x   2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 23x 24x  
// 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);