fix(striker-ui-api): enable auto restart, handle anvil-access-module events in access module

main
Tsu-ba-me 1 year ago
parent b7629e4c2b
commit 36a2e7489a
  1. 87
      striker-ui-api/src/lib/accessModule.ts
  2. 1
      striker-ui-api/src/types/AccessModule.d.ts

@ -2,7 +2,13 @@ import { ChildProcess, spawn, SpawnOptions } from 'child_process';
import EventEmitter from 'events'; import EventEmitter from 'events';
import { readFileSync } from 'fs'; import { readFileSync } from 'fs';
import { SERVER_PATHS, PGID, PUID, DEFAULT_JOB_PROGRESS } from './consts'; import {
SERVER_PATHS,
PGID,
PUID,
DEFAULT_JOB_PROGRESS,
REP_UUID,
} from './consts';
import { formatSql } from './formatSql'; import { formatSql } from './formatSql';
import { import {
@ -13,9 +19,27 @@ import {
uuid, uuid,
} from './shell'; } from './shell';
/**
* Notes:
* * This daemon's lifecycle events should follow the naming from systemd.
*/
class Access extends EventEmitter { class Access extends EventEmitter {
private ps: ChildProcess; private ps: ChildProcess;
private readonly mapToExternalEventHandler: Record<
string,
(args: { options: AccessStartOptions; ps: ChildProcess }) => void
> = {
connected: ({ options, ps }) => {
shvar(
options,
`Successfully started anvil-access-module daemon (pid=${ps.pid}): `,
);
this.emit('active', ps.pid);
},
};
constructor({ constructor({
eventEmitterOptions = {}, eventEmitterOptions = {},
spawnOptions = {}, spawnOptions = {},
@ -29,14 +53,23 @@ class Access extends EventEmitter {
} }
private start({ private start({
args = [], args = ['--emit-events'],
gid = PGID, gid = PGID,
restartInterval = 10000,
stdio = 'pipe', stdio = 'pipe',
timeout = 10000, timeout = 10000,
uid = PUID, uid = PUID,
...restSpawnOptions ...restSpawnOptions
}: AccessStartOptions = {}) { }: AccessStartOptions = {}) {
const options = { args, gid, stdio, timeout, uid, ...restSpawnOptions }; const options = {
args,
gid,
restartInterval,
stdio,
timeout,
uid,
...restSpawnOptions,
};
shvar(options, `Starting anvil-access-module daemon with: `); shvar(options, `Starting anvil-access-module daemon with: `);
@ -48,45 +81,60 @@ class Access extends EventEmitter {
...restSpawnOptions, ...restSpawnOptions,
}); });
ps.on('spawn', () => { ps.once('error', (error) => {
shvar(
options,
`Successfully started anvil-access-module daemon (pid=${ps.pid}): `,
);
});
ps.on('error', (error) => {
sherr( sherr(
`anvil-access-module daemon (pid=${ps.pid}) error: ${error.message}`, `anvil-access-module daemon (pid=${ps.pid}) error: ${error.message}`,
error, error,
); );
}); });
ps.on('close', (code, signal) => { ps.once('close', (code, signal) => {
shvar( shvar(
{ code, options, signal }, { code, options, signal },
`anvil-access-module daemon (pid=${ps.pid}) closed: `, `anvil-access-module daemon (pid=${ps.pid}) closed: `,
); );
});
let stdout = ''; this.emit('inactive', ps.pid);
shout(`Waiting ${restartInterval} before restarting.`);
setTimeout(() => {
this.ps = this.start(options);
}, restartInterval);
});
ps.stderr?.setEncoding('utf-8').on('data', (chunk: string) => { ps.stderr?.setEncoding('utf-8').on('data', (chunk: string) => {
sherr(`anvil-access-module daemon stderr: ${chunk}`); sherr(`anvil-access-module daemon stderr: ${chunk}`);
}); });
let stdout = '';
ps.stdout?.setEncoding('utf-8').on('data', (chunk: string) => { ps.stdout?.setEncoding('utf-8').on('data', (chunk: string) => {
stdout += chunk; const eventless = chunk.replace(/(\n)?event=([^\n]*)\n/g, (...parts) => {
shvar(parts, 'In replacer, args: ');
const { 1: n = '', 2: event } = parts;
this.mapToExternalEventHandler[event]?.call(null, { options, ps });
return n;
});
stdout += eventless;
let nindex: number = stdout.indexOf('\n'); let nindex: number = stdout.indexOf('\n');
// 1. ~a is the shorthand for -(a + 1) // 1. ~a is the shorthand for -(a + 1)
// 2. negatives are evaluated to true // 2. negative is evaluated to true
while (~nindex) { while (~nindex) {
const scriptId = stdout.substring(0, 36); const scriptId = stdout.substring(0, 36);
const output = stdout.substring(36, nindex); const output = stdout.substring(36, nindex);
if (scriptId) this.emit(scriptId, output); if (REP_UUID.test(scriptId)) {
this.emit(scriptId, output);
} else {
shout(`Access stdout: ${stdout}`);
}
stdout = stdout.substring(nindex + 1); stdout = stdout.substring(nindex + 1);
nindex = stdout.indexOf('\n'); nindex = stdout.indexOf('\n');
@ -103,7 +151,9 @@ class Access extends EventEmitter {
} }
private restart(options?: AccessStartOptions) { private restart(options?: AccessStartOptions) {
this.ps.once('close', () => this.start(options)); this.ps.once('close', () => {
this.ps = this.start(options);
});
this.stop(); this.stop();
} }
@ -420,6 +470,7 @@ const getVncinfo = async (serverUuid: string): Promise<ServerDetailVncInfo> => {
}; };
export { export {
access,
insertOrUpdateJob as job, insertOrUpdateJob as job,
insertOrUpdateUser, insertOrUpdateUser,
insertOrUpdateVariable as variable, insertOrUpdateVariable as variable,

@ -1,5 +1,6 @@
type AccessStartOptions = { type AccessStartOptions = {
args?: readonly string[]; args?: readonly string[];
restartInterval?: number;
} & import('child_process').SpawnOptions; } & import('child_process').SpawnOptions;
type SubroutineCommonParams = { type SubroutineCommonParams = {

Loading…
Cancel
Save