feat: allow user/edit/:id endpoint to upload avatar

This commit is contained in:
Mohamed Marrouchi 2025-01-06 18:54:02 +01:00
parent 57fcd9b3a3
commit 3721f4365e
2 changed files with 47 additions and 6 deletions

View File

@ -21,11 +21,14 @@ import {
Req, Req,
Session, Session,
UnauthorizedException, UnauthorizedException,
UploadedFile,
UseInterceptors, UseInterceptors,
} from '@nestjs/common'; } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { CsrfCheck } from '@tekuconcept/nestjs-csrf'; import { CsrfCheck } from '@tekuconcept/nestjs-csrf';
import { Request } from 'express'; import { Request } from 'express';
import { Session as ExpressSession } from 'express-session'; import { Session as ExpressSession } from 'express-session';
import { diskStorage, memoryStorage } from 'multer';
import { AttachmentService } from '@/attachment/services/attachment.service'; import { AttachmentService } from '@/attachment/services/attachment.service';
import { config } from '@/config'; import { config } from '@/config';
@ -261,17 +264,54 @@ export class ReadWriteUserController extends ReadOnlyUserController {
* @returns A promise that resolves to the updated user. * @returns A promise that resolves to the updated user.
*/ */
@CsrfCheck(true) @CsrfCheck(true)
@UseInterceptors(
FileInterceptor('avatar', {
limits: {
fileSize: config.parameters.maxUploadSize,
},
storage: (() => {
if (config.parameters.storageMode === 'memory') {
return memoryStorage();
} else {
return diskStorage({});
}
})(),
}),
)
@Patch('edit/:id') @Patch('edit/:id')
async updateOne( async updateOne(
@Req() req: Request, @Req() req: Request,
@Param('id') id: string, @Param('id') id: string,
@Body() userUpdate: UserEditProfileDto, @Body() userUpdate: UserEditProfileDto,
@UploadedFile() avatarFile?: Express.Multer.File,
) { ) {
if (!('id' in req.user && req.user.id) || req.user.id !== id) { if (!('id' in req.user && req.user.id) || req.user.id !== id) {
throw new UnauthorizedException(); throw new ForbiddenException();
} }
const result = await this.userService.updateOne(req.user.id, userUpdate); // Upload Avatar if provided
const avatar = avatarFile
? await this.attachmentService.store(
avatarFile,
{
name: avatarFile.originalname,
size: avatarFile.size,
type: avatarFile.mimetype,
},
config.parameters.avatarDir,
)
: undefined;
const result = await this.userService.updateOne(
req.user.id,
avatar
? {
...userUpdate,
avatar: avatar.id,
}
: userUpdate,
);
if (!result) { if (!result) {
this.logger.warn(`Unable to update User by id ${id}`); this.logger.warn(`Unable to update User by id ${id}`);
throw new NotFoundException(`User with ID ${id} not found`); throw new NotFoundException(`User with ID ${id} not found`);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright © 2024 Hexastack. All rights reserved. * Copyright © 2025 Hexastack. All rights reserved.
* *
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
@ -14,12 +14,12 @@ import {
PartialType, PartialType,
} from '@nestjs/swagger'; } from '@nestjs/swagger';
import { import {
IsEmail,
IsNotEmpty,
IsString,
IsArray, IsArray,
IsBoolean, IsBoolean,
IsEmail,
IsNotEmpty,
IsOptional, IsOptional,
IsString,
} from 'class-validator'; } from 'class-validator';
import { IsObjectId } from '@/utils/validation-rules/is-object-id'; import { IsObjectId } from '@/utils/validation-rules/is-object-id';
@ -66,6 +66,7 @@ export class UserCreateDto {
export class UserEditProfileDto extends OmitType(PartialType(UserCreateDto), [ export class UserEditProfileDto extends OmitType(PartialType(UserCreateDto), [
'username', 'username',
'roles', 'roles',
'avatar',
]) { ]) {
@ApiPropertyOptional({ description: 'User language', type: String }) @ApiPropertyOptional({ description: 'User language', type: String })
@IsOptional() @IsOptional()