From b332d9d75af8596924a65e0f10ecae8387db689f Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Tue, 10 Jun 2025 13:55:11 -0400 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9C=20Added=20JsDoc=20to=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/api.service.ts | 66 ++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/services/api.service.ts b/services/api.service.ts index e901a18..6619df5 100644 --- a/services/api.service.ts +++ b/services/api.service.ts @@ -8,15 +8,25 @@ import { IFolderData } from "threetwo-ui-typings"; /** * ApiService exposes REST endpoints and watches the comics directory for changes. - * Uses chokidar to watch the directory and broadcasts file events via Moleculer. + * It uses chokidar to monitor filesystem events and broadcasts them via the Moleculer broker. + * @extends Service */ export default class ApiService extends Service { + /** + * The chokidar file system watcher instance. + * @private + */ + private fileWatcher?: any; + + /** + * Creates an instance of ApiService. + * @param {ServiceBroker} broker - The Moleculer service broker instance. + */ public constructor(broker: ServiceBroker) { super(broker); this.parseServiceSchema({ name: "api", mixins: [ApiGateway], - // More info about settings: https://moleculer.services/docs/0.14/moleculer-web.html settings: { port: process.env.PORT || 3000, routes: [ @@ -38,7 +48,6 @@ export default class ApiService extends Service { autoAliases: true, aliases: {}, callingOptions: {}, - bodyParsers: { json: { strict: false, limit: "1MB" }, urlencoded: { extended: true, limit: "1MB" }, @@ -71,15 +80,23 @@ export default class ApiService extends Service { }); } - /** Active file system watcher instance. */ -private fileWatcher?: any; - /** - * Starts watching the comics directory with debounced, robust handlers. + * Initializes and starts the chokidar watcher on the COMICS_DIRECTORY. + * Debounces rapid events and logs initial scan completion. + * @private */ private startWatcher(): void { - const watchDir = path.resolve(process.env.COMICS_PATH || "/comics"); - this.logger.info(`Watching comics folder: ${watchDir}`); + const rawDir = process.env.COMICS_DIRECTORY; + if (!rawDir) { + this.logger.error("COMICS_DIRECTORY not set; cannot start watcher"); + return; + } + const watchDir = path.resolve(rawDir); + this.logger.info(`Watching comics folder at: ${watchDir}`); + if (!fs.existsSync(watchDir)) { + this.logger.error(`✖ Comics folder does not exist: ${watchDir}`); + return; + } this.fileWatcher = chokidar.watch(watchDir, { persistent: true, @@ -93,12 +110,22 @@ private fileWatcher?: any; ignored: (p) => p.endsWith(".dctmp") || p.includes("/.git/"), }); + /** + * Debounced handler for file system events, batching rapid triggers + * into a 200ms window. Leading and trailing calls invoked. + * @param {string} event - Type of file event (add, change, etc.). + * @param {string} p - Path of the file or directory. + * @param {fs.Stats} [stats] - Optional file stats for add/change events. + */ const debouncedEvent = debounce( (event: string, p: string, stats?: fs.Stats) => { try { this.handleFileEvent(event, p, stats); } catch (err) { - this.logger.error(`Error handling file event [${event}] for ${p}:`, err); + this.logger.error( + `Error handling file event [${event}] for ${p}:`, + err + ); } }, 200, @@ -116,7 +143,8 @@ private fileWatcher?: any; } /** - * Stops the file watcher and frees resources. + * Stops and closes the chokidar watcher, freeing resources. + * @private */ private async stopWatcher(): Promise { if (this.fileWatcher) { @@ -127,10 +155,11 @@ private fileWatcher?: any; } /** - * Handles and broadcasts file system events. - * @param event - Event type (add, change, etc.) - * @param filePath - Path of the file or directory - * @param stats - Optional file stats + * Handles a filesystem event by logging and optionally importing new files. + * @param event - The type of chokidar event ('add', 'change', 'unlink', etc.). + * @param filePath - The full path of the file or directory that triggered the event. + * @param stats - Optional fs.Stats data for 'add' or 'change' events. + * @private */ private async handleFileEvent( event: string, @@ -139,7 +168,6 @@ private fileWatcher?: any; ): Promise { this.logger.info(`File event [${event}]: ${filePath}`); if (event === "add" && stats) { - // Wait for write to stabilize setTimeout(async () => { const newStats = await fs.promises.stat(filePath); if (newStats.mtime.getTime() === stats.mtime.getTime()) { @@ -149,12 +177,14 @@ private fileWatcher?: any; { basePathToWalk: filePath } ); await this.broker.call("importqueue.processImport", { - fileObject: { filePath, fileSize: folderData[0].fileSize }, + fileObject: { + filePath, + fileSize: folderData[0].fileSize, + }, }); } }, 3000); } - // Broadcast to other services or clients this.broker.broadcast(event, { path: filePath }); } }