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

ci: 운영 서버 배포 #91

Merged
merged 52 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
b312cf2
change: change /rotations GET controller to use parameter
sideseal Jan 5, 2024
5b93251
modify: change partial entities to specific dtos
sideseal Jan 5, 2024
8f02863
add: added specific dtos
sideseal Jan 5, 2024
e0f1ccf
docs: add link describing changes of API and DB
Jiwon-Woo Jan 6, 2024
73ff336
feat: add custom Exception and custom ExceptionFilter
Jiwon-Woo Jan 11, 2024
bd7a973
feat: add Interceptor for error logging
Jiwon-Woo Jan 11, 2024
715f39c
feat: apply custom exception filter and interceptor
Jiwon-Woo Jan 11, 2024
2dd8b2d
docs: update readme
Jiwon-Woo Jan 12, 2024
bbc9282
Merge pull request #79 from Together42/67-Update-README
Jiwon-Woo Jan 12, 2024
50191ae
added: add swagger comment in find-all-rotation.dto
sideseal Jan 12, 2024
75bc11f
modify: check valid month changed to pipe
sideseal Jan 12, 2024
a81a3da
delete: delete duplicated working code - validate month
sideseal Jan 12, 2024
0234030
feat: enable docker container logs to be sent to cloudwatch
Jiwon-Woo Jan 24, 2024
a5bfda4
refactor: UnknownException contructor receive error
Jiwon-Woo Jan 24, 2024
3ac3a5c
feat: throw UnknownException in meet up EventHandler
Jiwon-Woo Jan 24, 2024
6569795
refactor: remove unnecessary logging
Jiwon-Woo Jan 24, 2024
e81b30c
Merge pull request #77 from Together42/45-rotation-fix
seo-wo Jan 26, 2024
dc5c856
Merge branch 'dev' into rotation-dto
sideseal Jan 26, 2024
50986fd
Merge pull request #78 from Together42/rotation-dto
seo-wo Jan 26, 2024
9cb2c3b
Merge branch 'dev' of https://github.com/Together42/nest-backend into…
Jiwon-Woo Jan 26, 2024
8179fe6
Merge pull request #81 from Together42/80-exception-filter-and-logging
Jiwon-Woo Jan 26, 2024
299480e
hotfix: fix type error
sideseal Jan 26, 2024
a6e0514
fix: change return type in findAllRotation and remove comment
sideseal Jan 26, 2024
dac0bdb
Merge pull request #84 from Together42/hotfix-rotation
Jiwon-Woo Jan 26, 2024
3f5ec34
build: add new env related with AWS Cloudwatch
Jiwon-Woo Jan 26, 2024
02bfe75
Merge pull request #85 from Together42/80-exception-filter-and-logging
Jiwon-Woo Jan 26, 2024
3d2076a
feat: transaction interceptor
seo-wo Jan 26, 2024
c14b17c
refactor: delete console.log
seo-wo Jan 26, 2024
9fa1b50
Merge pull request #86 from Together42/83-transaction_interceptor
seo-wo Jan 26, 2024
b94fe86
added: add slack module for resolve dependancy
sideseal Jan 26, 2024
f241e24
added: add slack module & service
sideseal Jan 26, 2024
8dd9d89
chore: change comment
sideseal Jan 26, 2024
e4eb5fb
feat: add checkTomorrowLibrarian
sideseal Jan 26, 2024
faf1d07
Merge branch 'dev' into 82-rotation-bot
sideseal Jan 26, 2024
040e893
fix: handle case if librarian is only one
sideseal Jan 26, 2024
69c694f
fix: fix unintentionally inserted try
sideseal Jan 26, 2024
04f20f5
hotfix: fix month/year validation
sideseal Jan 28, 2024
e5c843d
hotfix: fix additional cases and modify service logic
sideseal Jan 28, 2024
39e358a
refactor: validate query string by using dto class-validator
Jiwon-Woo Jan 28, 2024
fac3a68
modify: change user defined slack module to slack library
sideseal Feb 2, 2024
b40cbef
modify: change function name
sideseal Feb 2, 2024
935e77f
added: add rotation deadline alarm slack bot
sideseal Feb 2, 2024
fd4d845
fix: remove findAllRotations default value
Jiwon-Woo Feb 2, 2024
966ddd1
Merge pull request #89 from Together42/hotfix-rotation-dto
sideseal Feb 2, 2024
2981c6c
delete: delete month/year validation pipes
sideseal Feb 2, 2024
d8a2554
Merge pull request #88 from Together42/hotfix-rotation
seo-wo Feb 2, 2024
9db08e0
chore: stop logging to cloudwatch
Jiwon-Woo Feb 2, 2024
d453e1d
Merge pull request #90 from Together42/80-exception-filter-and-logging
Jiwon-Woo Feb 2, 2024
3c382ef
modify: change cron time
sideseal Feb 2, 2024
1959864
modify: remove comment and change cron time
sideseal Feb 2, 2024
f4a8a6d
hotfix: change duplicated cron name
sideseal Feb 2, 2024
76dd518
Merge pull request #87 from Together42/82-rotation-bot
seo-wo Feb 2, 2024
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
3 changes: 3 additions & 0 deletions .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,8 @@ jobs:
SERVICE_KEY=${{ secrets.OPENAPI_HOLIDAY_SERVICE_KEY }}
SLACK_BOT_USER_OAUTH_ACCESS_TOKEN=${{ secrets.SLACK_BOT_USER_OAUTH_ACCESS_TOKEN_DEV }}
SLACK_CHANNEL_JIPHYEONJEON=${{ secrets.SLACK_CHANNEL_JIPHYEONJEON_DEV }}
AWS_CLOUDWATCH_LOG_GROUP=${{ secrets.AWS_CLOUDWATCH_LOG_GROUP_DEV }}
AWS_CLOUDWATCH_REGION=${{ secrets.AWS_CLOUDWATCH_REGION }}
AWS_CLOUDWATCH_LOG_STREAM_BACKEND=${{ secrets.AWS_CLOUDWATCH_LOG_STREAM_BACKEND }}
FRONT_URL=${{ secrets.FRONT_URL_DEV }}" > .env.dev
docker-compose --env-file .env.dev up -d --build backend_dev
3 changes: 3 additions & 0 deletions .github/workflows/deploy-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,8 @@ jobs:
SERVICE_KEY=${{ secrets.OPENAPI_HOLIDAY_SERVICE_KEY }}
SLACK_BOT_USER_OAUTH_ACCESS_TOKEN=${{ secrets.SLACK_BOT_USER_OAUTH_ACCESS_TOKEN }}
SLACK_CHANNEL_JIPHYEONJEON=${{ secrets.SLACK_CHANNEL_JIPHYEONJEON }}
AWS_CLOUDWATCH_LOG_GROUP=${{ secrets.AWS_CLOUDWATCH_LOG_GROUP }}
AWS_CLOUDWATCH_REGION=${{ secrets.AWS_CLOUDWATCH_REGION }}
AWS_CLOUDWATCH_LOG_STREAM_BACKEND=${{ secrets.AWS_CLOUDWATCH_LOG_STREAM_BACKEND }}
FRONT_URL=${{ secrets.FRONT_URL }}" > .env.prod
docker-compose --env-file .env.prod up -d --build backend
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

## Description
친해지길 바라 백엔드를 리팩토링합니다.
- DB 스키마 및 REST API 재설계
- [REST API 재설계](https://github.com/Together42/nest-backend/wiki/API-%EB%B3%80%EA%B2%BD-%EC%82%AC%ED%95%AD)
- [DB 스키마 재설계](https://github.com/Together42/nest-backend/wiki/DB-%EB%B3%80%EA%B2%BD-%EC%82%AC%ED%95%AD)
- JavaScript/Express -> TypeScript/NestJS
- Raw Query -> TypeORM

Expand Down Expand Up @@ -37,6 +38,11 @@

<br>

## 서비스 아키텍처
![아키텍처](https://github.com/Together42/nest-backend/assets/74581396/761238a0-6b7e-4307-b24e-06d675d801c2)

<br>

## 실행 방법
### 1. git clone
```sh
Expand Down Expand Up @@ -101,6 +107,11 @@ yarn install && yarn start:dev

<br>

## API 명세
[Swagger Docs](https://dev.together.42jip.net/swagger)

<br>

## License

Nest is [MIT licensed](LICENSE).
11 changes: 10 additions & 1 deletion src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import { CqrsModule } from '@nestjs/cqrs';
import { AuthModule } from './auth/auth.module';
import { UserModule } from './user/user.module';
import configuration from './config/configuration';
import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
import { ServiceExceptionFilter } from './common/exception-filter/service.exception-filter';
import { LogInterceptor } from './common/interceptor/log.interceptor';
import { UnknownExceptionFilter } from './common/exception-filter/unknown.exception-filter';

@Module({
imports: [
Expand Down Expand Up @@ -57,6 +61,11 @@ import configuration from './config/configuration';
BatchModule,
],
controllers: [AppController],
providers: [AppService],
providers: [
AppService,
{ provide: APP_FILTER, useClass: UnknownExceptionFilter },
{ provide: APP_FILTER, useClass: ServiceExceptionFilter },
{ provide: APP_INTERCEPTOR, useClass: LogInterceptor },
],
})
export class AppModule {}
1 change: 0 additions & 1 deletion src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ export class AuthService {
const accessToken: string = await this.generateToken(user);
return { accessToken };
} catch (error) {
this.logger.error(`refreshAccessToken [error: ${error.message}]`);
throw new UnauthorizedException('Unauthorized', '401');
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/auth/guard/jwt.guard.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable, ExecutionContext, Logger } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { JwtService } from '@nestjs/jwt';
import { UnknownException } from 'src/common/exception/unknown.exception';

@Injectable()
export class JwtGuard extends AuthGuard('jwt') {
Expand Down Expand Up @@ -35,7 +36,7 @@ export class JwtGuard extends AuthGuard('jwt') {
token could be expired or invaild
return exception to client and let client to refresh token
*/
throw error;
throw new UnknownException(error);
}
this.logger.debug(`canActivate [result: ${result}]`);
return result;
Expand Down
1 change: 0 additions & 1 deletion src/auth/strategy/jwt.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
try {
done(null, payload);
} catch (error) {
this.logger.error(`validate [error: ${error.message}]`);
throw new UnauthorizedException('Unauthorized', '401');
}
}
Expand Down
8 changes: 6 additions & 2 deletions src/batch/batch.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ export class BatchService {
try {
await this.meetupsService.createMatching({ meetupId, teamNum: 1 });
} catch (e) {
this.logger.error('[createWeeklyMeeting]', e);
this.logger.error(`[createWeeklyMeeting] meetupId: ${meetupId}`);
this.logger.error(e);
}
},
});
this.logger.debug(`[createWeeklyMeeting] matchWeeklyMeeting: `, cronJob);
this.schedulerReistry.addCronJob('matchWeeklyMeeting', cronJob);
cronJob.start();
}
Expand All @@ -60,10 +62,12 @@ export class BatchService {
try {
await this.meetupsService.createMatching({ meetupId, teamNum: 1 });
} catch (e) {
this.logger.error('[createWeeklyDinner]', e);
this.logger.error(`[createWeeklyDinner] meetupId: ${meetupId}`);
this.logger.error(e);
}
},
});
this.logger.debug(`[createWeeklyDinner] matchWeeklyDinner: `, cronJob);
this.schedulerReistry.addCronJob('matchWeeklyDinner', cronJob);
cronJob.start();
}
Expand Down
6 changes: 5 additions & 1 deletion src/common/enum/error-message.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,9 @@ export enum ErrorMessage {
MEETUP_REGISTRATION_ALREADY_EXIST = '이미 신청한 이벤트 입니다.',
TOO_MANY_MEETUP_TEAM_NUMBER = '신청 인원보다 팀 개수가 많습니다.',

USER_NOT_FOUND = '존재하지 않는 유저입니다.'
USER_NOT_FOUND = '존재하지 않는 유저입니다.',

HASH_TOKEN_ERROR = 'HASH_TOKEN_ERROR'
}

export type KeyOfErrorMessage = keyof typeof ErrorMessage;
19 changes: 19 additions & 0 deletions src/common/exception-filter/service.exception-filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ArgumentsHost, Catch, ExceptionFilter, Logger } from '@nestjs/common';
import { ServiceException } from '../exception/service.exception';

@Catch(ServiceException)
export class ServiceExceptionFilter implements ExceptionFilter {
private readonly logger = new Logger(ServiceExceptionFilter.name);

catch(exception: ServiceException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const status = exception.getStatus();

response.status(status).json({
statusCode: status,
errorCode: exception.getErrorCode(),
message: exception.getMessage(),
});
}
}
18 changes: 18 additions & 0 deletions src/common/exception-filter/unknown.exception-filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ArgumentsHost, Catch, ExceptionFilter, HttpStatus, Logger } from '@nestjs/common';
import { STATUS_CODES } from 'http';
import { UnknownException } from '../exception/unknown.exception';

@Catch(UnknownException)
export class UnknownExceptionFilter implements ExceptionFilter {
private readonly logger = new Logger(UnknownExceptionFilter.name);
catch(exception: Error, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const status = HttpStatus.INTERNAL_SERVER_ERROR;

response.status(status).json({
statusCode: status,
message: exception.message || STATUS_CODES[status],
});
}
}
25 changes: 25 additions & 0 deletions src/common/exception/service.exception.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ErrorMessage, KeyOfErrorMessage } from '../enum/error-message.enum';

export class ServiceException extends Error {
private readonly status: number;
private readonly errorCode: string;

constructor(errorCode: KeyOfErrorMessage, status: number, message?: string) {
super(message || ErrorMessage[errorCode]);
this.name = errorCode;
this.status = status;
this.errorCode = errorCode;
}

getStatus() {
return this.status;
}

getErrorCode() {
return this.errorCode;
}

getMessage() {
return this.message;
}
}
12 changes: 12 additions & 0 deletions src/common/exception/unknown.exception.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export class UnknownException extends Error {
private readonly status: number;

constructor(e?: Error) {
super(e?.message || 'Unknown Error');
this.name = UnknownException.name;
}

getMessage() {
return this.message;
}
}
49 changes: 49 additions & 0 deletions src/common/interceptor/log.interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {
CallHandler,
ExecutionContext,
HttpException,
Injectable,
Logger,
NestInterceptor,
} from '@nestjs/common';
import { Observable, catchError } from 'rxjs';
import { ServiceException } from '../exception/service.exception';
import { UnknownException } from '../exception/unknown.exception';
import { SlackService } from 'nestjs-slack';
import { Message } from 'slack-block-builder';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class LogInterceptor implements NestInterceptor {
private readonly logger = new Logger(LogInterceptor.name);

constructor(
private slackService: SlackService,
private configService: ConfigService,
) {}

intercept(context: ExecutionContext, next: CallHandler<any>): Observable<any> {
// const start = new Date();
const request = context.switchToHttp().getRequest();

return next.handle().pipe(
catchError((err) => {
const log = `${request.method} ${request.url} - ${err.stack}`;
this.logger.error(log);
if (err instanceof ServiceException || err instanceof HttpException) {
throw err;
}
this.slackService.postMessage(
Message({
text: log,
channel: this.configService.get('slack.jiphyeonjeonChannel'),
}).buildToObject(),
);
throw new UnknownException(err);
}),
// tap((observable) => {
// const end = new Date();
// }),
);
}
}
48 changes: 48 additions & 0 deletions src/common/interceptor/transaction.interceptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
CallHandler,
ExecutionContext,
Injectable,
NestInterceptor,
} from '@nestjs/common';
import {
catchError,
finalize,
Observable,
concatMap,
} from 'rxjs';
import { QueryRunner, DataSource } from 'typeorm';

@Injectable()
export class TransactionInterceptor implements NestInterceptor {
constructor(private readonly dataSource: DataSource) {}

async intercept(
context: ExecutionContext,
next: CallHandler<any>,
) : Promise<Observable<any>> {
const request = context.switchToHttp().getRequest();
const queryRunner: QueryRunner = await this.initRunner();
request.queryRunnerManager = queryRunner.manager;

return next.handle().pipe(
concatMap(async (data) => {
await queryRunner.commitTransaction();
return data;
}),
catchError(async (error) => {
await queryRunner.rollbackTransaction();
throw error;
}),
finalize(async () => {
await queryRunner.release();
}),
);
}

private async initRunner(): Promise<QueryRunner> {
const queryRunner: QueryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
return queryRunner;
}
}
8 changes: 8 additions & 0 deletions src/decorator/transaction.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ExecutionContext, createParamDecorator } from "@nestjs/common";

export const TransactionManager = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.queryRunnerManager;
}
)
Loading