diff --git a/apps/edr-freight-api/src/modules/bookings/bookings.module.ts b/apps/edr-freight-api/src/modules/bookings/bookings.module.ts index fddb99945..d7c9c2506 100644 --- a/apps/edr-freight-api/src/modules/bookings/bookings.module.ts +++ b/apps/edr-freight-api/src/modules/bookings/bookings.module.ts @@ -2,13 +2,14 @@ import { Module } from "@nestjs/common"; import { TypeOrmModule } from "@nestjs/typeorm"; import { FilesModule } from "../files/files.module"; +import { MinioModule } from "../minio/minio.module"; import { BookingsController } from "./bookings.controller"; import { BookingsRepository } from "./bookings.repository"; import { BookingsService } from "./bookings.service"; import { Booking } from "./entities/booking.entity"; @Module({ - imports: [TypeOrmModule.forFeature([Booking]), FilesModule], + imports: [TypeOrmModule.forFeature([Booking]), FilesModule, MinioModule], controllers: [BookingsController], providers: [BookingsService, BookingsRepository], exports: [BookingsService], diff --git a/apps/edr-freight-api/src/modules/bookings/bookings.service.ts b/apps/edr-freight-api/src/modules/bookings/bookings.service.ts index 2493b5f89..b04a016cb 100644 --- a/apps/edr-freight-api/src/modules/bookings/bookings.service.ts +++ b/apps/edr-freight-api/src/modules/bookings/bookings.service.ts @@ -7,12 +7,14 @@ import { import { IsNull, Not } from "typeorm"; import { FilesService } from "../files/files.service"; +import { MinioService } from "../minio/minio.service"; import { BookingsRepository } from "./bookings.repository"; import { CreateBookingDto } from "./dto/create-booking.dto"; import { FilterBookingDto } from "./dto/filter-booking.dto"; import { UpdateBookingDto } from "./dto/update-booking.dto"; import { UpdateStatusDto } from "./dto/update-status.dto"; import { Booking } from "./entities/booking.entity"; +import { FileRecord } from "../files/entities/file.entity"; /** Weight thresholds (tons) that trigger overweight surcharge alerts. */ const WEIGHT_LIMITS = { @@ -29,6 +31,7 @@ export class BookingsService { constructor( private readonly bookingsRepository: BookingsRepository, private readonly filesService: FilesService, + private readonly minioService: MinioService, ) {} // ── helpers ────────────────────────────────────────────────────────── @@ -233,15 +236,45 @@ export class BookingsService { if (!booking) { throw new NotFoundException(`Booking ${id} not found`); } + + // Add signed URLs for files (5-minute expiration) + if (booking.files && booking.files.length > 0) { + booking.files = await Promise.all( + booking.files.map(async (file: FileRecord) => { + const objectName = this.extractObjectName(file.url); + const signedUrl = await this.minioService.getSignedUrl(objectName, 300); + return { ...file, signedUrl }; + }) + ); + } + return booking; } + /** Extract object name from Minio URL. */ + private extractObjectName(url: string): string { + const parts = url.split("/"); + return parts.slice(4).join("/"); + } + /** Find booking by reference with files. */ async findByReference(reference: string): Promise { const booking = await this.bookingsRepository.findByReferenceWithFiles(reference); if (!booking) { throw new NotFoundException(`Booking with reference "${reference}" not found`); } + + // Add signed URLs for files (5-minute expiration) + if (booking.files && booking.files.length > 0) { + booking.files = await Promise.all( + booking.files.map(async (file: FileRecord) => { + const objectName = this.extractObjectName(file.url); + const signedUrl = await this.minioService.getSignedUrl(objectName, 300); + return { ...file, signedUrl }; + }) + ); + } + return booking; } diff --git a/apps/edr-freight-api/src/modules/minio/minio.service.ts b/apps/edr-freight-api/src/modules/minio/minio.service.ts index eb75f18a8..4dba9c66e 100644 --- a/apps/edr-freight-api/src/modules/minio/minio.service.ts +++ b/apps/edr-freight-api/src/modules/minio/minio.service.ts @@ -72,4 +72,13 @@ export class MinioService { throw error; } } + + async getSignedUrl(objectName: string, expirySeconds: number = 300): Promise { + try { + return await this.client.presignedGetObject(this.bucket, objectName, expirySeconds); + } catch (error) { + this.logger.error(`Failed to generate signed URL for ${objectName}:`, error); + throw error; + } + } }