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

๐ŸŽจfeat : ์œ ์ € ๊ถŒํ•œ ๋ฐ ์ธ์ฆ ๋กœ์ง ์ถ”๊ฐ€ #5

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ web_modules/
.env.test.local
.env.production.local
.env.local

/key
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
Expand Down
2 changes: 0 additions & 2 deletions commitlint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ module.exports = {
]
},
'body-max-line-length': ({ body }) => {
console.log(body)
console.log(body.length)
const isBodyMatch = body && body.length <= 72
return [!body, isBodyMatch, '๋ณธ๋ฌธ์€ 72์ž ์ดํ•˜๋กœ ์ž‘์„ฑํ•ด์ฃผ์„ธ์š”']
},
Expand Down
19 changes: 18 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "cross-env NODE_ENV=production nest start",
"start:dev": "cross-env NODE_ENV=development nest start --watch",
"start:watch": "cross-env NODE_ENV=development nest build --webpack --webpackPath webpack-hmr.config.js --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
Expand All @@ -18,18 +19,29 @@
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"db:push": "dotenv -e .env.development -- npx prisma db push",
"prepare": "husky install"
},
"dependencies": {
"@nestjs/apollo": "^10.1.7",
"@nestjs/common": "^9.0.0",
"@nestjs/config": "^2.2.0",
"@nestjs/core": "^9.0.0",
"@nestjs/graphql": "^10.1.7",
"@nestjs/platform-express": "^9.0.0",
"@nestjs/platform-socket.io": "^9.2.1",
"@nestjs/swagger": "^6.1.4",
"@nestjs/websockets": "^9.2.1",
"@prisma/client": "^4.9.0",
"@socket.io/admin-ui": "^0.5.1",
"@socket.io/redis-adapter": "^8.0.1",
"@types/jsonwebtoken": "^9.0.1",
"@types/socket.io": "^3.0.2",
"apollo-server-express": "^3.11.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"graphql": "^16.6.0",
"jsonwebtoken": "^9.0.0",
"redis": "^4.6.4",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0"
Expand All @@ -48,6 +60,7 @@
"@typescript-eslint/parser": "^5.0.0",
"cross-env": "^7.0.3",
"cz-conventional-changelog": "^3.3.0",
"dotenv-cli": "^7.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
Expand All @@ -56,13 +69,17 @@
"joi": "^17.7.0",
"lint-staged": "^13.1.0",
"prettier": "^2.3.2",
"prisma": "^4.9.0",
"run-script-webpack-plugin": "^0.1.1",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "^29.0.5",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
"typescript": "^4.7.4",
"webpack": "^5.75.0",
"webpack-node-externals": "^3.0.0"
},
"jest": {
"moduleFileExtensions": [
Expand Down
21 changes: 21 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model User {
id Int @id @default(autoincrement())
nickname String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
role UserRole?
}

enum UserRole {
cheonkyu0 marked this conversation as resolved.
Show resolved Hide resolved
Admin
Client
}
49 changes: 46 additions & 3 deletions src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'
import { JwtMiddleware } from './jwt/jwt.middleware'
import {
Module,
NestModule,
MiddlewareConsumer,
RequestMethod,
} from '@nestjs/common'
import { ConfigModule } from '@nestjs/config'
import { LoggerMiddleware } from './logger/logger.middleware'
import { EventsModule } from './events/events.module'
import { LoggerModule } from './logger/logger.module'
import joi from 'joi'
import { WorkspacesModule } from './workspaces/workspaces.module'
import { JwtModule } from './jwt/jwt.module'
import { GraphQLModule } from '@nestjs/graphql'
import { ApolloDriverConfig, ApolloDriver } from '@nestjs/apollo'
import { PrismaModule } from './prisma/prisma.module'
import { UsersModule } from './users/users.module'
import { AuthModule } from './auth/auth.module'
import { SocketsModule } from './sockets/sockets.module'

@Module({
imports: [
Expand All @@ -17,20 +30,50 @@ import { WorkspacesModule } from './workspaces/workspaces.module'
ignoreEnvFile: process.env.NODE_ENV === 'production',
validationSchema: joi.object({
cheonkyu0 marked this conversation as resolved.
Show resolved Hide resolved
PORT: joi.string().required(),
AUTH_KEY: joi.string().required(),
REDIS_HOST: joi.string().required(),
REDIS_PORT: joi.string().required(),
REDIS_PASSWORD: joi.string().required(),
JWT_PRIVATE_KEY: joi.string().required(),
JWT_PUBLIC_KEY: joi.string().required(),
}),
}),
PrismaModule,
GraphQLModule.forRoot<ApolloDriverConfig>({
cheonkyu0 marked this conversation as resolved.
Show resolved Hide resolved
driver: ApolloDriver,
autoSchemaFile: true,
context: ({ req, connection }) => {
return {
token: req
? req.headers[process.env.AUTH_KEY]
: connection.context[process.env.AUTH_KEY],
}
},
}),
LoggerModule,
WorkspacesModule,
EventsModule,
WorkspacesModule,
JwtModule.forRoot({
isRSA: true,
priveKey: Buffer.from(process.env.JWT_PRIVATE_KEY, 'base64').toString(
'ascii',
),
pubkey: Buffer.from(process.env.JWT_PUBLIC_KEY, 'base64').toString(
'ascii',
),
}),
UsersModule,
AuthModule,
SocketsModule,
],
controllers: [],
providers: [],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('*')
consumer.apply(JwtMiddleware).forRoutes({
cheonkyu0 marked this conversation as resolved.
Show resolved Hide resolved
path: '/graphql',
method: RequestMethod.POST,
})
}
}
7 changes: 7 additions & 0 deletions src/auth/auth-exception.filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ExceptionFilter } from '@nestjs/common'

export class RoleException implements ExceptionFilter {
async catch() {
return { ok: false, error: '๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค' }
cheonkyu0 marked this conversation as resolved.
Show resolved Hide resolved
}
}
10 changes: 10 additions & 0 deletions src/auth/auth-user.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { GqlExecutionContext } from '@nestjs/graphql'
import { createParamDecorator, ExecutionContext } from '@nestjs/common'

export const AuthUser = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const gqlContext = GqlExecutionContext.create(ctx).getContext()
const user = gqlContext['user']
return user
},
)
34 changes: 34 additions & 0 deletions src/auth/auth.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { UsersService } from 'src/users/users.service'
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'
import { Reflector } from '@nestjs/core'
import { GqlExecutionContext } from '@nestjs/graphql'
import { JwtService } from 'src/jwt/jwt.service'
import { RoleException } from './auth-exception.filter'
@Injectable()
export class AuthGuard implements CanActivate {
constructor(
private readonly relector: Reflector,
cheonkyu0 marked this conversation as resolved.
Show resolved Hide resolved
private readonly jwtService: JwtService,
private readonly usersService: UsersService,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const roles = this.relector.get('roles', context.getHandler())
if (!roles) {
return true
}
const gqlContext = GqlExecutionContext.create(context).getContext()
const token = gqlContext.token
if (token) {
const decode = this.jwtService.verify(token)
if (typeof decode === 'object' && decode.hasOwnProperty('id')) {
const { user } = await this.usersService.findById(decode.id)
if (user) {
gqlContext['user'] = user
return roles.includes('Any') || roles.includes(user.role)
}
}
}

throw new RoleException()
}
}
20 changes: 20 additions & 0 deletions src/auth/auth.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { UsersModule } from './../users/users.module'
import { Module } from '@nestjs/common'
import { APP_GUARD, APP_FILTER } from '@nestjs/core'
import { AuthGuard } from './auth.guard'
import { RoleException } from './auth-exception.filter'

@Module({
imports: [UsersModule],
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
{
provide: APP_FILTER,
useClass: RoleException,
},
],
})
export class AuthModule {}
6 changes: 6 additions & 0 deletions src/auth/role.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { SetMetadata } from '@nestjs/common'
import { UserRole } from '@prisma/client'

export type AllowedRoles = keyof typeof UserRole | 'Any'

export const Role = (roles: AllowedRoles[]) => SetMetadata('roles', roles)
1 change: 1 addition & 0 deletions src/common/common.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const JWT_PROVIDER = 'jwt_provider'
6 changes: 6 additions & 0 deletions src/common/common.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Socket } from 'socket.io'

export type SocketMiddleware = (
socket: Socket,
next: (err?: Error) => void,
) => void
27 changes: 27 additions & 0 deletions src/common/dtos/core.entites.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Field, ObjectType } from '@nestjs/graphql'
import { IsNotEmpty, IsNumber, IsDate } from 'class-validator'

@ObjectType()
export class CoreDTO {
@Field(() => Number)
@IsNotEmpty()
@IsNumber()
id: number

@Field(() => Date)
@IsDate()
createdAt: Date

@Field(() => Date)
@IsDate()
updatedAt: Date
}

@ObjectType()
export class CoreOutput {
@Field(() => String, { nullable: true })
error?: string

@Field(() => Boolean)
ok: boolean
}
Loading