diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..937b84a --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.ts text eol=lf \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 895f82a..930e5c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.2.1", "@nestjs/core": "^10.0.0", + "@nestjs/jwt": "^10.2.0", "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.3.1", "@types/xlsx-populate": "github:JanLoebel/types-xlsx-populate", @@ -1888,6 +1889,18 @@ } } }, + "node_modules/@nestjs/jwt": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.2.0.tgz", + "integrity": "sha512-x8cG90SURkEiLOehNaN2aRlotxT0KZESUliOPKKnjWiyJOcWurkF3w345WOX0P4MgFzUjGoZ1Sy0aZnxeihT0g==", + "dependencies": { + "@types/jsonwebtoken": "9.0.5", + "jsonwebtoken": "9.0.2" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, "node_modules/@nestjs/mapped-types": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.0.5.tgz", @@ -2416,6 +2429,14 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", + "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/methods": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", @@ -3730,6 +3751,11 @@ "node": "*" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -4549,6 +4575,14 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -7111,6 +7145,27 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, "node_modules/jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -7122,6 +7177,25 @@ "setimmediate": "^1.0.5" } }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/kareem": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.0.tgz", @@ -7264,6 +7338,11 @@ "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", @@ -7279,16 +7358,31 @@ "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==" }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, "node_modules/lodash.isnil": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==" }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.isundefined": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", @@ -7306,6 +7400,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/lodash.reduce": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", diff --git a/package.json b/package.json index 5020e89..42b2ea6 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@nestjs/common": "^10.0.0", "@nestjs/config": "^3.2.1", "@nestjs/core": "^10.0.0", + "@nestjs/jwt": "^10.2.0", "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.3.1", "@types/xlsx-populate": "github:JanLoebel/types-xlsx-populate", diff --git a/scripts/git-hooks/pre-commit b/scripts/git-hooks/pre-commit new file mode 100644 index 0000000..08058e5 --- /dev/null +++ b/scripts/git-hooks/pre-commit @@ -0,0 +1,2 @@ +#!/usr/bin/env sh +npm run lint diff --git a/src/app.module.ts b/src/app.module.ts index 26b6253..a668e9d 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,9 +1,7 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; -import { APP_FILTER } from '@nestjs/core'; import { AuthModule } from './auth/auth.module'; -import { AllExceptionsFilter } from './common/filters/all-exception.filter'; import { DebugModule } from './debug/debug.module'; import { EventModule } from './event/event.module'; import { GroupModule } from './group/group.module'; @@ -23,10 +21,10 @@ import { UserModule } from './user/user.module'; UserModule, ], providers: [ - { - provide: APP_FILTER, - useClass: AllExceptionsFilter, - }, + // { + // provide: APP_FILTER, + // useClass: AllExceptionsFilter, + // }, ], }) export class AppModule {} diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index 2ca9198..56791b9 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -29,6 +29,12 @@ export class AuthController { return this.authService.kakaoToken(code); } + @Get('kakao/token/info') + @ApiQuery({ name: 'kakaoToken', required: true }) + async kakaoTokenInfo(@Query('kakaoToken') kakaoToken: string) { + return this.authService.kakaoTokenInfo(kakaoToken); + } + @Post('kakao/login') @ApiQuery({ name: 'kakaoToken', required: true }) async kakaoLogin(@Query('kakaoToken') kakaoToken: string) { @@ -46,4 +52,15 @@ export class AuthController { async kakaoUnlink(@Query('kakaoToken') kakaoToken: string) { return this.authService.kakaoUnlink(kakaoToken); } + + @Get('login') + @ApiQuery({ name: 'kakaoToken', required: true }) + async login(@Query('kakaoToken') kakaoToken: string) { + return this.authService.login(kakaoToken); + } + + @Get('signout') + async signout(@Query('kakaoToken') kakaoToken: string) { + return this.authService.signout(kakaoToken); + } } diff --git a/src/auth/auth.guard.ts b/src/auth/auth.guard.ts new file mode 100644 index 0000000..75df5e3 --- /dev/null +++ b/src/auth/auth.guard.ts @@ -0,0 +1,36 @@ +import { + CanActivate, + ExecutionContext, + Injectable, + UnauthorizedException, +} from '@nestjs/common'; +import { JwtService } from '@nestjs/jwt'; +import { Request } from 'express'; + +import { jwtConstants } from './constants'; + +@Injectable() +export class AuthGuard implements CanActivate { + constructor(private jwtService: JwtService) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + const token = this.extractTokenFromHeader(request); + if (!token) { + throw new UnauthorizedException(); + } + try { + request['user'] = await this.jwtService.verifyAsync(token, { + secret: jwtConstants.secret, + }); + } catch { + throw new UnauthorizedException(); + } + return true; + } + + private extractTokenFromHeader(request: Request): string | undefined { + const [type, token] = request.headers.authorization?.split(' ') ?? []; + return type === 'Bearer' ? token : undefined; + } +} diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index 0848f97..79aeab6 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -1,12 +1,24 @@ import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; +import { JwtModule } from '@nestjs/jwt'; +import { UserModule } from '../user/user.module'; import { AuthController } from './auth.controller'; import { AuthService } from './auth.service'; +import { jwtConstants } from './constants'; @Module({ - imports: [ConfigModule, HttpModule], + imports: [ + ConfigModule, + HttpModule, + UserModule, + JwtModule.register({ + global: true, + secret: jwtConstants.secret, + signOptions: { expiresIn: '60s' }, + }), + ], controllers: [AuthController], providers: [AuthService], }) diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index a70becd..b80e2cf 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -1,13 +1,18 @@ import { HttpService } from '@nestjs/axios'; import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; +import { JwtService } from '@nestjs/jwt'; import { firstValueFrom } from 'rxjs'; +import { UserService } from '../user/user.service'; + @Injectable() export class AuthService { constructor( private readonly configService: ConfigService, private readonly httpService: HttpService, + private readonly userService: UserService, + private readonly jwtService: JwtService, ) {} kakaoCallback( @@ -40,6 +45,19 @@ export class AuthService { return data['access_token']; } + async kakaoTokenInfo(kakaoToken: string) { + const { data } = await firstValueFrom( + this.httpService.get('https://kapi.kakao.com/v1/user/access_token_info', { + headers: { + Authorization: `Bearer ${kakaoToken}`, + 'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8', + }, + }), + ); + + return data; + } + async kakaoLogin(kakaoToken: string) { const { data } = await firstValueFrom( this.httpService.get('https://kapi.kakao.com/v2/user/me', { @@ -86,4 +104,35 @@ export class AuthService { return data; } + + async login(kakaoToken: string) { + const kakaoInfo = await this.kakaoLogin(kakaoToken); + + const kakaoId = kakaoInfo.id; + const name = kakaoInfo.kakao_account.profile.nickname; + const email = kakaoInfo.kakao_account.email; + + let user = await this.userService.findOneByKakaoId(kakaoId); + if (!user) { + user = await this.userService.create({ + kakaoId, + name, + email, + }); + } + + const payload = { sub: user.id, name: user.name, iat: Date.now() }; + return { + access_token: await this.jwtService.signAsync(payload), + }; + } + + async signout(kakaoToken: string) { + const { kakaoId } = await this.kakaoTokenInfo(kakaoToken); + + this.userService.removeOneByKakaoId(kakaoId); + await this.kakaoUnlink(kakaoToken); + + return { message: 'Successfully signed out' }; + } } diff --git a/src/auth/constants.ts b/src/auth/constants.ts new file mode 100644 index 0000000..37903da --- /dev/null +++ b/src/auth/constants.ts @@ -0,0 +1,7 @@ +import * as dotenv from 'dotenv'; +import * as process from 'node:process'; + +dotenv.config(); +export const jwtConstants = { + secret: process.env.JWT_SECRET, +}; diff --git a/src/common/configs/swagger.config.ts b/src/common/configs/swagger.config.ts index c2df57c..b7d993c 100644 --- a/src/common/configs/swagger.config.ts +++ b/src/common/configs/swagger.config.ts @@ -60,6 +60,13 @@ const authSwaggerConfig = (app: INestApplication) => { }, }, }) + .addBearerAuth( + { + type: 'http', + scheme: 'bearer', + }, + 'Authorization', + ) .build(); const document = SwaggerModule.createDocument(app, config, { diff --git a/src/common/filters/all-exception.filter.ts b/src/common/filters/all-exception.filter.ts index ba101a9..a31195d 100644 --- a/src/common/filters/all-exception.filter.ts +++ b/src/common/filters/all-exception.filter.ts @@ -34,13 +34,9 @@ export class AllExceptionsFilter implements ExceptionFilter { const timestamp = new Date().toISOString(); webhook.error( - path, + `[${request.method}] ${path}`, timestamp, - 'request body: ' + - request.body.toString() + - '\n' + - 'error message: ' + - message.toString(), + 'error message: ' + message.toString(), ); httpAdapter.reply( diff --git a/src/group/group.service.ts b/src/group/group.service.ts index dbcbcfd..22f743d 100644 --- a/src/group/group.service.ts +++ b/src/group/group.service.ts @@ -124,9 +124,16 @@ export class GroupService { } async getMembers(groupId: string) { - return this.groupRepository.findOne(groupId).then((group) => { - return this.getAllMembers(group.members); - }); + const group = await this.groupRepository.findOne(groupId); + const members = await this.getAllMembers(group.members); + const memberInfo = []; + if (group.members.length > 0) { + for (const info in members[0].memberInfo) { + memberInfo.push(info); + } + } + + return { memberInfo, members }; } async getEvents(groupId: string) { diff --git a/src/user/dto/create-user.dto.ts b/src/user/dto/create-user.dto.ts index 0311be1..d3dac81 100644 --- a/src/user/dto/create-user.dto.ts +++ b/src/user/dto/create-user.dto.ts @@ -1 +1,21 @@ -export class CreateUserDto {} +import { ApiProperty } from '@nestjs/swagger'; + +export class CreateUserDto { + @ApiProperty({ + example: 1234567890, + description: '사용자의 카카오 ID', + }) + readonly kakaoId: number; + + @ApiProperty({ + example: '박근형', + description: '사용자의 이름', + }) + readonly name: string; + + @ApiProperty({ + example: 'w8385@kakao.com', + description: '사용자의 이메일', + }) + readonly email: string; +} diff --git a/src/user/entities/user.entity.ts b/src/user/entities/user.entity.ts index 4f82c14..c20f5c4 100644 --- a/src/user/entities/user.entity.ts +++ b/src/user/entities/user.entity.ts @@ -1 +1,6 @@ -export class User {} +export class User { + id: string; + kakaoId: number; + name: string; + email: string; +} diff --git a/src/user/interfaces/user.interfaces.ts b/src/user/interfaces/user.interfaces.ts new file mode 100644 index 0000000..6bb250f --- /dev/null +++ b/src/user/interfaces/user.interfaces.ts @@ -0,0 +1,5 @@ +export interface UserInterface extends Document { + kakaoId: number; + name: string; + email: string; +} diff --git a/src/user/schemas/user.schema.ts b/src/user/schemas/user.schema.ts new file mode 100644 index 0000000..c10ded3 --- /dev/null +++ b/src/user/schemas/user.schema.ts @@ -0,0 +1,7 @@ +import { Schema } from 'mongoose'; + +export const UserSchema = new Schema({ + kakaoId: Number, + name: String, + email: String, +}); diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index f975628..2e35fb6 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -6,9 +6,12 @@ import { Param, Patch, Post, + Request, + UseGuards, } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; +import { AuthGuard } from '../auth/auth.guard'; import { CreateUserDto } from './dto/create-user.dto'; import { UpdateUserDto } from './dto/update-user.dto'; import { UserService } from './user.service'; @@ -28,18 +31,38 @@ export class UserController { return this.userService.findAll(); } - @Get(':id') - findOne(@Param('id') id: string) { - return this.userService.findOne(+id); + @UseGuards(AuthGuard) + @ApiBearerAuth('Authorization') + @Get('me') + me(@Request() req: any) { + return req.user; } - @Patch(':id') - update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) { - return this.userService.update(+id, updateUserDto); + @Get(':userId') + findOne(@Param('userId') userId: string) { + return this.userService.findOne(userId); } - @Delete(':id') - remove(@Param('id') id: string) { - return this.userService.remove(+id); + @Get('kakao/:kakaoId') + findOneByKakaoId(@Param('kakaoId') kakaoId: string) { + return this.userService.findOneByKakaoId(kakaoId); + } + + @Patch(':userId') + update( + @Param('userId') userId: string, + @Body() updateUserDto: UpdateUserDto, + ) { + return this.userService.update(userId, updateUserDto); + } + + @Delete() + removeAll() { + return this.userService.removeAll(); + } + + @Delete(':userId') + remove(@Param('userId') userId: string) { + return this.userService.remove(userId); } } diff --git a/src/user/user.module.ts b/src/user/user.module.ts index c59c53a..3b7327e 100644 --- a/src/user/user.module.ts +++ b/src/user/user.module.ts @@ -1,10 +1,15 @@ import { Module } from '@nestjs/common'; +import { DatabaseModule } from '../database/database.module'; import { UserController } from './user.controller'; +import { userProviders } from './user.providers'; +import { UserRepository } from './user.repository'; import { UserService } from './user.service'; @Module({ + imports: [DatabaseModule], controllers: [UserController], - providers: [UserService], + providers: [UserService, ...userProviders, UserRepository], + exports: [UserService], }) export class UserModule {} diff --git a/src/user/user.providers.ts b/src/user/user.providers.ts new file mode 100644 index 0000000..a8b729d --- /dev/null +++ b/src/user/user.providers.ts @@ -0,0 +1,12 @@ +import { Connection } from 'mongoose'; + +import { UserSchema } from './schemas/user.schema'; + +export const userProviders = [ + { + provide: 'USER_MODEL', + useFactory: (connection: Connection) => + connection.model('User', UserSchema), + inject: ['MONGODB_CONNECTION'], + }, +]; diff --git a/src/user/user.repository.ts b/src/user/user.repository.ts new file mode 100644 index 0000000..53a36d5 --- /dev/null +++ b/src/user/user.repository.ts @@ -0,0 +1,48 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { Model } from 'mongoose'; + +import { CreateUserDto } from './dto/create-user.dto'; +import { User } from './entities/user.entity'; +import { UserInterface } from './interfaces/user.interfaces'; + +@Injectable() +export class UserRepository { + constructor( + @Inject('USER_MODEL') + private readonly userModel: Model, + ) {} + + create(createUserDto: CreateUserDto) { + return this.userModel.create(createUserDto) as Promise; + } + + findAll() { + return this.userModel.find().exec() as Promise; + } + + findOne(userId: string): Promise { + return this.userModel.findById(userId).exec() as Promise; + } + + findOneByKakaoId(kakaoId: string) { + return this.userModel.findOne({ kakaoId }).exec() as Promise; + } + + update(userId: string, updateUserDto: CreateUserDto) { + return this.userModel + .findByIdAndUpdate(userId, updateUserDto, { new: true }) + .exec() as Promise; + } + + deleteAll() { + this.userModel.deleteMany().exec().then(); + } + + delete(userId: string) { + this.userModel.findByIdAndDelete(userId).exec().then(); + } + + deleteOneByKakaoId(kakaoId: string) { + this.userModel.findOneAndDelete({ kakaoId }).exec().then(); + } +} diff --git a/src/user/user.service.ts b/src/user/user.service.ts index af873a0..281dde8 100644 --- a/src/user/user.service.ts +++ b/src/user/user.service.ts @@ -2,26 +2,42 @@ import { Injectable } from '@nestjs/common'; import { CreateUserDto } from './dto/create-user.dto'; import { UpdateUserDto } from './dto/update-user.dto'; +import { User } from './entities/user.entity'; +import { UserRepository } from './user.repository'; @Injectable() export class UserService { - create(createUserDto: CreateUserDto) { - return createUserDto; + constructor(private readonly userRepository: UserRepository) {} + + async create(createUserDto: CreateUserDto) { + return await this.userRepository.create(createUserDto); + } + + async findAll() { + return await this.userRepository.findAll(); + } + + async findOne(userId: string): Promise { + return await this.userRepository.findOne(userId); + } + + async findOneByKakaoId(kakaoId: string) { + return await this.userRepository.findOneByKakaoId(kakaoId); } - findAll() { - return `This action returns all user`; + async update(userId: string, updateUserDto: UpdateUserDto) { + return { userId, updateUserDto }; } - findOne(id: number) { - return `This action returns a #${id} user`; + removeAll() { + return this.userRepository.deleteAll(); } - update(id: number, updateUserDto: UpdateUserDto) { - return { id, updateUserDto }; + remove(userId: string) { + return this.userRepository.delete(userId); } - remove(id: number) { - return `This action removes a #${id} user`; + removeOneByKakaoId(kakaoId: string) { + return this.userRepository.deleteOneByKakaoId(kakaoId); } }