Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[iOS#배포] 0.1.1 버전 배포 #247

Merged
merged 17 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,006 changes: 1,983 additions & 23 deletions BE/package-lock.json

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions BE/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.456.0",
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.1.1",
"@nestjs/core": "^10.0.0",
Expand All @@ -32,24 +33,35 @@
"@nestjs/typeorm": "^10.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"ioredis": "^5.3.2",
"multer": "^1.4.5-lts.1",
"mysql": "^2.18.1",
"nest-winston": "^1.9.4",
"passport": "^0.6.0",
"passport-google-oauth20": "^2.0.0",
"redis": "^4.6.11",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.8.1",
"sharp": "^0.32.6",
"typeorm": "^0.3.17",
"uuid": "^9.0.1",
"winston": "^3.11.0"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/express": "^4.17.17",
"@types/ioredis": "^5.0.0",
"@types/jest": "^29.5.2",
"@types/multer": "^1.4.11",
"@types/multer-s3": "^3.0.3",
"@types/node": "^20.3.1",
"@types/passport-google-oauth20": "^2.0.14",
"@types/redis": "^4.0.11",
"@types/sharp": "^0.32.0",
"@types/supertest": "^2.0.12",
"@types/uuid": "^9.0.7",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"eslint": "^8.42.0",
Expand Down
12 changes: 9 additions & 3 deletions BE/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Module } from '@nestjs/common';
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
Expand All @@ -14,6 +14,8 @@ import { PassportModule } from '@nestjs/passport';
import { AuthModule } from './auth/auth.module';
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';
import { Mates } from './mates/mates.entity';
import { LoggingMiddleware } from './common/logging.middleware';

@Module({
imports: [
Expand All @@ -33,7 +35,7 @@ import { join } from 'path';
username: config.get<string>('DATABASE_USERNAME'),
password: config.get<string>('DATABASE_PASSWORD'),
database: config.get<string>('DATABASE_NAME'),
entities: [StudyLogs, Categories, UsersModel],
entities: [StudyLogs, Categories, UsersModel, Mates],
synchronize: true,
}),
inject: [ConfigService],
Expand All @@ -48,4 +50,8 @@ import { join } from 'path';
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggingMiddleware).forRoutes('*');
}
}
55 changes: 51 additions & 4 deletions BE/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { AuthService } from './auth.service';

import {
Controller,
Get,
Expand All @@ -9,13 +8,17 @@ import {
Post,
Body,
Patch,
UseInterceptors,
UploadedFile,
BadRequestException,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Response } from 'express';
import { AccessTokenGuard } from './guard/bearer-token.guard';
import { User } from 'src/users/decorator/user.decorator';
import {
ApiBearerAuth,
ApiConsumes,
ApiExcludeEndpoint,
ApiOperation,
ApiResponse,
Expand All @@ -24,13 +27,18 @@ import {
import { UsersService } from 'src/users/users.service';
import { UpdateUserDto } from 'src/users/dto/update-user.dto';
import { UsersModel } from 'src/users/entity/users.entity';
import { FileInterceptor } from '@nestjs/platform-express';
import { ConfigService } from '@nestjs/config';
import * as path from 'path';
import { ENV } from 'src/common/const/env-keys.const';

@ApiTags('로그인 페이지')
@Controller('auth')
export class AuthController {
constructor(
private readonly authService: AuthService,
private readonly usersService: UsersService,
private readonly configService: ConfigService,
) {}

@Get('google')
Expand Down Expand Up @@ -60,15 +68,54 @@ export class AuthController {

@Patch('info')
@UseGuards(AccessTokenGuard)
@UseInterceptors(FileInterceptor('image'))
@ApiOperation({ summary: '유저 정보 설정 (완)' })
@ApiConsumes('multipart/form-data')
@ApiResponse({ status: 200, description: '프로필 변경 성공' })
@ApiResponse({ status: 400, description: '잘못된 요청' })
@ApiResponse({ status: 401, description: '인증 실패' })
@ApiBearerAuth()
patchUser(
async patchUser(
@User('id') user_id: number,
@Body() user: UpdateUserDto,
): Promise<UsersModel> {
return this.usersService.updateUser(user_id, user);
@UploadedFile() file: Express.Multer.File,
): Promise<any> {
const isNomal = await this.usersService.isNormalImage(file);
if (!isNomal) {
throw new BadRequestException('유해한 이미지 입니다!!');
}
const image_url = await this.usersService.s3Upload(file);
const updatedUser = await this.usersService.updateUser(
user_id,
user as UsersModel,
image_url,
);
return {
nickname: updatedUser.nickname,
email: updatedUser.email,
image_url: path.join(
this.configService.get(ENV.CDN_ENDPOINT),
updatedUser.image_url,
),
};
}

@Get('info')
@UseGuards(AccessTokenGuard)
@ApiOperation({ summary: '유저 정보 설정 (완)' })
@ApiResponse({ status: 200, description: '프로필 조회 성공' })
@ApiResponse({ status: 400, description: '잘못된 요청' })
@ApiResponse({ status: 401, description: '인증 실패' })
@ApiBearerAuth()
async getUser(@User('id') user_id: number): Promise<any> {
const user = await this.usersService.findUserById(user_id);
return {
nickname: user.nickname,
email: user.email,
image_url: path.join(
this.configService.get(ENV.CDN_ENDPOINT),
user.image_url ?? 'default.png',
),
};
}
}
8 changes: 7 additions & 1 deletion BE/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import { AuthController } from './auth.controller';
import { UsersModule } from 'src/users/users.module';
import { JwtModule } from '@nestjs/jwt';
import { GoogleStrategy } from './google.strategy';
import { MulterModule } from '@nestjs/platform-express';
import { multerConfig } from 'src/common/multer.config';

@Module({
imports: [
ConfigModule.forRoot(),
JwtModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
Expand All @@ -17,6 +18,11 @@ import { GoogleStrategy } from './google.strategy';
signOptions: { expiresIn: configService.get('JWT_EXPIRES_IN') },
}),
}),
MulterModule.registerAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: multerConfig,
}),
UsersModule,
],
controllers: [AuthController],
Expand Down
5 changes: 2 additions & 3 deletions BE/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UsersModel } from 'src/users/entity/users.entity';
import { UsersService } from 'src/users/users.service';

@Injectable()
Expand Down Expand Up @@ -40,16 +41,14 @@ export class AuthService {
}

public async loginWithGoogle(user) {
console.log(user.email);
const prevUser = await this.usersService.findUserByEmail(user.email);
if (!prevUser) {
const id = user.email.split('@')[0];
const userEntity = {
nickname:
id + Buffer.from(user.email + user.auth_type).toString('base64'),
email: user.email,
image_url: '',
};
} as UsersModel;
const newUser = await this.usersService.createUser(userEntity);
return {
access_token: this.signToken(newUser),
Expand Down
1 change: 0 additions & 1 deletion BE/src/auth/google.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
email: any,
done: VerifyCallback,
): Promise<any> {
console.log(accessToken);
const user = {
email: email.emails[0].value,
};
Expand Down
4 changes: 3 additions & 1 deletion BE/src/categories/categories.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ export class Categories {
})
color_code: string;

@ManyToOne(() => UsersModel, (user) => user.categories)
@ManyToOne(() => UsersModel, (user) => user.categories, {
onDelete: 'CASCADE',
})
@JoinColumn({ name: 'user_id' })
user_id: UsersModel;

Expand Down
8 changes: 8 additions & 0 deletions BE/src/common/const/env-keys.const.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export enum ENV {
IMAGE_ENDPOINT = 'IMAGE_ENDPOINT',
IMAGE_ACCESSKEY = 'IMAGE_ACCESSKEY',
IMAGE_SECRETKEY = 'IMAGE_SECRETKEY',
IMAGE_REGION = 'IMAGE_REGION',
IMAGE_BUCKET = 'IMAGE_BUCKET',
CDN_ENDPOINT = 'CDN_ENDPOINT',
}
26 changes: 12 additions & 14 deletions BE/src/common/logging.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,18 @@ export class LoggingInterceptor implements NestInterceptor {
const response = ctx.getResponse();
const { method, url, body } = request;

this.logger.debug(
`[Request] Method: ${method}, URL: ${url}, Body: ${JSON.stringify(body)}`,
this.logger.debug(`[Request] ${method} ${url} \n ${JSON.stringify(body)}`);
return next.handle().pipe(
tap((body) => {
const requestToResponse: `${number}ms` = `${
Date.now() - request.now
}ms`;
this.logger.debug(
`[Response] ${
response.statusCode
} ${requestToResponse} \n ${JSON.stringify(body)} \n`,
);
}),
);

return next
.handle()
.pipe(
tap((body) =>
this.logger.debug(
`[Response] Status: ${response.statusCode}, Body: ${JSON.stringify(
body,
)}`,
),
),
);
}
}
10 changes: 10 additions & 0 deletions BE/src/common/logging.middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Injectable, NestMiddleware } from '@nestjs/common';
import { NextFunction } from 'express';

@Injectable()
export class LoggingMiddleware implements NestMiddleware {
use(req: Request & { now: number }, res: Response, next: NextFunction) {
req.now = Date.now();
next();
}
}
26 changes: 26 additions & 0 deletions BE/src/common/multer.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as path from 'path';
import { BadRequestException } from '@nestjs/common';
import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface';
import * as multer from 'multer';

export const multerConfig = (): MulterOptions => {
const storage = multer.memoryStorage();
const fileFilter = (req, file, callback) => {
const ext = path.extname(file.originalname);
if (ext !== '.jpg' && ext !== '.jpeg' && ext !== '.png') {
return callback(
new BadRequestException('jpg/jpeg/png 파일만 업로드 가능합니다!'),
false,
);
}
return callback(null, true);
};

const limits = { fileSize: 1024 * 1024 * 10 }; //10MB

return {
storage,
fileFilter,
limits,
};
};
22 changes: 22 additions & 0 deletions BE/src/common/redis.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Injectable } from '@nestjs/common';
import { RedisClientType, createClient } from 'redis';

@Injectable()
export class RedisService {
private client: RedisClientType;
constructor() {
this.client = createClient();
this.client.connect();
}
set(key: string, value: string) {
this.client.set(key, value);
}

get(key: string): Promise<string | null> {
return this.client.get(key);
}

async del(key: string): Promise<void> {
await this.client.del(key);
}
}
22 changes: 22 additions & 0 deletions BE/src/common/response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ApiProperty } from '@nestjs/swagger';

export class ResponseDto {
@ApiProperty({
type: 'number',
example: 200,
description: '상태 코드',
})
statusCode;

@ApiProperty({
type: 'string',
example: '요청이 정상적으로 처리되었습니다.',
description: '메세지',
})
message;

constructor(statusCode: number, message: string) {
this.statusCode = statusCode;
this.message = message;
}
}
Loading