📜 Added JsDoc to methods
This commit is contained in:
@@ -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 });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user