Skip to content

Commit

Permalink
feat: 요청자 정보를 cls namespace에 담도록 구현 (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
Coalery authored Jan 14, 2024
1 parent cf0d9ed commit 9ca701b
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 48 deletions.
3 changes: 3 additions & 0 deletions src/constant/message.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export const Message = {
// 401 Unauthorized
TOKEN_REQUIRED: 'Token is required',

// 403 Forbidden
CANNOT_CREATE_GROUP: 'Cannot create group',
CANNOT_MODIFY_GROUP: 'Cannot modify group',
Expand Down
43 changes: 43 additions & 0 deletions src/core/auth/AuthGuard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Request } from 'express';
import {
CanActivate,
ExecutionContext,
Inject,
Injectable,
UnauthorizedException,
} from '@nestjs/common';

import { ITokenVerifier, TokenVerifier } from '@sight/core/auth/ITokenVerifier';

import { Message } from '@sight/constant/message';
import { ClsService } from 'nestjs-cls';

@Injectable()
export class AuthGuard implements CanActivate {
constructor(
@Inject(TokenVerifier)
private readonly tokenVerifier: ITokenVerifier,
private readonly clsService: ClsService,
) {}

canActivate(context: ExecutionContext): boolean {
const req: Request = context.switchToHttp().getRequest();
const authorizationHeader = req.headers['authorization'];

if (!authorizationHeader) {
throw new UnauthorizedException(Message.TOKEN_REQUIRED);
}

const token = authorizationHeader.split(' ')[1];
if (!token) {
throw new UnauthorizedException(Message.TOKEN_REQUIRED);
}

const requester = this.tokenVerifier.verify(token);
req['requester'] = requester;

this.clsService.set('requester', requester);

return true;
}
}
12 changes: 12 additions & 0 deletions src/core/auth/AuthModule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';

import { AuthGuard } from '@sight/core/auth/AuthGuard';

@Module({
providers: [
{ provide: APP_GUARD, useClass: AuthGuard },
// TODO: TokenVerifier 구현 후 추가
],
})
export class AuthModule {}
6 changes: 6 additions & 0 deletions src/core/auth/IRequester.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { UserRole } from '@sight/core/auth/UserRole';

export interface IRequester {
userId: string;
role: UserRole;
}
7 changes: 7 additions & 0 deletions src/core/auth/ITokenVerifier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { IRequester } from '@sight/core/auth/IRequester';

export const TokenVerifier = Symbol('TokenVerifier');

export interface ITokenVerifier {
verify: (token: string) => IRequester;
}
5 changes: 5 additions & 0 deletions src/core/auth/Requester.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createParamDecorator } from '@nestjs/common';

export const Requester = createParamDecorator((data, req) => {
return req['requester'];
});
5 changes: 5 additions & 0 deletions src/core/auth/UserRole.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const UserRole = {
USER: 'USER',
ADMIN: 'ADMIN',
} as const;
export type UserRole = (typeof UserRole)[keyof typeof UserRole];
19 changes: 0 additions & 19 deletions src/core/persistence/transaction/TransactionMiddleware.ts

This file was deleted.

14 changes: 2 additions & 12 deletions src/core/persistence/transaction/TransactionModule.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,15 @@
import {
MiddlewareConsumer,
Module,
NestModule,
OnModuleInit,
} from '@nestjs/common';
import { Module, OnModuleInit } from '@nestjs/common';
import { DiscoveryModule } from '@nestjs/core';

import { TransactionalApplier } from '@sight/core/persistence/transaction/TransactionalApplier';
import { TransactionMiddleware } from '@sight/core/persistence/transaction/TransactionMiddleware';

@Module({
imports: [DiscoveryModule],
providers: [TransactionalApplier],
})
export class TransactionModule implements NestModule, OnModuleInit {
export class TransactionModule implements OnModuleInit {
constructor(private readonly transactionalApplier: TransactionalApplier) {}

configure(consumer: MiddlewareConsumer) {
consumer.apply(TransactionMiddleware).forRoutes('*');
}

onModuleInit() {
this.transactionalApplier.bindTransactional();
}
Expand Down
23 changes: 6 additions & 17 deletions src/core/persistence/transaction/TransactionalApplier.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EntityManager } from '@mikro-orm/core';
import { Injectable, InternalServerErrorException } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
import { ICommandHandler, IEventHandler } from '@nestjs/cqrs';
import { ClsService } from 'nestjs-cls';

Expand Down Expand Up @@ -36,25 +36,14 @@ export class TransactionalApplier {

private createWrappedFunction(originalFn: AsyncFn, transaction: boolean) {
const wrapper = async (...args: any[]) => {
const entityManager: EntityManager | undefined = this.cls.get(
TRANSACTIONAL_ENTITY_MANAGER,
);
if (!entityManager) {
throw new InternalServerErrorException('Entity manager is not exists');
}

if (transaction) {
return await entityManager.transactional(async (manager) => {
return await this.cls.runWith(
{ [TRANSACTIONAL_ENTITY_MANAGER]: manager },
() => originalFn(args),
);
return await this.em.transactional(async (manager) => {
this.cls.set(TRANSACTIONAL_ENTITY_MANAGER, manager);
return await originalFn(args);
});
} else {
return await this.cls.runWith(
{ [TRANSACTIONAL_ENTITY_MANAGER]: this.em },
() => originalFn(args),
);
this.cls.set(TRANSACTIONAL_ENTITY_MANAGER, this.em);
return await originalFn(args);
}
};
Object.setPrototypeOf(wrapper, originalFn);
Expand Down

0 comments on commit 9ca701b

Please sign in to comment.