import assert from "assert";
|
|
import { mkdir } from "fs/promises";
|
|
import { Socket } from "net";
|
|
import { dirname } from "path";
|
|
import { Microservice } from "@autoplex/microservice";
|
|
import { IIpcRequest, IPC } from "./schema";
|
|
import AbstractIpcService from "./AbstractIpcService";
|
|
|
|
type IpcServer = IPC["server"];
|
|
|
|
export abstract class IpcServerService<M extends Microservice = Microservice> extends AbstractIpcService<M>
|
|
{
|
|
/**
|
|
* The IPC server instance
|
|
*/
|
|
private __server!: IpcServer|null;
|
|
|
|
// Service Implementation ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* Install the event handlers for receiving on the IPC socket
|
|
*
|
|
* Example: this.addMessageHandler("some_event", this.onSomeEvent)
|
|
*/
|
|
protected installMessageHandlers() {
|
|
// no-op
|
|
}
|
|
|
|
// Service Management --------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Boot the IPC service
|
|
*/
|
|
public bootIpc(ipc: IPC) {
|
|
return new Promise<void>(async (resolve) => {
|
|
// Create the socket directory if it doesn't exist
|
|
await mkdir(dirname(this.SOCKET_PATH), { recursive: true });
|
|
// Serve the IPC server
|
|
ipc.serve(this.SOCKET_PATH, () => {
|
|
this.__server = ipc.server;
|
|
this.installMessageHandlers();
|
|
resolve();
|
|
});
|
|
ipc.server.start();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Add a message/request handler for the server
|
|
*/
|
|
protected addMessageHandler(method: string, handle: (...args: any[]) => Promise<any>) {
|
|
assert(this.__server !== null, "Attempted to add events to null server");
|
|
this.__server.on(method, async (request: IIpcRequest, socket: Socket) => {
|
|
let handlerPromise = handle.apply(this, [request.data]);
|
|
if (request.id === null) {
|
|
handlerPromise.catch(error => this.log("Error:", method, error, request));
|
|
return;
|
|
}
|
|
const responseMethod = `method_response_${request.id}`;
|
|
try {
|
|
this.__server!.emit(socket, responseMethod, { data: await handlerPromise });
|
|
} catch(error) {
|
|
this.log(this.log("Error:", method, error, request));
|
|
this.__server!.emit(socket, responseMethod, { error });
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Shutdown the IPC service
|
|
*/
|
|
public async shutdownIpc(ipc: IPC|null) {
|
|
this.__server?.stop();
|
|
this.__server = null;
|
|
for (let socket of <Socket[]>Object.values(ipc?.of ?? [])) {
|
|
socket.destroy();
|
|
}
|
|
}
|
|
|
|
// Methods -------------------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Broadcast a message to all connected clients
|
|
*/
|
|
public broadcast(method: string, data?: any) {
|
|
if (this.__server === null) {
|
|
return;
|
|
}
|
|
this.__server.emit(method, data);
|
|
}
|
|
}
|