diff --git a/projects/solidify-frontend/src/lib/server/helpers/server-cache.helper.ts b/projects/solidify-frontend/src/lib/server/helpers/server-cache.helper.ts index 7ba07596a6aa5335f2446ed43c49ff2ffa9e5fe6..67fde9d76355d15cd06107bf39773cb0f050ceab 100644 --- a/projects/solidify-frontend/src/lib/server/helpers/server-cache.helper.ts +++ b/projects/solidify-frontend/src/lib/server/helpers/server-cache.helper.ts @@ -58,21 +58,20 @@ export class ServerCacheHelper { return cacheRequest as CacheRequestModel; } - static cleanCacheByResourceId(logger: Logger, serverEnvironment: DefaultSolidifyServerEnvironment, ssrCache: SsrCache, resource: string, id: string): Promise<boolean> { + static async cleanCacheByResourceId(logger: Logger, serverEnvironment: DefaultSolidifyServerEnvironment, ssrCache: SsrCache, resource: string, id: string): Promise<boolean> { const cacheName = ServerCacheHelper.generateCacheName(resource, id); if (ssrCache.has(cacheName)) { - return ssrCache.clearResource(cacheName).then(() => { + try { + await ssrCache.clearResource(cacheName); logger.info(`Cache SSR cleaned for resource '${resource}' and id '${id}'`); return true; - }).catch(err => { + } catch (err) { logger.error(`Unable to clean SSR cache for resource '${resource}' and id '${id}'. See error ${err}`); return false; - }); + } } - return Promise.resolve(false).then(() => { - logger.error(`Unable to find SSR cache to clean for resource '${resource}' and id '${id}'`); - return false; - }); + logger.error(`Unable to find SSR cache to clean for resource '${resource}' and id '${id}'`); + return false; }; static generateCacheKey(cacheName: string, cacheVersion: string): string { diff --git a/projects/solidify-frontend/src/lib/server/server.ts b/projects/solidify-frontend/src/lib/server/server.ts index 4c77b641f703d7f295f5e6d0905256087e5b37bf..5e6a8e18e72f8eb89a6c0427211eeda96035c4d5 100644 --- a/projects/solidify-frontend/src/lib/server/server.ts +++ b/projects/solidify-frontend/src/lib/server/server.ts @@ -165,7 +165,7 @@ export class SsrServer { server.use(this._nodeTools.express.json()); if (isNonEmptyArray(this._serverEnvironment.staticRoutes)) { - server.get(this._serverEnvironment.staticRoutes, (req, res) => { + server.get(this._serverEnvironment.staticRoutes, async (req, res) => { this._logger.info("server.ts: serve static file : " + req.url); let url = req.url; const indexOfQueryParam = url.indexOf("?"); @@ -248,15 +248,16 @@ export class SsrServer { users: {[this._serverEnvironment.cleanCacheEndpointsBasicAuthLogin]: this._serverEnvironment.cleanCacheEndpointsBasicAuthPassword}, }), router); - server.post(["/clean-ssr-cache/all"], (req, res) => { - this._ssrCache.clearAll().then(() => { + server.post(["/clean-ssr-cache/all"], async (req, res) => { + try { + await this._ssrCache.clearAll(); res.status(200).send("Cache SSR cleaned"); - }).catch(err => { + } catch (e) { res.status(400).send("Unable to clean SSR cache"); - }); + } }); - server.post([`/clean-ssr-cache/:resource`], (req, res) => { + server.post([`/clean-ssr-cache/:resource`], async (req, res) => { const resource = req.params.resource; const listId = req.body as string[]; if (isNullOrUndefinedOrEmptyArray(listId)) { @@ -279,7 +280,7 @@ export class SsrServer { }); }); - server.post([`/clean-ssr-cache/:resource/:id`], (req, res) => { + server.post([`/clean-ssr-cache/:resource/:id`], async (req, res) => { const resource = req.params.resource; const id = req.params.id; ServerCacheHelper.cleanCacheByResourceId(this._logger, this._serverEnvironment, this._ssrCache, resource, id).then(result => { diff --git a/projects/solidify-frontend/src/lib/server/services/server-cache.service.ts b/projects/solidify-frontend/src/lib/server/services/server-cache.service.ts index af9788de00b2f0044e31cadb9c5afd2525332b25..c2db0a002ba6d9117b34e31edb84fd71ecb7ba98 100644 --- a/projects/solidify-frontend/src/lib/server/services/server-cache.service.ts +++ b/projects/solidify-frontend/src/lib/server/services/server-cache.service.ts @@ -21,6 +21,7 @@ * ----------------------------------------------------------------------------------------------%% */ +import {Stats} from "node:fs"; import {Logger} from "winston"; import {SOLIDIFY_CONSTANTS} from "../../core/constants"; import { @@ -64,20 +65,21 @@ export class SsrCache { return this._nodeTools.join(this._cacheLocation, ...relativePath); } - private _createDirectoryIfNotExist(...relativePath: string[]): void { + private async _createDirectoryIfNotExist(...relativePath: string[]): Promise<void> { const absolutePath = this._getAbsolutePath(...relativePath); - if (!this._nodeTools.fs.existsSync(absolutePath)) { - this._nodeTools.fs.mkdirSync(absolutePath, {recursive: true}); + const exist = await this._fileExist(absolutePath); + if (!exist) { + await this._nodeTools.fsp.mkdir(absolutePath, {recursive: true}); } } - private _deleteDirectoryIfExit(...relativePath: string[]): Promise<void> { + private async _deleteDirectoryIfExit(...relativePath: string[]): Promise<void> { const absolutePath = this._getAbsolutePath(...relativePath); - return this._deleteDirectoryIfExitAbsolute(absolutePath); + return await this._deleteDirectoryIfExitAbsolute(absolutePath); } - private _deleteDirectoryIfExitAbsolute(absolutePath: string): Promise<void> { - if (this._nodeTools.fs.existsSync(absolutePath)) { + private async _deleteDirectoryIfExitAbsolute(absolutePath: string): Promise<void> { + if (await this._fileExist(absolutePath)) { return this._deleteDirectoryAbsolute(absolutePath); } return Promise.resolve(); @@ -109,7 +111,7 @@ export class SsrCache { * @param cacheRequest * @param acceptGzip */ - getPath(cacheRequest: CacheRequestModel, acceptGzip: boolean): string | undefined { + getPath(cacheRequest: CacheRequestModel, acceptGzip: boolean): Promise<string | undefined> { const cacheName = cacheRequest.cacheName; const md5Path = this._generateMd5RelativePath(cacheName).join(SOLIDIFY_CONSTANTS.SEPARATOR); const path = this._nodeTools.join(md5Path, cacheRequest.cacheKey + this._HTML_EXTENSION); @@ -126,17 +128,21 @@ export class SsrCache { return undefined; } - private _getAbsolutePathCache(cacheRequest: CacheRequestModel, absolutePath: string, returnCompressed: boolean): string | undefined { + private async _getAbsolutePathCache(cacheRequest: CacheRequestModel, absolutePath: string, returnCompressed: boolean): Promise<string | undefined> { const absolutePathGzip = absolutePath + this._GZIP_EXTENSION; const absolutePathUsed = returnCompressed ? absolutePathGzip : absolutePath; - const isValidCache = this._isValidCache(cacheRequest.requestOption.cacheTimeToLiveInMinutes, absolutePathUsed); - if (isNullOrUndefined(isValidCache) || isTrue(isValidCache)) { - return absolutePathUsed; + try { + const isValidCache = await this._isValidCache(cacheRequest.requestOption.cacheTimeToLiveInMinutes, absolutePathUsed); + if (isNullOrUndefined(isValidCache) || isTrue(isValidCache)) { + return absolutePathUsed; + } + this._logger.info(`server-cache.service.ts: Remove expired cache for version ${cacheRequest.cacheKey}`); + await this._deleteDirectoryIfExitAbsolute(absolutePath); + await this._deleteDirectoryIfExitAbsolute(absolutePathGzip); + return undefined; + } catch (e) { + return undefined; } - this._logger.info(`server-cache.service.ts: Remove expired cache for version ${cacheRequest.cacheKey}`); - this._deleteDirectoryIfExitAbsolute(absolutePath); - this._deleteDirectoryIfExitAbsolute(absolutePathGzip); - return undefined; } /** @@ -146,22 +152,22 @@ export class SsrCache { * @param value * @param ttl */ - set(cacheRequest: CacheRequestModel, value: any): Promise<void> { + async set(cacheRequest: CacheRequestModel, value: any): Promise<void> { const cacheName = cacheRequest.cacheName; const md5RelativePath = this._generateMd5RelativePath(cacheName).join(SOLIDIFY_CONSTANTS.SEPARATOR); - this._createDirectoryIfNotExist(md5RelativePath); + await this._createDirectoryIfNotExist(md5RelativePath); const relativePath = this._nodeTools.join(md5RelativePath, cacheRequest.cacheKey + this._HTML_EXTENSION); const absolutePath = this._getAbsolutePath(relativePath); - this._deleteDirectoryIfExit(relativePath); - return this._nodeTools.fsp.appendFile(absolutePath, value, {encoding: this._HTML_ENCODING}) - .then(() => { - this._nodeTools.fs.createReadStream(absolutePath) - .pipe(this._nodeTools.zlib.createGzip({level: 9})) - .pipe(this._nodeTools.fs.createWriteStream(`${absolutePath}${this._GZIP_EXTENSION}`)) - .on("finish", () => this._logger.verbose(`server-cache.service.ts: Successfully generate gzip for cache result ${cacheRequest.cacheKey}`)); - }).catch(e => { - this._logger.error(`server-cache.service.ts: Unable to generate gzip version for cache ${cacheRequest.cacheKey}`, e); - }); + await this._deleteDirectoryIfExit(relativePath); + try { + await this._nodeTools.fsp.appendFile(absolutePath, value, {encoding: this._HTML_ENCODING}); + this._nodeTools.fs.createReadStream(absolutePath) + .pipe(this._nodeTools.zlib.createGzip({level: 9})) + .pipe(this._nodeTools.fs.createWriteStream(`${absolutePath}${this._GZIP_EXTENSION}`)) + .on("finish", () => this._logger.verbose(`server-cache.service.ts: Successfully generate gzip for cache result ${cacheRequest.cacheKey}`)); + } catch (e) { + this._logger.error(`server-cache.service.ts: Unable to generate gzip version for cache ${cacheRequest.cacheKey}`, e); + } } /** @@ -169,16 +175,16 @@ export class SsrCache { * * @param cacheName resource and eventually id (ex: home | archive-58eb331b-a45f-4848-8c3c-1e613730d213) */ - clearResource(cacheName: string): Promise<any> { + async clearResource(cacheName: string): Promise<any> { const relativePath = this._generateMd5RelativePath(cacheName); - return this._deleteDirectoryIfExit(...relativePath); + return await this._deleteDirectoryIfExit(...relativePath); } /** * Clear all ssr cache */ - clearAll(): Promise<void> { - return this._deleteDirectoryIfExit(); + async clearAll(): Promise<void> { + return await this._deleteDirectoryIfExit(); } private _generateMd5RelativePath(cacheName: string): string[] { @@ -189,22 +195,44 @@ export class SsrCache { return this._nodeTools.md5(cacheName); } - private _isValidCache(timeToLiveInMinutes: number | undefined, absolutePath: string): boolean | undefined { + private async _isValidCache(timeToLiveInMinutes: number | undefined, absolutePath: string): Promise<boolean | undefined> { if (isNullOrUndefined(timeToLiveInMinutes)) { return undefined; } - const creationDate = this._getFileCreationDate(absolutePath); - if (isNullOrUndefined(creationDate)) { + try { + const creationDate = await this._getFileCreationDate(absolutePath); + if (isNullOrUndefined(creationDate)) { + return undefined; + } + const currentDate = new Date(); + const dateExpiration = new Date(creationDate.setMinutes(creationDate.getMinutes() + timeToLiveInMinutes)); + const isValidCache = currentDate <= dateExpiration; + return isValidCache; + } catch (e) { + return undefined; + } + } + + private async _getFileCreationDate(absolutePath: string): Promise<Date | undefined> { + try { + const stats = await this._fileStats(absolutePath); + return stats.birthtime; + } catch (e) { return undefined; } - const currentDate = new Date(); - const dateExpiration = new Date(creationDate.setMinutes(creationDate.getMinutes() + timeToLiveInMinutes)); - const isValidCache = currentDate <= dateExpiration; - return isValidCache; } - private _getFileCreationDate(absolutePath: string): Date | undefined { - return this._nodeTools.fs.statSync(absolutePath)?.birthtime; + private async _fileExist(absoluteFilePath: string): Promise<boolean> { + try { + await this._fileStats(absoluteFilePath); + return true; + } catch (e) { + return false; + } + } + + private async _fileStats(absoluteFilePath: string): Promise<Stats> { + return await this._nodeTools.fsp.stat(absoluteFilePath); } }