Compare commits

...

4 Commits

Author SHA1 Message Date
1fc9185afc
Refactor storage and file deletion logic
Adjusted the indentation and formatting across several files for better readability. Enhanced the file deletion logic to handle cases where a file is found in the database but not in storage, and to delete only from the database when there are multiple references.
2024-10-14 13:56:43 +02:00
8686f0c27b
Add file deletion method in storage service
Implemented a new method to delete files from the storage by checksum and extension. This method logs the deletion process and throws a NotFoundException if the file cannot be found. Updated dependencies to include 'rm' from 'node:fs/promises'.
2024-10-14 12:10:52 +02:00
79b2dec9e9
Add file deletion endpoint with admin guard
Introduced the deleteFile method in files.service.ts to handle file deletion, considering duplicates. Also updated files.controller.ts to create a new DELETE endpoint protected by the AdminGuard to ensure only admins can delete files.
2024-10-14 12:04:12 +02:00
3e6b2dc2bc
Add TODO comment for future file deletion implementation
A TODO comment was added to the files.service.ts to indicate the need for a file deletion feature. This serves as a placeholder for future development to ensure the feature is addressed.
2024-10-14 11:58:30 +02:00
4 changed files with 107 additions and 14 deletions

View File

@ -73,7 +73,10 @@ export const FilesTable = pgTable("files", {
})
.notNull(),
groupId: p.uuid("group_id").default(null).references(() => FilesGroupTable.uuid),
groupId: p
.uuid("group_id")
.default(null)
.references(() => FilesGroupTable.uuid),
fileSize: p.integer("file_size").notNull(),

View File

@ -3,6 +3,7 @@ import {
BadRequestException,
Controller,
DefaultValuePipe,
Delete,
Get,
HttpCode,
HttpStatus,
@ -17,7 +18,7 @@ import {
StreamableFile,
UseGuards,
} from "@nestjs/common";
import { InsertAdminState } from "../auth/auth.guard";
import { AdminGuard, InsertAdminState } from "../auth/auth.guard";
import { FilesService } from "./files.service";
@Controller("files")
@ -121,4 +122,11 @@ export class FilesController {
async getFile(@Param("fileId") fileId: string) {
return await this.filesService.get(fileId);
}
@UseGuards(AdminGuard)
@HttpCode(HttpStatus.OK)
@Delete(":fileId")
async deleteFile(@Param("fileId") fileId: string) {
return await this.filesService.deleteFile(fileId);
}
}

View File

@ -67,6 +67,54 @@ export class FilesService {
});
}
/**
* Deletes a file from the storage and/or the database based on the provided file ID.
*
* @param {string} fileId - The unique identifier of the file to delete.
* @return {Promise<void>} A promise that resolves when the deletion process is complete.
* @throws {NotFoundException} If the file is not found in the database.
*/
public async deleteFile(fileId: string) {
//get checksum for fileId
const currentFileInDb = await this.database
.use()
.select()
.from(FilesTable)
.where(eq(FilesTable.uuid, fileId))
.prepare("findFileById")
.execute();
if (currentFileInDb.length === 0)
throw new NotFoundException("File not found in database");
//check if multiple entry for checksum
const sameFileInStorage = await this.database
.use()
.select()
.from(FilesTable)
.where(eq(FilesTable.checksum, currentFileInDb[0].checksum));
if (sameFileInStorage.length > 1) {
//if that the case then only remove the entry in database relative to fileId.
await this.database
.use()
.delete(FilesTable)
.where(eq(FilesTable.uuid, fileId))
.execute();
}
if (sameFileInStorage.length === 1) {
//if there is one only entry then remove the file from the storage and the database.
await this.database
.use()
.delete(FilesTable)
.where(eq(FilesTable.uuid, fileId))
.execute();
await this.storage.delete(
currentFileInDb[0].checksum,
currentFileInDb[0].extension,
currentFileInDb[0].isDocumentation,
);
}
}
/**
* Searches for files in the database using the specified search field, limit, and offset.
*
@ -172,13 +220,14 @@ export class FilesService {
})
.returning();
if (groupExists[0].uuid) {
await this.database.use()
await this.database
.use()
.update(FilesTable)
// @ts-ignore TODO FIX
.set({groupId: groupExists[0].uuid})
.set({ groupId: groupExists[0].uuid })
.where(eq(FilesTable.uuid, inserted[0].uuid))
.prepare("addGroupToFile")
.execute()
.execute();
}
console.log(inserted);

View File

@ -1,6 +1,6 @@
import * as console from "node:console";
import * as crypto from "node:crypto";
import { readFile, writeFile } from "node:fs/promises";
import { readFile, rm, writeFile } from "node:fs/promises";
import { join } from "node:path";
import {
BadRequestException,
@ -59,11 +59,16 @@ export class StorageService {
* @param {string} currentMime - The MIME type to check for presence in all sets.
* @return {boolean} Returns true if the MIME type is found in all sets, otherwise false.
*/
function checkMime(mimesForMachines: Map<string, Set<string>>, currentMime: string): boolean {
function checkMime(
mimesForMachines: Map<string, Set<string>>,
currentMime: string,
): boolean {
let notFoundCount = 0;
for (const mimesForMachine of mimesForMachines) {
const [key, set] = mimesForMachine
if (!set.has(currentMime)) {notFoundCount++}
const [key, set] = mimesForMachine;
if (!set.has(currentMime)) {
notFoundCount++;
}
}
return notFoundCount === 0;
}
@ -73,7 +78,10 @@ export class StorageService {
// Array of MIMEs with possible duplicate field
const _mimes: Array<string> = [];
const machinesMap: Map<string, Set<string>> = new Map<string, Set<string>>()
const machinesMap: Map<string, Set<string>> = new Map<
string,
Set<string>
>();
// Fetching MIMEs for the associated machines
for (const machineId of machineIds) {
console.debug(`Fetching mimeTypes for machine : ${machineId}`);
@ -97,12 +105,12 @@ export class StorageService {
);
console.debug(`Total : ${_allowedMime.length}`);
// Append each MIME of a machine
const tempSet = new Set<string>()
const tempSet = new Set<string>();
for (const allowedMimeElement of _allowedMime) {
tempSet.add(allowedMimeElement.slug)
tempSet.add(allowedMimeElement.slug);
}
machinesMap.set(machineId, tempSet)
tempSet.clear()
machinesMap.set(machineId, tempSet);
tempSet.clear();
}
//Store the MIMEs without duplicate
const mimeSet = new Set(_mimes);
@ -263,4 +271,29 @@ export class StorageService {
throw err;
}
}
/**
* Deletes a file from the storage.
*
* @param {string} checksum - The checksum of the file to delete.
* @param {string} extension - The extension of the file to delete.
* @param {boolean} isDocumentation - A flag indicating whether the file is a documentation file.
* @return {Promise<void>} A promise that resolves when the file is successfully deleted.
* @throws {NotFoundException} Throws an exception if the file cannot be found.
*/
public async delete(
checksum: string,
extension: string,
isDocumentation: boolean,
) {
try {
const fileName = `${isDocumentation ? "doc" : "file"}-${checksum}.${extension.toLowerCase()}`;
console.log(`Deleting file "${fileName}" from storage...`);
await rm(join(process.cwd(), "assets/", fileName));
console.log(`File "${fileName}" deleted successfully.`);
} catch (err) {
console.log("File not found.");
throw new NotFoundException(err);
}
}
}