Enhance contract clearance features with review timestamps and staff identifiers; improve file handling in controllers and views for better user experience.

This commit is contained in:
Marshal
2026-06-27 19:30:42 +00:00
parent 0ab553bf48
commit 7a2a55d94b
8 changed files with 282 additions and 123 deletions

View File

@@ -1,5 +1,12 @@
import { Controller, Get, Param, ParseUUIDPipe, Res } from "@nestjs/common";
import { ApiOperation, ApiTags } from "@nestjs/swagger";
import {
Controller,
Get,
Param,
ParseUUIDPipe,
Query,
Res,
} from "@nestjs/common";
import { ApiOperation, ApiQuery, ApiTags } from "@nestjs/swagger";
import { Response } from "express";
import { FilesService } from "./files.service";
@@ -11,18 +18,34 @@ export class FilesController {
@Get(":fileId")
@ApiOperation({
summary: "Download a file by ID",
summary: "Stream a file by ID",
description:
"Global endpoint — streams any uploaded file directly from MinIO by its UUID. " +
"No resource context (e.g. booking ID) required.",
"No resource context (e.g. booking ID) required. Serves inline by default so " +
"the browser can preview it; pass ?download=1 to force a download.",
})
@ApiQuery({
name: "download",
required: false,
description: "Set to 1/true to force a download instead of inline preview.",
})
async download(
@Param("fileId", ParseUUIDPipe) fileId: string,
@Query("download") download: string | undefined,
@Res() res: Response,
) {
const { stream, record } = await this.filesService.streamById(fileId);
const forceDownload = download === "1" || download === "true";
const disposition = forceDownload ? "attachment" : "inline";
res.setHeader("Content-Type", record.mimeType);
res.setHeader("Content-Disposition", `attachment; filename="${record.name}"`);
res.setHeader(
"Content-Disposition",
`${disposition}; filename="${record.name}"`,
);
// Allow the browser to cache the streamed bytes briefly for smoother
// in-page previews (re-opening the viewer shouldn't re-hit MinIO).
res.setHeader("Cache-Control", "private, max-age=300");
stream.pipe(res);
}
}