From a3c302e7565bf612535dd61af00fdc9831a85378 Mon Sep 17 00:00:00 2001 From: ShittyKopper Date: Thu, 4 Jan 2024 19:55:02 +0300 Subject: [PATCH] upd: introduce a separate ./cache dir and cache video thumbnails there --- .gitignore | 1 + docker-compose_example.yml | 1 + .../src/core/InternalStorageService.ts | 27 +++++++++++++++++++ .../backend/src/server/FileServerService.ts | 12 +++++++++ 4 files changed, 41 insertions(+) diff --git a/.gitignore b/.gitignore index 11e69b2621..4816375a4f 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,7 @@ api-docs.json *.code-workspace .DS_Store /files +/cache ormconfig.json temp /packages/frontend/src/**/*.stories.ts diff --git a/docker-compose_example.yml b/docker-compose_example.yml index 0a422a2a91..b61daa5eb3 100644 --- a/docker-compose_example.yml +++ b/docker-compose_example.yml @@ -20,6 +20,7 @@ services: - shonk volumes: - ./files:/sharkey/files + - ./cache:/sharkey/cache - ./.config:/sharkey/.config:ro redis: diff --git a/packages/backend/src/core/InternalStorageService.ts b/packages/backend/src/core/InternalStorageService.ts index 22129bb348..96d7be2acb 100644 --- a/packages/backend/src/core/InternalStorageService.ts +++ b/packages/backend/src/core/InternalStorageService.ts @@ -16,6 +16,7 @@ const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); const path = Path.resolve(_dirname, '../../../../files'); +const cachePath = Path.resolve(_dirname, '../../../../cache'); @Injectable() export class InternalStorageService { @@ -30,11 +31,26 @@ export class InternalStorageService { return Path.resolve(path, key); } + @bindThis + public resolveCachePath(key: string) { + return Path.resolve(cachePath, key); + } + + @bindThis + public existsCache(key: string) { + return fs.existsSync(this.resolveCachePath(key)); + } + @bindThis public read(key: string) { return fs.createReadStream(this.resolvePath(key)); } + @bindThis + public readCache(key: string) { + return fs.createReadStream(this.resolveCachePath(key)); + } + @bindThis public saveFromPath(key: string, srcPath: string) { fs.mkdirSync(path, { recursive: true }); @@ -49,8 +65,19 @@ export class InternalStorageService { return `${this.config.url}/files/${key}`; } + @bindThis + public saveCacheFromBuffer(key: string, data: Buffer) { + fs.mkdirSync(cachePath, { recursive: true }); + fs.writeFileSync(this.resolveCachePath(key), data); + } + @bindThis public del(key: string) { fs.unlink(this.resolvePath(key), () => {}); } + + @bindThis + public delCache(key: string) { + fs.unlink(this.resolveCachePath(key), () => {}); + } } diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index 714dddb162..6659932ed1 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -4,6 +4,7 @@ */ import * as fs from 'node:fs'; +import * as crypto from 'node:crypto'; import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; import { Inject, Injectable } from '@nestjs/common'; @@ -33,6 +34,7 @@ const _filename = fileURLToPath(import.meta.url); const _dirname = dirname(_filename); const assets = `${_dirname}/../../server/file/assets/`; +const cacheDir = `${_dirname}/../../../../cache/`; @Injectable() export class FileServerService { @@ -380,6 +382,14 @@ export class FileServerService { @bindThis private async videoThumbnailHandler(request: FastifyRequest<{ Querystring: { url: string; }; }>, reply: FastifyReply) { + const cacheKey = crypto.createHash('md5').update(request.query.url).digest('base64url'); + const cacheFile = `videoThumbnail-${cacheKey}.webp`; + if (this.internalStorageService.existsCache(cacheFile)) { + reply.header('Content-Type', 'image/webp'); + reply.header('Cache-Control', 'max-age=31536000, immutable'); + return reply.sendFile(cacheFile, cacheDir); + } + const file = await this.getStreamAndTypeFromUrl(request.query.url); if (file === '404') { @@ -414,6 +424,8 @@ export class FileServerService { file.cleanup(); } + this.internalStorageService.saveCacheFromBuffer(cacheFile, image.data); + reply.header('Content-Type', image.type); reply.header('Cache-Control', 'max-age=31536000, immutable'); return image.data;