File size: 3,051 Bytes
4327358
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import { sleep, waitUntil } from '@waha/utils/promiseTimeout';
import { spawn } from 'child_process';
import { Logger } from 'pino';

export class GowsSubprocess {
  private checkIntervalMs: number = 100;
  private readyDelayMs: number = 1_000;
  private readyText = 'gRPC server started!';

  private child: any;
  private ready: boolean = false;

  constructor(
    private logger: Logger,
    readonly path: string,
    readonly socket: string,
    readonly pprof: boolean = false,
  ) {}

  start(onExit: (code: number) => void) {
    this.logger.info('Starting GOWS subprocess...');
    this.logger.debug(`GOWS path '${this.path}', socket: '${this.socket}'...`);

    const args = ['--socket', this.socket];
    if (this.pprof) {
      this.logger.info('Debug mode enabled, adding pprof flags');
      args.push('--pprof');
      args.push('--pprof-port=6060');
      args.push('--pprof-host=0.0.0.0');
    }

    this.child = spawn(this.path, args, {
      detached: true,
    });
    this.logger.debug(`GOWS started with PID: ${this.child.pid}`);
    this.child.on('close', async (code, singal) => {
      const msg = code
        ? `GOWS subprocess closed with code ${code}`
        : `GOWS subprocess closed by signal ${singal}`;
      this.logger.debug(msg);
      onExit(code);
    });
    this.child.on('error', (err) => {
      this.logger.error(`GOWS subprocess error: ${err}`);
    });

    this.child.stderr.setEncoding('utf8');
    this.child.stderr.on('data', (data) => {
      this.logger.error(data);
    });

    this.child.stdout.setEncoding('utf8');
    this.child.stdout.on('data', async (data) => {
      // remove empty line at the end, split by \n
      const lines = data.trim().split('\n');
      lines.forEach((line) => this.log(line));
    });
    this.listenReady();
  }

  listenReady() {
    this.child.stdout.on('data', async (data) => {
      if (this.ready) {
        return;
      }
      if (!data.includes(this.readyText)) {
        return;
      }
      await sleep(this.readyDelayMs);
      this.ready = true;
      this.logger.info('GOWS is ready');
    });
  }

  async waitWhenReady(timeout: number) {
    const started = await waitUntil(
      async () => this.ready,
      this.checkIntervalMs,
      timeout,
    );
    if (!started) {
      const msg = 'GOWS did not start after 10 seconds';
      this.logger.error(msg);
      throw new Error(msg);
    }
  }

  async stop() {
    this.logger.info('Stopping GOWS subprocess...');
    this.child?.kill('SIGTERM');
    this.logger.info('GOWS subprocess stopped');
  }

  private log(msg) {
    if (msg.startsWith('ERROR | ')) {
      this.logger.error(msg.slice(8));
    } else if (msg.startsWith('WARN | ')) {
      this.logger.warn(msg.slice(7));
    } else if (msg.startsWith('INFO | ')) {
      this.logger.info(msg.slice(7));
    } else if (msg.startsWith('DEBUG | ')) {
      this.logger.debug(msg.slice(8));
    } else if (msg.startsWith('TRACE | ')) {
      this.logger.trace(msg.slice(8));
    } else {
      this.logger.info(msg);
    }
  }
}