From 7d8d4e4d6bd376da3b7b3e3f72265b827039b726 Mon Sep 17 00:00:00 2001 From: Rodrigo Rodriguez Date: Wed, 11 Sep 2024 21:02:19 -0300 Subject: [PATCH] fix (templates): tal-to-data OK. --- packages/core.gbapp/services/GBDeployer.ts | 2 +- packages/core.gbapp/services/GBMinService.ts | 87 +++++++------------- src/util.ts | 68 ++++++++------- 3 files changed, 66 insertions(+), 91 deletions(-) diff --git a/packages/core.gbapp/services/GBDeployer.ts b/packages/core.gbapp/services/GBDeployer.ts index e3ac9706..bd9fb3eb 100644 --- a/packages/core.gbapp/services/GBDeployer.ts +++ b/packages/core.gbapp/services/GBDeployer.ts @@ -607,7 +607,7 @@ export class GBDeployer implements IGBDeployer { if (!GBConfigService.get('STORAGE_NAME')) { const filePath = path.join(GBConfigService.get('STORAGE_LIBRARY'), gbai, packageName); - GBUtil.copyIfNewerRecursive(filePath, packageWorkFolder); + await GBUtil.copyIfNewerRecursive(filePath, packageWorkFolder); } else { await this.downloadFolder(min, path.join('work', `${gbai}`), packageName); } diff --git a/packages/core.gbapp/services/GBMinService.ts b/packages/core.gbapp/services/GBMinService.ts index 803d1528..973724e2 100644 --- a/packages/core.gbapp/services/GBMinService.ts +++ b/packages/core.gbapp/services/GBMinService.ts @@ -36,9 +36,8 @@ import { createRpcServer } from '@push-rpc/core'; import AuthenticationContext from 'adal-node'; import arrayBufferToBuffer from 'arraybuffer-to-buffer'; -import { watch } from 'fs'; -import debounce from 'lodash.debounce'; - +import { Semaphore } from 'async-mutex'; +import { Mutex } from 'async-mutex'; import chokidar from 'chokidar'; import { AutoSaveStateMiddleware, @@ -409,7 +408,7 @@ export class GBMinService { const packageTeams = urlJoin(`work`, GBUtil.getGBAIPath(instance.botId), manifest); if (!(await GBUtil.exists(packageTeams))) { const data = await this.deployer.getBotManifest(instance); - await fs.writeFile(packageTeams, data); + await fs.writeFile(packageTeams, data); } // Serves individual URL for each bot user interface. @@ -487,7 +486,7 @@ export class GBMinService { return min; } - + public static getProviderName(req: any, res: any) { if (!res) { return 'GeneralBots'; @@ -1117,7 +1116,7 @@ export class GBMinService { const folder = `work/${path}/cache`; const filename = `${GBAdminService.generateUuid()}.png`; - await fs.writeFile(urlJoin(folder, filename), data); + await fs.writeFile(urlJoin(folder, filename), data); step.context.activity.text = urlJoin( GBServer.globals.publicAddress, `${min.instance.botId}`, @@ -1305,7 +1304,7 @@ export class GBMinService { buffer = arrayBufferToBuffer(await res.arrayBuffer()); } -await fs.writeFile(localFileName, buffer); + await fs.writeFile(localFileName, buffer); return { fileName: attachment.name, @@ -1743,64 +1742,42 @@ await fs.writeFile(localFileName, buffer); createRpcServer(proxies, GBServer.globals.server.apiServer, opts); } - // Map to track recent changes with timestamps - private recentChanges: Map = new Map(); + // Map to track recent changes with timestamps - private async watchPackages(min: GBMinInstance, packageType) { + private recentChanges: Set = new Set(); + private mutex: Mutex = new Mutex(); + + public async watchPackages(min: GBMinInstance, packageType: string): Promise { if (!GBConfigService.get('STORAGE_NAME')) { const packagePath = GBUtil.getGBAIPath(min.botId, packageType); const libraryPath = path.join(GBConfigService.get('STORAGE_LIBRARY'), packagePath); - - const watcher = chokidar.watch(libraryPath, { - persistent: true, - ignoreInitial: true, // Ignore initial add events - depth: 99, // Watch subdirectories - awaitWriteFinish: { - stabilityThreshold: 500, // Wait for 500ms to ensure file is written completely - } - }); - - const WRITE_INTERVAL = 5000; // 5 seconds interval - - // Function to handle file changes and prevent multiple calls within the 5-second window - const handleFileChange = async (filePath) => { - const now = Date.now(); - - // Add or update the file in the recent changes list - this.recentChanges.set(filePath, now); - - // Clean up entries older than 5 seconds - for (const [key, timestamp] of this.recentChanges.entries()) { - if (now - timestamp > WRITE_INTERVAL) { - this.recentChanges.delete(key); - } - } - - // If we have recent changes, deploy the package only once - if (this.recentChanges.size > 0) { - - try { - const workFolder = path.join('work', packagePath); - await this.deployer.deployPackage2(min, null, workFolder, true); - } catch (error) { - GBLogEx.error(min, `Error deploying package: ${GBUtil.toYAML(error)}`); + const watcher = chokidar.watch(libraryPath, { + depth: 99 // Watch subdirectories + }); + + const handleFileChange = async (filePath: string) => { + this.recentChanges.add(filePath); + + // Use mutex to ensure only one deployment runs at a time + await this.mutex.runExclusive(async () => { + if (this.recentChanges.size > 0) { + try { + const workFolder = path.join('work', packagePath); + await this.deployer.deployPackage2(min, null, workFolder, true); + } catch (error) { + GBLogEx.error(min, `Error deploying package: ${GBUtil.toYAML(error)}`); + } finally { + this.recentChanges.clear(); + } } - - // Delay further processing to prevent multiple deployments within 5 seconds - await new Promise(resolve => setTimeout(resolve, WRITE_INTERVAL)); - - // After the delay, clear only entries that were processed - this.recentChanges.clear(); - } + }); }; - + // Watch for file changes - watcher.on('change', (filePath) => { + watcher.on('change', filePath => { handleFileChange(filePath).catch(error => console.error('Error processing file change:', error)); }); - } } - } diff --git a/src/util.ts b/src/util.ts index e678d32e..874fa1f1 100644 --- a/src/util.ts +++ b/src/util.ts @@ -161,47 +161,45 @@ export class GBUtil { } public static async copyIfNewerRecursive(src, dest) { + // Check if the source exists if (!(await GBUtil.exists(src))) { - console.error(`Source path "${src}" does not exist.`); - return; + return; } - - // Check if the source is a directory - if ((await fs.stat(src)).isDirectory()) { - // Create the destination directory if it doesn't exist - if (!(await GBUtil.exists(dest))) { - await fs.mkdir(dest, { recursive: true }); - } - - // Read all files and directories in the source directory - const entries = await fs.readdir(src); - - for (let entry of entries) { - const srcEntry = path.join(src, entry); - const destEntry = path.join(dest, entry); - - // Recursively copy each entry - - await this.copyIfNewerRecursive(srcEntry, destEntry); - } - } else { - // Source is a file, check if we need to copy it - - if (await GBUtil.exists(dest)) { - const srcStat = await fs.stat(src); - const destStat = await fs.stat(dest); - - // Copy only if the source file is newer than the destination file - if (srcStat.mtime > destStat.mtime) { - await fs.cp(src, dest, { force: true }); + + // Check if the source is a directory + if ((await fs.stat(src)).isDirectory()) { + // Create the destination directory if it doesn't exist + if (!(await GBUtil.exists(dest))) { + await fs.mkdir(dest, { recursive: true }); + } + + // Read all files and directories in the source directory + const entries = await fs.readdir(src); + + for (let entry of entries) { + const srcEntry = path.join(src, entry); + const destEntry = path.join(dest, entry); + + // Recursively copy each entry + await this.copyIfNewerRecursive(srcEntry, destEntry); } } else { - // Destination file doesn't exist, so copy it - await fs.cp(src, dest, { force: true }); + // Source is a file, check if we need to copy it + if (await GBUtil.exists(dest)) { + const srcStat = await fs.stat(src); + const destStat = await fs.stat(dest); + + // Copy only if the source file is newer than the destination file + if (srcStat.mtime > destStat.mtime) { + await fs.cp(src, dest, { force: true }); + } + } else { + // Destination file doesn't exist, so copy it + await fs.cp(src, dest, { force: true }); + } } - } } - // Check if is a tree or flat object. + // Check if is a tree or flat object. public static hasSubObject(t) { for (var key in t) {