diff --git a/src/host/hostmanager.js b/src/host/hostmanager.js index d2220fb..8e5420b 100644 --- a/src/host/hostmanager.js +++ b/src/host/hostmanager.js @@ -1,5 +1,210 @@ +import log4js from "log4js"; +import { loadConfig, saveConfig } from "../config.js"; +import { getDeviceInfo } from "../utils.js"; +import { sendIPC } from "../desktop/notify.js"; + +const config = loadConfig(); + +const logger = log4js.getLogger("Host Manager"); +logger.level = config.logLevel; + export class HostManager { constructor(socketClient) { this.socketClient = socketClient; + this.host = null; + this.id = null; + } + + isOtpRequired() { + return ( + config.host?.id == undefined || + config.host?.id == "" || + config.host?.authCode == undefined || + config.host?.authCode == "" + ); + } + + subscribeToObjectEvents() { + const { id } = this; + this.socketClient.subscribeToObjectEvent({ + objectType: "host", + _id: id, + eventType: "childUpdate", + }); + this.socketClient.subscribeToObjectEvent({ + objectType: "host", + _id: id, + eventType: "childDelete", + }); + this.socketClient.subscribeToObjectEvent({ + objectType: "host", + _id: id, + eventType: "childNew", + }); + } + + unsubscribeFromObjectEvents() { + if (!this.id) { + return; + } + const { id } = this; + this.socketClient.unsubscribeFromObjectEvent({ + objectType: "host", + _id: id, + eventType: "childUpdate", + }); + this.socketClient.unsubscribeFromObjectEvent({ + objectType: "host", + _id: id, + eventType: "childDelete", + }); + this.socketClient.unsubscribeFromObjectEvent({ + objectType: "host", + _id: id, + eventType: "childNew", + }); + } + + async authenticateWithOtp(otp, options = {}) { + const { retryOnFailure = false, logs = true } = options; + + if (!otp) { + return { valid: false, error: "OTP is required" }; + } + + await this.socketClient.waitForConnection(); + return this.authenticate({ otp }, { retryOnFailure, logs }); + } + + async authenticate(authenticationData, options = {}) { + const { logs = true } = options; + + await this.socketClient.waitForConnection(); + + return new Promise((resolve, reject) => { + const { socket } = this.socketClient; + if (!socket?.connected) { + reject(new Error("Socket client is not connected")); + return; + } + + if (logs) { + logger.debug("Host authenticating..."); + } + socket.emit( + "authenticate", + authenticationData, + async (verifyResult) => { + if (verifyResult.valid == false) { + this.socketClient.authenticated = false; + sendIPC("setAuthenticated", false); + if (logs) { + logger.error("Host not authenticated:", verifyResult.error); + } + + resolve({ valid: false, error: verifyResult.error }); + return; + } + + if (logs) { + logger.info("Host authenticated."); + } + await this.onAuthenticated(verifyResult.host); + resolve({ valid: true, host: this.host }); + }, + ); + }); + } + + async onAuthenticated(hostData) { + this.socketClient.authenticated = true; + sendIPC("setAuthenticated", true); + sendIPC("setLoading", false); + this.socketClient.loading = false; + + if (this.host) { + sendIPC("setHost", { + ...this.host, + online: false, + state: { type: "offline" }, + }); + } + + this.host = { + ...hostData, + state: { type: "online" }, + online: true, + }; + sendIPC("setHost", this.host); + this.id = this.host._id; + + config.host = { id: this.id, authCode: this.host.authCode }; + await saveConfig(config); + + this.sendDeviceInfo(); + this.subscribeToObjectEvents(); + this.socketClient.subscribeToObjectUpdates({ + objectType: "host", + _id: this.id, + }); + this.socketClient.documentPrinterManager.reloadDocumentPrinters(); + this.socketClient.printerManager.reloadPrinters(); + await this.socketClient.fileManager.updateFiles(); + } + + onConnect() { + if (this.isOtpRequired()) { + logger.info("An OTP code is required to setup this host."); + this.socketClient.authenticated = false; + sendIPC("setAuthenticated", false); + return; + } + this.authenticate({ + id: config.host.id, + authCode: config.host.authCode, + }); + } + + handleAction(action, callback) { + switch (action.type) { + case "reloadPrinters": + this.socketClient.printerManager.updatePrinters().catch((error) => { + logger.error("Failed to reload printers:", error); + }); + return; + } + callback({ success: true }); + } + + async handleUpdate(data) { + if (data._id != this.id) { + return; + } + + logger.debug("Handling host update for id:", data._id); + const { online, state } = this.host; + this.host = { ...this.host, ...data.object, online, state }; + sendIPC("setHost", this.host); + + if (data.object?.authCode) { + config.host = { id: this.id, authCode: data.object.authCode }; + await saveConfig(config); + } + } + + async handleChildEvent(data) { + if (data.parentType == "printer") { + this.socketClient.printerManager.reloadPrinters(); + } else if (data.parentType == "documentPrinter") { + this.socketClient.documentPrinterManager.reloadDocumentPrinters(); + } + } + + sendDeviceInfo() { + logger.debug("Sending device info..."); + const deviceInfo = getDeviceInfo(); + this.socketClient.socket.emit("updateHost", { + host: { deviceInfo: deviceInfo }, + }); } } diff --git a/src/socket/socketclient.js b/src/socket/socketclient.js index 86cd52b..d78e6df 100644 --- a/src/socket/socketclient.js +++ b/src/socket/socketclient.js @@ -3,8 +3,7 @@ import { WebSocketScanner } from "../network/websocketScanner.js"; import { io } from "socket.io-client"; import axios from "axios"; // Load configuration -import { loadConfig, saveConfig } from "../config.js"; -import { getDeviceInfo } from "../utils.js"; +import { loadConfig } from "../config.js"; import { sendIPC } from "../desktop/notify.js"; import { PrinterManager } from "../printer/printermanager.js"; import { HostManager } from "../host/hostmanager.js"; @@ -23,8 +22,6 @@ export class SocketClient { this.authenticated = false; this.connected = false; this.loading = false; - this.host = null; - this.id = null; this.reconnectTimeout = null; this.hostManager = new HostManager(this); this.fileManager = new FileManager(this); @@ -38,6 +35,14 @@ export class SocketClient { sendIPC("setLoading", false); } + get id() { + return this.hostManager.id; + } + + get host() { + return this.hostManager.host; + } + setupSocketEventHandlers() { this.socket.on("connect", this.handleConnect.bind(this)); this.socket.on("connect_error", this.handleError.bind(this)); @@ -61,42 +66,6 @@ export class SocketClient { }, 3000); } - subscribeToObjectEvents() { - this.subscribeToObjectEvent({ - objectType: "host", - _id: this.id, - eventType: "childUpdate", - }); - this.subscribeToObjectEvent({ - objectType: "host", - _id: this.id, - eventType: "childDelete", - }); - this.subscribeToObjectEvent({ - objectType: "host", - _id: this.id, - eventType: "childNew", - }); - } - - unsubscribeFromObjectEvents() { - this.unsubscribeFromObjectEvent({ - objectType: "host", - _id: this.id, - eventType: "childUpdate", - }); - this.unsubscribeFromObjectEvent({ - objectType: "host", - _id: this.id, - eventType: "childDelete", - }); - this.unsubscribeFromObjectEvent({ - objectType: "host", - _id: this.id, - eventType: "childNew", - }); - } - async connect() { try { await stopWaiting(); @@ -120,7 +89,7 @@ export class SocketClient { } disconnect() { - this.unsubscribeFromObjectEvents(); + this.hostManager.unsubscribeFromObjectEvents(); this.socket.disconnect(); } @@ -154,109 +123,12 @@ export class SocketClient { }); } - async authenticateWithOtp(otp, options = {}) { - const { retryOnFailure = false, logs = true } = options; - - if (!otp) { - return { valid: false, error: "OTP is required" }; - } - - await this.waitForConnection(); - return this.authenticate({ otp }, { retryOnFailure, logs }); + authenticateWithOtp(otp, options) { + return this.hostManager.authenticateWithOtp(otp, options); } - async authenticate(authenticationData, options = {}) { - const { retryOnFailure = true, logs = true } = options; - - await this.waitForConnection(); - - return new Promise((resolve, reject) => { - if (!this.socket?.connected) { - reject(new Error("Socket client is not connected")); - return; - } - - if (logs) { - logger.debug("Host authenticating..."); - } - this.socket.emit( - "authenticate", - authenticationData, - async (verifyResult) => { - if (verifyResult.valid == false) { - this.authenticated = false; - sendIPC("setAuthenticated", false); - if (logs) { - logger.error("Host not authenticated:", verifyResult.error); - } - - resolve({ valid: false, error: verifyResult.error }); - return; - } - - if (logs) { - logger.info("Host authenticated."); - } - this.authenticated = true; - sendIPC("setAuthenticated", true); - sendIPC("setLoading", false); - this.loading = false; - if (this.host) { - sendIPC("setHost", { - ...this.host, - online: false, - state: { type: "offline" }, - }); - } - this.host = { - ...verifyResult.host, - state: { type: "online" }, - online: true, - }; - sendIPC("setHost", this.host); - this.id = this.host._id; - - config.host = { id: this.id, authCode: this.host.authCode }; - - await saveConfig(config); - - this.sendDeviceInfo(); - this.subscribeToObjectEvents(); - this.subscribeToObjectUpdates({ objectType: "host", _id: this.id }); - this.documentPrinterManager.reloadDocumentPrinters(); - this.printerManager.reloadPrinters(); - await this.fileManager.updateFiles(); - resolve({ valid: true, host: this.host }); - }, - ); - }); - } - - handleHostAction(action, callback) { - switch (action.type) { - case "reloadPrinters": - this.printerManager.updatePrinters().catch((error) => { - logger.error("Failed to reload printers:", error); - }); - return; - } - callback({ success: true }); - } - - async handleHostUpdate(data) { - if (data._id != this.id) { - return; - } - - logger.debug("Handling host update for id:", data._id); - const { online, state } = this.host; - this.host = { ...this.host, ...data.object, online, state }; - sendIPC("setHost", this.host); - - if (data.object?.authCode) { - config.host = { id: this.id, authCode: data.object.authCode }; - await saveConfig(config); - } + authenticate(authenticationData, options) { + return this.hostManager.authenticate(authenticationData, options); } handleObjectAction(data, callback) { @@ -266,7 +138,7 @@ export class SocketClient { const action = data.action; if (id == this.id && objectType == "host") { - this.handleHostAction(action, callback); + this.hostManager.handleAction(action, callback); } if (objectType == "printer") { @@ -290,7 +162,7 @@ export class SocketClient { data._id, ); if (data.objectType == "host") { - this.handleHostUpdate(data); + this.hostManager.handleUpdate(data); } if (data.objectType == "documentPrinter") { this.documentPrinterManager.handleDocumentPrinterUpdate( @@ -318,18 +190,7 @@ export class SocketClient { this.reconnectTimeout = null; } - const otpRequired = - config.host?.id == undefined || - config.host?.id == "" || - config.host?.authCode == undefined || - config.host?.authCode == ""; - if (otpRequired) { - logger.info("An OTP code is required to setup this host."); - this.authenticated = false; - sendIPC("setAuthenticated", false); - return; - } - this.authenticate({ id: config.host.id, authCode: config.host.authCode }); + this.hostManager.onConnect(); } async handleError(error) { @@ -345,14 +206,6 @@ export class SocketClient { this.scheduleReconnect(); } - sendDeviceInfo() { - logger.debug("Sending device info..."); - const deviceInfo = getDeviceInfo(); - this.socket.emit("updateHost", { - host: { deviceInfo: deviceInfo }, - }); - } - async listObjects({ objectType, populate, @@ -818,14 +671,6 @@ export class SocketClient { emitUnsubscribe(); } - async handleHostChildUpdate(data) { - if (data.parentType == "printer") { - this.printerManager.reloadPrinters(); - } else if (data.parentType == "documentPrinter") { - this.documentPrinterManager.reloadDocumentPrinters(); - } - } - async handleObjectEvent({ objectType, _id, event }) { logger.debug("Received object event...", { objectType, @@ -853,7 +698,7 @@ export class SocketClient { objectType == "host" && _id == this.id ) { - await this.handleHostChildUpdate(data); + await this.hostManager.handleChildEvent(data); } }