Files
pingvin-share/backend/src/file/file.controller.ts
Lucas Reis ccc783ab6a feat(s3): stream s3 content over a zip file (#822)
* chore: update package.json and enhance file handling in backend

* refactor: remove StreamResponseFilter and update getZip method for improved file handling

- Deleted StreamResponseFilter as it was no longer needed.
- Updated getZip method in LocalFileService to return a Promise and handle errors more effectively.
- Adjusted getZip method in FileService to await the storage service's getZip call.

* refactor: remove unused UseFilters import in file.controller.ts
2025-05-04 20:37:04 +02:00

107 lines
2.6 KiB
TypeScript

import {
Body,
Controller,
Delete,
Get,
Param,
Post,
Query,
Res,
StreamableFile,
UseGuards,
} from "@nestjs/common";
import { SkipThrottle } from "@nestjs/throttler";
import * as contentDisposition from "content-disposition";
import { Response } from "express";
import { CreateShareGuard } from "src/share/guard/createShare.guard";
import { ShareOwnerGuard } from "src/share/guard/shareOwner.guard";
import { FileService } from "./file.service";
import { FileSecurityGuard } from "./guard/fileSecurity.guard";
import * as mime from "mime-types";
@Controller("shares/:shareId/files")
export class FileController {
constructor(private fileService: FileService) {}
@Post()
@SkipThrottle()
@UseGuards(CreateShareGuard, ShareOwnerGuard)
async create(
@Query()
query: {
id: string;
name: string;
chunkIndex: string;
totalChunks: string;
},
@Body() body: string,
@Param("shareId") shareId: string,
) {
const { id, name, chunkIndex, totalChunks } = query;
// Data can be empty if the file is empty
return await this.fileService.create(
body,
{ index: parseInt(chunkIndex), total: parseInt(totalChunks) },
{ id, name },
shareId,
);
}
@Get("zip")
@UseGuards(FileSecurityGuard)
async getZip(
@Res({ passthrough: true }) res: Response,
@Param("shareId") shareId: string,
) {
const zipStream = await this.fileService.getZip(shareId);
res.set({
"Content-Type": "application/zip",
"Content-Disposition": contentDisposition(`${shareId}.zip`),
});
return new StreamableFile(zipStream);
}
@Get(":fileId")
@UseGuards(FileSecurityGuard)
async getFile(
@Res({ passthrough: true }) res: Response,
@Param("shareId") shareId: string,
@Param("fileId") fileId: string,
@Query("download") download = "true",
) {
const file = await this.fileService.get(shareId, fileId);
const headers = {
"Content-Type":
mime?.lookup?.(file.metaData.name) || "application/octet-stream",
"Content-Length": file.metaData.size,
"Content-Security-Policy": "script-src 'none'",
};
if (download === "true") {
headers["Content-Disposition"] = contentDisposition(file.metaData.name);
} else {
headers["Content-Disposition"] = contentDisposition(file.metaData.name, {
type: "inline",
});
}
res.set(headers);
return new StreamableFile(file.file);
}
@Delete(":fileId")
@SkipThrottle()
@UseGuards(ShareOwnerGuard)
async remove(
@Param("fileId") fileId: string,
@Param("shareId") shareId: string,
) {
await this.fileService.remove(shareId, fileId);
}
}