mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-20 02:05:21 -04:00
refactor(media): store filenames, use pre-signed s3/azure URLs, UUIDs
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
parent
4132833b5d
commit
157a0fe278
47 changed files with 869 additions and 389 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -100,28 +100,33 @@ export class MediaController {
|
|||
'uploadMedia',
|
||||
);
|
||||
}
|
||||
const upload = await this.mediaService.saveFile(file.buffer, user, note);
|
||||
const upload = await this.mediaService.saveFile(
|
||||
file.originalname,
|
||||
file.buffer,
|
||||
user,
|
||||
note,
|
||||
);
|
||||
return await this.mediaService.toMediaUploadDto(upload);
|
||||
}
|
||||
|
||||
@Get(':filename')
|
||||
@OpenApi(404, 500)
|
||||
@Get(':uuid')
|
||||
@OpenApi(200, 404, 500)
|
||||
async getMedia(
|
||||
@Param('filename') filename: string,
|
||||
@Param('uuid') uuid: string,
|
||||
@Res() response: Response,
|
||||
): Promise<void> {
|
||||
const mediaUpload = await this.mediaService.findUploadByFilename(filename);
|
||||
const targetUrl = mediaUpload.fileUrl;
|
||||
response.redirect(targetUrl);
|
||||
const mediaUpload = await this.mediaService.findUploadByUuid(uuid);
|
||||
const dto = await this.mediaService.toMediaUploadDto(mediaUpload);
|
||||
response.send(dto);
|
||||
}
|
||||
|
||||
@Delete(':filename')
|
||||
@Delete(':uuid')
|
||||
@OpenApi(204, 403, 404, 500)
|
||||
async deleteMedia(
|
||||
@RequestUser() user: User,
|
||||
@Param('filename') filename: string,
|
||||
@Param('uuid') uuid: string,
|
||||
): Promise<void> {
|
||||
const mediaUpload = await this.mediaService.findUploadByFilename(filename);
|
||||
const mediaUpload = await this.mediaService.findUploadByUuid(uuid);
|
||||
if (
|
||||
await this.permissionsService.checkMediaDeletePermission(
|
||||
user,
|
||||
|
@ -129,18 +134,18 @@ export class MediaController {
|
|||
)
|
||||
) {
|
||||
this.logger.debug(
|
||||
`Deleting '${filename}' for user '${user.username}'`,
|
||||
`Deleting '${uuid}' for user '${user.username}'`,
|
||||
'deleteMedia',
|
||||
);
|
||||
await this.mediaService.deleteFile(mediaUpload);
|
||||
} else {
|
||||
this.logger.warn(
|
||||
`${user.username} tried to delete '${filename}', but is not the owner of upload or connected note`,
|
||||
`${user.username} tried to delete '${uuid}', but is not the owner of upload or connected note`,
|
||||
'deleteMedia',
|
||||
);
|
||||
const mediaUploadNote = await mediaUpload.note;
|
||||
throw new PermissionError(
|
||||
`Neither file '${filename}' nor note '${
|
||||
`Neither file '${uuid}' nor note '${
|
||||
mediaUploadNote?.publicId ?? 'unknown'
|
||||
}'is owned by '${user.username}'`,
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -100,28 +100,33 @@ export class MediaController {
|
|||
`Received filename '${file.originalname}' for note '${note.publicId}' from user '${user.username}'`,
|
||||
'uploadMedia',
|
||||
);
|
||||
const upload = await this.mediaService.saveFile(file.buffer, user, note);
|
||||
const upload = await this.mediaService.saveFile(
|
||||
file.originalname,
|
||||
file.buffer,
|
||||
user,
|
||||
note,
|
||||
);
|
||||
return await this.mediaService.toMediaUploadDto(upload);
|
||||
}
|
||||
|
||||
@Get(':filename')
|
||||
@OpenApi(404, 500)
|
||||
@Get(':uuid')
|
||||
@OpenApi(200, 404, 500)
|
||||
async getMedia(
|
||||
@Param('filename') filename: string,
|
||||
@Param('uuid') uuid: string,
|
||||
@Res() response: Response,
|
||||
): Promise<void> {
|
||||
const mediaUpload = await this.mediaService.findUploadByFilename(filename);
|
||||
const targetUrl = mediaUpload.fileUrl;
|
||||
response.redirect(targetUrl);
|
||||
const mediaUpload = await this.mediaService.findUploadByUuid(uuid);
|
||||
const dto = await this.mediaService.toMediaUploadDto(mediaUpload);
|
||||
response.send(dto);
|
||||
}
|
||||
|
||||
@Delete(':filename')
|
||||
@Delete(':uuid')
|
||||
@OpenApi(204, 403, 404, 500)
|
||||
async deleteMedia(
|
||||
@RequestUser() user: User,
|
||||
@Param('filename') filename: string,
|
||||
@Param('uuid') uuid: string,
|
||||
): Promise<void> {
|
||||
const mediaUpload = await this.mediaService.findUploadByFilename(filename);
|
||||
const mediaUpload = await this.mediaService.findUploadByUuid(uuid);
|
||||
if (
|
||||
await this.permissionsService.checkMediaDeletePermission(
|
||||
user,
|
||||
|
@ -129,18 +134,18 @@ export class MediaController {
|
|||
)
|
||||
) {
|
||||
this.logger.debug(
|
||||
`Deleting '${filename}' for user '${user.username}'`,
|
||||
`Deleting '${uuid}' for user '${user.username}'`,
|
||||
'deleteMedia',
|
||||
);
|
||||
await this.mediaService.deleteFile(mediaUpload);
|
||||
} else {
|
||||
this.logger.warn(
|
||||
`${user.username} tried to delete '${filename}', but is not the owner of upload or connected note`,
|
||||
`${user.username} tried to delete '${uuid}', but is not the owner of upload or connected note`,
|
||||
'deleteMedia',
|
||||
);
|
||||
const mediaUploadNote = await mediaUpload.note;
|
||||
throw new PermissionError(
|
||||
`Neither file '${filename}' nor note '${
|
||||
`Neither file '${uuid}' nor note '${
|
||||
mediaUploadNote?.publicId ?? 'unknown'
|
||||
}'is owned by '${user.username}'`,
|
||||
);
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export const okDescription = 'This request was successful';
|
||||
export const foundDescription =
|
||||
'The requested resource was found at another URL';
|
||||
export const createdDescription =
|
||||
'The requested resource was successfully created';
|
||||
export const noContentDescription =
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
@ -8,6 +8,7 @@ import {
|
|||
ApiBadRequestResponse,
|
||||
ApiConflictResponse,
|
||||
ApiCreatedResponse,
|
||||
ApiFoundResponse,
|
||||
ApiInternalServerErrorResponse,
|
||||
ApiNoContentResponse,
|
||||
ApiNotFoundResponse,
|
||||
|
@ -21,6 +22,7 @@ import {
|
|||
badRequestDescription,
|
||||
conflictDescription,
|
||||
createdDescription,
|
||||
foundDescription,
|
||||
internalServerErrorDescription,
|
||||
noContentDescription,
|
||||
notFoundDescription,
|
||||
|
@ -33,6 +35,7 @@ export type HttpStatusCodes =
|
|||
| 200
|
||||
| 201
|
||||
| 204
|
||||
| 302
|
||||
| 400
|
||||
| 401
|
||||
| 403
|
||||
|
@ -130,6 +133,14 @@ export const OpenApi = (
|
|||
HttpCode(204),
|
||||
);
|
||||
break;
|
||||
case 302:
|
||||
decoratorsToApply.push(
|
||||
ApiFoundResponse({
|
||||
description: description ?? foundDescription,
|
||||
}),
|
||||
HttpCode(302),
|
||||
);
|
||||
break;
|
||||
case 400:
|
||||
decoratorsToApply.push(
|
||||
ApiBadRequestResponse({
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue