@underpostnet/underpost
Version:
Underpost Platform — end-to-end CI/CD and application-delivery toolchain CLI. Covers bare metal, Kubernetes, K3s, kubeadm, LXD, container/image orchestration, secrets, databases, cron jobs, monitoring, SSH, runners, PWA + Workbox delivery, and release orc
117 lines (103 loc) • 3.58 kB
JavaScript
/**
* Mailer WebSocket channel — manages user↔socket bidirectional mapping
* for targeted real-time pushes (e.g. email confirmation notifications).
* @module ws/core/channels/core.ws.mailer
*/
import { IoChannel } from '../../IoInterface.js';
/**
* @class CoreWsMailerChannel
* @classdesc Manages the mailer WebSocket channel with O(1) user↔socket lookup.
* Handles register/unregister messages and cleanup on disconnect.
*/
class CoreWsMailerChannel {
/** @type {Object.<string, Object.<string, { model: { user: Object } }>>} Socket data keyed by `[hostKeyContext][socketId]`. */
static #data = {};
/** @type {Object.<string, Object.<string, string>>} Reverse index: `[hostKeyContext][userId]` → socketId. */
static #userIndex = {};
/** @type {IoChannel} */
static #io = new IoChannel({
channel: 'mailer',
controller(socket, client, payload, hostKeyContext) {
switch (payload.status) {
case 'register-user':
CoreWsMailerChannel.setUser(hostKeyContext, socket.id, payload.user);
break;
case 'unregister-user':
CoreWsMailerChannel.removeSocket(hostKeyContext, socket.id);
break;
default:
break;
}
},
disconnect(socket, client, reason, hostKeyContext) {
CoreWsMailerChannel.removeSocket(hostKeyContext, socket.id);
},
});
/** @returns {Object.<string, import('socket.io').Socket>} Connected sockets map. */
static get client() {
return this.#io.client;
}
/** @returns {string} Channel name. */
static get channel() {
return this.#io.channel;
}
/**
* Initializes state for a server instance.
* @param {string} hostKeyContext - Unique server context ID (`${host}${path}`).
*/
static init(hostKeyContext) {
this.#data[hostKeyContext] = {};
this.#userIndex[hostKeyContext] = {};
}
/**
* Registers a socket connection.
* @param {import('socket.io').Socket} socket
* @param {string} hostKeyContext
*/
static connection(socket, hostKeyContext) {
return this.#io.connection(socket, hostKeyContext);
}
/**
* Handles socket disconnection.
* @param {import('socket.io').Socket} socket
* @param {string} reason
* @param {string} hostKeyContext
*/
static disconnect(socket, reason, hostKeyContext) {
return this.#io.disconnect(socket, reason, hostKeyContext);
}
/**
* Registers a user↔socket mapping.
* @param {string} hostKeyContext
* @param {string} socketId
* @param {Object} user - User data with `_id` property.
*/
static setUser(hostKeyContext, socketId, user) {
this.#data[hostKeyContext][socketId] = { model: { user } };
if (user?._id) {
this.#userIndex[hostKeyContext][user._id.toString()] = socketId;
}
}
/**
* Removes a socket entry and its reverse user index.
* @param {string} hostKeyContext
* @param {string} socketId
*/
static removeSocket(hostKeyContext, socketId) {
const entry = this.#data[hostKeyContext]?.[socketId];
if (entry?.model?.user?._id) {
delete this.#userIndex[hostKeyContext][entry.model.user._id.toString()];
}
delete this.#data[hostKeyContext]?.[socketId];
}
/**
* Finds the socket ID for a user (O(1) reverse index lookup).
* @param {string} hostKeyContext
* @param {string} userId - The user `_id`.
* @returns {string|undefined} Socket ID, or `undefined` if not connected.
*/
static getUserWsId(hostKeyContext, userId) {
return this.#userIndex[hostKeyContext]?.[userId];
}
}
export { CoreWsMailerChannel };