From 84c24b2e92ad2e85f2e3462ac09b9e184d417c3f Mon Sep 17 00:00:00 2001 From: Andreas Knuth Date: Fri, 12 Jun 2026 14:42:14 -0500 Subject: [PATCH] fix for image dir --- bizmatch-server/src/config/pictures.config.ts | 4 +++ bizmatch-server/src/file/file.service.ts | 27 ++++++++++--------- bizmatch-server/src/image/image.controller.ts | 7 ++--- bizmatch-server/src/main.ts | 3 ++- docker-compose.yml | 3 +++ 5 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 bizmatch-server/src/config/pictures.config.ts diff --git a/bizmatch-server/src/config/pictures.config.ts b/bizmatch-server/src/config/pictures.config.ts new file mode 100644 index 0000000..b5bd96f --- /dev/null +++ b/bizmatch-server/src/config/pictures.config.ts @@ -0,0 +1,4 @@ +// Single source of truth for the pictures directory. +// MUST match the container-side path of the volume mount in docker-compose.yml: +// ./bizmatch-server/pictures:/app/dist/pictures +export const PICTURES_DIR = process.env.PICTURES_DIR || '/app/dist/pictures'; diff --git a/bizmatch-server/src/file/file.service.ts b/bizmatch-server/src/file/file.service.ts index 077ec8f..07b67eb 100644 --- a/bizmatch-server/src/file/file.service.ts +++ b/bizmatch-server/src/file/file.service.ts @@ -3,14 +3,15 @@ import fs from 'fs-extra'; import { WINSTON_MODULE_PROVIDER } from 'nest-winston'; import sharp from 'sharp'; import { Logger } from 'winston'; +import { PICTURES_DIR } from '../config/pictures.config'; @Injectable() export class FileService { constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger) { - fs.ensureDirSync(`./pictures`); - fs.ensureDirSync(`./pictures/profile`); - fs.ensureDirSync(`./pictures/logo`); - fs.ensureDirSync(`./pictures/property`); + fs.ensureDirSync(`${PICTURES_DIR}`); + fs.ensureDirSync(`${PICTURES_DIR}/profile`); + fs.ensureDirSync(`${PICTURES_DIR}/logo`); + fs.ensureDirSync(`${PICTURES_DIR}/property`); } // ############ // Profile @@ -22,10 +23,10 @@ export class FileService { .avif({ quality }) // Verwende AVIF //.webp({ quality }) // Verwende Webp .toBuffer(); - await sharp(output).toFile(`./pictures/profile/${adjustedEmail}.avif`); + await sharp(output).toFile(`${PICTURES_DIR}/profile/${adjustedEmail}.avif`); } hasProfile(adjustedEmail: string) { - return fs.existsSync(`./pictures/profile/${adjustedEmail}.avif`); + return fs.existsSync(`${PICTURES_DIR}/profile/${adjustedEmail}.avif`); } // ############ // Logo @@ -37,18 +38,18 @@ export class FileService { .avif({ quality }) // Verwende AVIF //.webp({ quality }) // Verwende Webp .toBuffer(); - await sharp(output).toFile(`./pictures/logo/${adjustedEmail}.avif`); // Ersetze Dateierweiterung - // await fs.outputFile(`./pictures/logo/${userId}`, file.buffer); + await sharp(output).toFile(`${PICTURES_DIR}/logo/${adjustedEmail}.avif`); // Ersetze Dateierweiterung + // await fs.outputFile(`${PICTURES_DIR}/logo/${userId}`, file.buffer); } hasCompanyLogo(adjustedEmail: string) { - return fs.existsSync(`./pictures/logo/${adjustedEmail}.avif`) ? true : false; + return fs.existsSync(`${PICTURES_DIR}/logo/${adjustedEmail}.avif`) ? true : false; } // ############ // Property // ############ async getPropertyImages(imagePath: string, serial: string): Promise { const result: string[] = []; - const directory = `./pictures/property/${imagePath}/${serial}`; + const directory = `${PICTURES_DIR}/property/${imagePath}/${serial}`; if (fs.existsSync(directory)) { const files = await fs.readdir(directory); files.forEach(f => { @@ -60,7 +61,7 @@ export class FileService { } } async hasPropertyImages(imagePath: string, serial: string): Promise { - const directory = `./pictures/property/${imagePath}/${serial}`; + const directory = `${PICTURES_DIR}/property/${imagePath}/${serial}`; if (fs.existsSync(directory)) { const files = await fs.readdir(directory); return files.length > 0; @@ -69,7 +70,7 @@ export class FileService { } } async storePropertyPicture(file: Express.Multer.File, imagePath: string, serial: string): Promise { - const directory = `./pictures/property/${imagePath}/${serial}`; + const directory = `${PICTURES_DIR}/property/${imagePath}/${serial}`; fs.ensureDirSync(`${directory}`); const imageName = await this.getNextImageName(directory); //await fs.outputFile(`${directory}/${imageName}`, file.buffer); @@ -111,7 +112,7 @@ export class FileService { } deleteDirectoryIfExists(imagePath) { - const dirPath = `pictures/property/${imagePath}`; + const dirPath = `${PICTURES_DIR}/property/${imagePath}`; try { const exists = fs.pathExistsSync(); if (exists) { diff --git a/bizmatch-server/src/image/image.controller.ts b/bizmatch-server/src/image/image.controller.ts index 2766e17..2bfdd9e 100644 --- a/bizmatch-server/src/image/image.controller.ts +++ b/bizmatch-server/src/image/image.controller.ts @@ -6,6 +6,7 @@ import { Logger } from 'winston'; import { FileService } from '../file/file.service'; import { CommercialPropertyService } from '../listings/commercial-property.service'; import { SelectOptionsService } from '../select-options/select-options.service'; +import { PICTURES_DIR } from '../config/pictures.config'; @Controller('image') export class ImageController { @@ -28,7 +29,7 @@ export class ImageController { @UseGuards(AuthGuard) @Delete('propertyPicture/:imagePath/:serial/:imagename') async deletePropertyImagesById(@Param('imagePath') imagePath: string, @Param('serial') serial: string, @Param('imagename') imagename: string): Promise { - this.fileService.deleteImage(`pictures/property/${imagePath}/${serial}/${imagename}`); + this.fileService.deleteImage(`${PICTURES_DIR}/property/${imagePath}/${serial}/${imagename}`); await this.listingService.deleteImage(imagePath, serial, imagename); } // ############ @@ -43,7 +44,7 @@ export class ImageController { @UseGuards(AuthGuard) @Delete('profile/:email/') async deleteProfileImagesById(@Param('email') email: string): Promise { - this.fileService.deleteImage(`pictures/profile/${email}.avif`); + this.fileService.deleteImage(`${PICTURES_DIR}/profile/${email}.avif`); } // ############ // Logo @@ -57,6 +58,6 @@ export class ImageController { @UseGuards(AuthGuard) @Delete('logo/:email/') async deleteLogoImagesById(@Param('email') adjustedEmail: string): Promise { - this.fileService.deleteImage(`pictures/logo/${adjustedEmail}.avif`); + this.fileService.deleteImage(`${PICTURES_DIR}/logo/${adjustedEmail}.avif`); } } diff --git a/bizmatch-server/src/main.ts b/bizmatch-server/src/main.ts index 760275b..b4e8d38 100644 --- a/bizmatch-server/src/main.ts +++ b/bizmatch-server/src/main.ts @@ -4,6 +4,7 @@ import express from 'express'; import helmet from 'helmet'; import { WINSTON_MODULE_NEST_PROVIDER } from 'nest-winston'; import { AppModule } from './app.module'; +import { PICTURES_DIR } from './config/pictures.config'; async function bootstrap() { const server = express(); @@ -14,7 +15,7 @@ async function bootstrap() { app.useLogger(logger); //app.use('/bizmatch/payment/webhook', bodyParser.raw({ type: 'application/json' })); // Serve static files from pictures directory - app.use('/pictures', express.static('pictures')); + app.use('/pictures', express.static(PICTURES_DIR)); app.setGlobalPrefix('bizmatch'); diff --git a/docker-compose.yml b/docker-compose.yml index 47193fa..037d875 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,6 +25,9 @@ services: restart: unless-stopped env_file: - ./bizmatch-server/.env + environment: + # MUST match the container-side path of the volume mount below + PICTURES_DIR: /app/dist/pictures depends_on: - postgres networks: