diff --git a/src/auth/dto/login.dto.ts b/src/auth/dto/login.dto.ts index 6788d79..03612ca 100644 --- a/src/auth/dto/login.dto.ts +++ b/src/auth/dto/login.dto.ts @@ -7,7 +7,7 @@ export class LoginDto { @IsNotEmpty() @ApiProperty({ description: 'Email', - example: 'eric@test.com', + example: 'eric1@test.com', }) email: string; @@ -15,7 +15,7 @@ export class LoginDto { @IsNotEmpty() @ApiProperty({ description: 'Password', - example: '123456!', + example: 'abc123456789!', }) password: string; } diff --git a/src/aws/aws.s3.ts b/src/aws/aws.s3.ts index 5d954e5..1801945 100644 --- a/src/aws/aws.s3.ts +++ b/src/aws/aws.s3.ts @@ -1,6 +1,6 @@ // src/aws/aws.s3.ts import * as AWS from 'aws-sdk'; -import { Injectable } from '@nestjs/common'; +import { BadRequestException, Injectable } from '@nestjs/common'; @Injectable() export class AwsS3Service { @@ -15,9 +15,14 @@ export class AwsS3Service { // S3 업로드 로직 async uploadFile(file) { + if (!file) { + throw new BadRequestException('No file uploaded'); + } + console.log("2. file in aws.s3.ts: ", file); + const params = { - Bucket: process.env.AWS_BUCKET_NAME || 'aws-s3-local-mingle', // AWS S3 버킷 이름 - Key: `profileimg/${String(Date.now())}`, // 폴더와 파일 이름 + Bucket: process.env.AWS_BUCKET_NAME || 's3-image-local-mingle', // AWS S3 버킷 이름 + Key: `profileImg/${String(Date.now())}`, // 폴더와 파일 이름 Body: file.buffer, // 파일 내용 ContentType: file.mimetype, // 파일 타입 }; @@ -27,6 +32,7 @@ export class AwsS3Service { if (err) { return reject(err); } + console.log("2.1. file in aws.s3.ts: ", file); resolve(data); }); }); diff --git a/src/users/dto/create-user-detail.dto.ts b/src/users/dto/create-user-detail.dto.ts deleted file mode 100644 index 96356b1..0000000 --- a/src/users/dto/create-user-detail.dto.ts +++ /dev/null @@ -1,48 +0,0 @@ -// src/users/dto/create-user-detail.dto.ts - -import { ApiProperty } from '@nestjs/swagger'; -import { IsNotEmpty, IsString, IsOptional } from 'class-validator'; - -/* -// 사용자 정보 모델 -model UserDetail { - userDetailId Int @id @default(autoincrement()) // Primary Key - UserId Int // Foreign Key - nickname String @unique - intro String? - profileImg String? - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - - User User @relation(fields: [UserId], references: [userId]) - - @@map("UserInfo") -} -*/ - -export class CreateUserDetailDto { - @ApiProperty({ - example: 'nickname1', - description: 'The nickname of the user', - }) - @IsNotEmpty() - @IsString() - nickname: string; - - @ApiProperty({ - example: 'Hello, I am user1', - description: 'The introduction of the user', - }) - @IsOptional() - @IsString() - intro?: string; - - @ApiProperty({ - example: 'profile.jpg', - description: 'The profile image of the user', - }) - @IsOptional() - @IsString() - profileImg?: string; -} diff --git a/src/users/dto/create-user.dto.ts b/src/users/dto/create-user.dto.ts index ea102ba..331c4b9 100644 --- a/src/users/dto/create-user.dto.ts +++ b/src/users/dto/create-user.dto.ts @@ -10,45 +10,6 @@ import { Matches, } from 'class-validator'; -// model User { -// userId Int @id @default(autoincrement()) // Primary Key -// email String @unique -// password String -// createdAt DateTime @default(now()) -// updatedAt DateTime @updatedAt - -// UserDetail UserDetail[] -// HostEvents HostEvent[] -// GuestEvents GuestEvent[] - -// @@map("User") -// } - -/* Eric's code -export class CreateUserDto { - @ApiProperty({ - example: 'user1@email.com', - description: 'The email of the user', - }) - @IsNotEmpty() - @IsEmail({}, { message: '이메일 형식이 아닙니다.' }) - email: string; - - @ApiProperty({ - example: 'Password1!', - description: 'The password of the user', - }) - @IsNotEmpty() - @IsString() - @MinLength(8, { message: '패스워드는 최소 8자리 이상이어야 합니다.' }) - @MaxLength(15, { message: '패스워드는 최대 15자리까지 가능합니다.' }) - @Matches(/^(?=.*[a-zA-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,15}$/, { - message: '패스워드는 8-15 글자, 영문/숫자/특수문자가 포함되어야 합니다.', - }) - password: string; -} -*/ - export class CreateUserDto { @IsEmail() @IsNotEmpty() @@ -58,18 +19,6 @@ export class CreateUserDto { }) email: string; - @IsString() - @IsNotEmpty() - @MinLength(8) - @MaxLength(15) - //알파벳 포함 , 숫자 포함 , 특수문자 포함 - @Matches(/^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/) - @ApiProperty({ - description: 'password', - example: 'abc123456789!', - }) - password: string; - @IsString() @IsNotEmpty() @MinLength(2) @@ -89,6 +38,18 @@ export class CreateUserDto { }) intro: string; + @IsString() + @IsNotEmpty() + @MinLength(8) + @MaxLength(15) + //알파벳 포함 , 숫자 포함 , 특수문자 포함 + @Matches(/^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/) + @ApiProperty({ + description: 'password', + example: 'abc123456789!', + }) + password: string; + @IsString() @IsNotEmpty() @ApiProperty({ diff --git a/src/users/dto/update-user-detail.dto.ts b/src/users/dto/update-user-detail.dto.ts deleted file mode 100644 index 787df5b..0000000 --- a/src/users/dto/update-user-detail.dto.ts +++ /dev/null @@ -1,5 +0,0 @@ -// src/users/dto/update-user-detail.dto.ts -import { PartialType } from '@nestjs/swagger'; -import { CreateUserDetailDto } from './create-user-detail.dto'; - -export class UpdateUserDetailDto extends PartialType(CreateUserDetailDto) {} diff --git a/src/users/dto/update-user.dto.ts b/src/users/dto/update-user.dto.ts index 2b846b8..86f4333 100644 --- a/src/users/dto/update-user.dto.ts +++ b/src/users/dto/update-user.dto.ts @@ -1,5 +1,45 @@ // src/users/dto/update-user.dto.ts -import { PartialType } from '@nestjs/swagger'; +import { ApiProperty, PartialType } from '@nestjs/swagger'; import { CreateUserDto } from './create-user.dto'; +import { IsNotEmpty, IsString, Matches, MaxLength, MinLength } from 'class-validator'; -export class UpdateUserDto extends PartialType(CreateUserDto) {} +export class UpdateUserDto { + @IsString() + @IsNotEmpty() + @MinLength(2) + @MaxLength(8) + //영어 또는 한글이 포함 + @Matches(/^(?=.*[A-Za-z가-힣]).*[A-Za-z가-힣0-9]*$/) + @ApiProperty({ + description: 'nickname', + example: '닉네임', + }) + nickname: string; + + @IsString() + @ApiProperty({ + description: 'intro', + example: '안녕하세요', + }) + intro: string; + + @IsString() + @IsNotEmpty() + @MinLength(8) + @MaxLength(15) + //알파벳 포함 , 숫자 포함 , 특수문자 포함 + @Matches(/^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]+$/) + @ApiProperty({ + description: 'password', + example: 'abc123456789!', + }) + password: string; + + @IsString() + @IsNotEmpty() + @ApiProperty({ + description: 'password', + example: 'abc123456789!', + }) + confirmPassword: string; +} diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index d52d873..f24d8f0 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -5,7 +5,7 @@ import { UsersService } from './users.service'; import { CreateUserDto } from './dto/create-user.dto'; import { UpdateUserDto } from './dto/update-user.dto'; import { DeleteUserDto } from './dto/delete-user.dto'; -import { ApiBearerAuth, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiResponse, ApiTags, ApiBody, ApiConsumes } from '@nestjs/swagger'; +import { ApiBearerAuth, ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiResponse, ApiTags, ApiBody, ApiConsumes, ApiProperty } from '@nestjs/swagger'; import { UserEntity } from './entities/user.entity'; import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard'; import { User } from '@prisma/client'; @@ -46,7 +46,7 @@ export class UsersController { const users = await this.usersService.findAll(); if (!users) { throw new NotFoundException('Users does not exist'); - } + } // TODO: HEE's code const userEntity = users.map((user) => new UserEntity(user)); @@ -86,12 +86,14 @@ export class UsersController { @UseGuards(JwtAuthGuard) // passport를 사용하여 인증 확인 @ApiBearerAuth() // Swagger 문서에 Bearer 토큰 인증 추가 @ApiOperation({ summary: '회원 정보 수정' }) + @ApiResponse({ status: 200, description: '회원 정보가 수정되었습니다' }) + @ApiResponse({ status: 400, description: '중복된 닉네임입니다' }) + @ApiResponse({ status: 401, description: '패스워드가 일치하지 않습니다' }) + @ApiResponse({ status: 404, description: '유저 정보가 존재하지 않습니다' }) + async update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) { - const updatedUser = await this.usersService.update(+id, updateUserDto); - if (!updatedUser) { - throw new NotFoundException('User does not exist'); - } - return updatedUser; + await this.usersService.update(+id, updateUserDto); + return {'message' : '회원 정보가 수정되었습니다'}; } // 6. 회원 탈퇴를 한다. @@ -150,17 +152,23 @@ export class UsersController { async updateProfileImage(@Req() req: RequestWithUser, @UploadedFile() file) { const { userId } = req.user; - console.log('updateProfileImage in users.controller.ts - userId:', userId); - console.log('updateProfileImage in users.controller.ts - file:', file); - + console.log("1. file in users.controller.ts:", file); const user = await this.usersService.findOne(userId); - console.log('User:', user); + // console.log('User:', user); if (!user) { throw new NotFoundException('User does not exist'); } + // 이미지를 s3에 업로드한다. const uploadedFile = await this.awsS3Service.uploadFile(file) as { Location: string }; - return this.usersService.updateProfileImage(userId, uploadedFile.Location); + + // s3에 업로드된 이미지 URL을 DB에 저장한다. + const s3ProfileImgURL = await this.usersService.updateProfileImage(userId, uploadedFile.Location); + + return { + 'message': '이미지가 업로드되었습니다', + 'profileImgURL' : s3ProfileImgURL, + } } // 사용자가 관심 등록한 모임 리스트를 조회한다. diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 8a8c5fb..298f6ba 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -88,10 +88,40 @@ export class UsersService { /* FiXME */ // 5. user 정보 수정한다. async update(id: number, updateUserDto: UpdateUserDto) { - return await this.prisma.user.update({ + console.log('updateUserDto in users.service:', updateUserDto) + const { nickname, intro, confirmPassword } = updateUserDto; + + const user = await this.prisma.user.findUnique({ where: { userId: id }, - data: updateUserDto, }); + if (!user) { + throw new BadRequestException('유저 정보가 존재하지 않습니다.'); + } + + // 패스워드, 패스워드 확인 일치 여부 확인 + const isPasswordMatching = await bcrypt.compare(confirmPassword, user.password); + if (!isPasswordMatching) { + throw new BadRequestException('패스워드가 일치하지 않습니다.'); + } + // 중복된 닉네임 확인 + const existingNickname = await this.prisma.userDetail.findUnique({ + where: { nickname }, + }); + if (existingNickname) { + throw new ConflictException('중복된 닉네임입니다.'); + } + + // userdetail page 업데이트 + const updatedUser = await this.prisma.userDetail.update({ + where: { userDetailId: user.userId}, + data: { + intro: intro, + nickname: nickname + }, + }); + + return updatedUser; + } // 6. 회원 탈퇴를 한다. @@ -151,17 +181,16 @@ export class UsersService { where: { UserId: id }, }); - console.log('User Detail:', userDetail); - console.log('User Detail ID:', userDetail.userDetailId); - if (!userDetail) { throw new BadRequestException('회원 상세 정보가 존재하지 않습니다.'); } + console.log("3. profileImg URL in usrs.service", profileImg); // userDetailId를 사용하여 프로필 이미지를 업데이트한다. - return await this.prisma.userDetail.update({ + const updatedProfileImage = await this.prisma.userDetail.update({ where: { userDetailId: userDetail.userDetailId }, data: { profileImg: profileImg }, }); + return updatedProfileImage.profileImg; } }