📜 Added JsDoc to methods

This commit is contained in:
2025-06-10 13:55:11 -04:00
parent a0671ce6d1
commit b332d9d75a

View File

@@ -8,15 +8,25 @@ import { IFolderData } from "threetwo-ui-typings";
/** /**
* ApiService exposes REST endpoints and watches the comics directory for changes. * 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 { 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) { public constructor(broker: ServiceBroker) {
super(broker); super(broker);
this.parseServiceSchema({ this.parseServiceSchema({
name: "api", name: "api",
mixins: [ApiGateway], mixins: [ApiGateway],
// More info about settings: https://moleculer.services/docs/0.14/moleculer-web.html
settings: { settings: {
port: process.env.PORT || 3000, port: process.env.PORT || 3000,
routes: [ routes: [
@@ -38,7 +48,6 @@ export default class ApiService extends Service {
autoAliases: true, autoAliases: true,
aliases: {}, aliases: {},
callingOptions: {}, callingOptions: {},
bodyParsers: { bodyParsers: {
json: { strict: false, limit: "1MB" }, json: { strict: false, limit: "1MB" },
urlencoded: { extended: true, 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 { private startWatcher(): void {
const watchDir = path.resolve(process.env.COMICS_PATH || "/comics"); const rawDir = process.env.COMICS_DIRECTORY;
this.logger.info(`Watching comics folder: ${watchDir}`); 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, { this.fileWatcher = chokidar.watch(watchDir, {
persistent: true, persistent: true,
@@ -93,12 +110,22 @@ private fileWatcher?: any;
ignored: (p) => p.endsWith(".dctmp") || p.includes("/.git/"), 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( const debouncedEvent = debounce(
(event: string, p: string, stats?: fs.Stats) => { (event: string, p: string, stats?: fs.Stats) => {
try { try {
this.handleFileEvent(event, p, stats); this.handleFileEvent(event, p, stats);
} catch (err) { } 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, 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<void> { private async stopWatcher(): Promise<void> {
if (this.fileWatcher) { if (this.fileWatcher) {
@@ -127,10 +155,11 @@ private fileWatcher?: any;
} }
/** /**
* Handles and broadcasts file system events. * Handles a filesystem event by logging and optionally importing new files.
* @param event - Event type (add, change, etc.) * @param event - The type of chokidar event ('add', 'change', 'unlink', etc.).
* @param filePath - Path of the file or directory * @param filePath - The full path of the file or directory that triggered the event.
* @param stats - Optional file stats * @param stats - Optional fs.Stats data for 'add' or 'change' events.
* @private
*/ */
private async handleFileEvent( private async handleFileEvent(
event: string, event: string,
@@ -139,7 +168,6 @@ private fileWatcher?: any;
): Promise<void> { ): Promise<void> {
this.logger.info(`File event [${event}]: ${filePath}`); this.logger.info(`File event [${event}]: ${filePath}`);
if (event === "add" && stats) { if (event === "add" && stats) {
// Wait for write to stabilize
setTimeout(async () => { setTimeout(async () => {
const newStats = await fs.promises.stat(filePath); const newStats = await fs.promises.stat(filePath);
if (newStats.mtime.getTime() === stats.mtime.getTime()) { if (newStats.mtime.getTime() === stats.mtime.getTime()) {
@@ -149,12 +177,14 @@ private fileWatcher?: any;
{ basePathToWalk: filePath } { basePathToWalk: filePath }
); );
await this.broker.call("importqueue.processImport", { await this.broker.call("importqueue.processImport", {
fileObject: { filePath, fileSize: folderData[0].fileSize }, fileObject: {
filePath,
fileSize: folderData[0].fileSize,
},
}); });
} }
}, 3000); }, 3000);
} }
// Broadcast to other services or clients
this.broker.broadcast(event, { path: filePath }); this.broker.broadcast(event, { path: filePath });
} }
} }