feat: Basic setup with auth
This commit is contained in:
8
common/exceptions/custom-exceptions.ts
Normal file
8
common/exceptions/custom-exceptions.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { HttpException, HttpStatus } from '@nestjs/common';
|
||||
|
||||
// Base exception
|
||||
export class BaseException extends HttpException {
|
||||
protected constructor(code: string, message: string, status: HttpStatus) {
|
||||
super({ code, message }, status);
|
||||
}
|
||||
}
|
||||
32
common/exceptions/exception-filter.ts
Normal file
32
common/exceptions/exception-filter.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import {
|
||||
ExceptionFilter,
|
||||
Catch,
|
||||
ArgumentsHost,
|
||||
HttpException,
|
||||
Logger,
|
||||
} from '@nestjs/common';
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
@Catch(HttpException) // What exception to catch
|
||||
export class HttpExceptionFilter implements ExceptionFilter<HttpException> {
|
||||
constructor(private readonly logger: Logger) {}
|
||||
catch(exception: HttpException, host: ArgumentsHost) {
|
||||
const ctx = host.switchToHttp();
|
||||
const request: Request = ctx.getRequest();
|
||||
const response: Response = ctx.getResponse();
|
||||
const status = exception.getStatus();
|
||||
|
||||
if (status >= 500) {
|
||||
this.logger.warn({
|
||||
method: request.method,
|
||||
url: request.url,
|
||||
message: exception.message,
|
||||
});
|
||||
}
|
||||
|
||||
response.status(status).json({
|
||||
message: exception.message,
|
||||
statusCode: status,
|
||||
});
|
||||
}
|
||||
}
|
||||
1
common/exceptions/index.ts
Normal file
1
common/exceptions/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './custom-exceptions';
|
||||
1
common/http/index.ts
Normal file
1
common/http/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './response';
|
||||
25
common/http/response.ts
Normal file
25
common/http/response.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export class MessageResponse {
|
||||
readonly success: boolean;
|
||||
readonly message: string;
|
||||
|
||||
constructor(message?: string) {
|
||||
this.success = true;
|
||||
this.message = message ?? 'Success';
|
||||
}
|
||||
}
|
||||
|
||||
export class DataResponse<T> extends MessageResponse {
|
||||
readonly data: T;
|
||||
|
||||
constructor(data: T, message?: string) {
|
||||
super(message);
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
// Skipped
|
||||
export class GlobalErrorResponseDTO {
|
||||
success: boolean;
|
||||
message: string;
|
||||
statusCode: number;
|
||||
}
|
||||
0
common/interceptors/index.ts
Normal file
0
common/interceptors/index.ts
Normal file
72
common/interceptors/response.interceptor.ts
Normal file
72
common/interceptors/response.interceptor.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import {
|
||||
Injectable,
|
||||
NestInterceptor,
|
||||
ExecutionContext,
|
||||
CallHandler,
|
||||
} from '@nestjs/common';
|
||||
import { DataResponse, MessageResponse } from 'common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class ResponseInterceptor<T> implements NestInterceptor<T, any> {
|
||||
intercept(_: ExecutionContext, next: CallHandler): Observable<any> {
|
||||
return next.handle().pipe(
|
||||
map((data) => {
|
||||
if (data instanceof MessageResponse) return data;
|
||||
else if (data instanceof DataResponse) return data;
|
||||
else if (typeof data === 'string') return new MessageResponse(data);
|
||||
return new DataResponse(data);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: How to access request
|
||||
*
|
||||
@Injectable()
|
||||
export class ResponseInterceptor<T> implements NestInterceptor<T, any> {
|
||||
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
||||
const req = context.switchToHttp().getRequest();
|
||||
req.userId = 'ram';
|
||||
return next.handle();
|
||||
// return next.handle().pipe(
|
||||
// map((data) => {
|
||||
// if (data instanceof MessageResponse) return data;
|
||||
// else if (data instanceof DataResponse) return data;
|
||||
// else if (typeof data === 'string') return new MessageResponse(data);
|
||||
// return new DataResponse(data);
|
||||
// }),
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
||||
REQUEST
|
||||
↓
|
||||
Guards
|
||||
↓
|
||||
Interceptors (before)
|
||||
↓
|
||||
Pipes
|
||||
↓
|
||||
Controller method
|
||||
↓
|
||||
Interceptors (after) ← YOU ARE HERE
|
||||
↓
|
||||
Response
|
||||
|
||||
|
||||
3️⃣ What is next.handle() really?
|
||||
next.handle(): Observable<T>
|
||||
|
||||
|
||||
This is:
|
||||
|
||||
An RxJS stream of whatever the controller returns
|
||||
|
||||
Not the request
|
||||
|
||||
Not the response object
|
||||
|
||||
Just the return value
|
||||
* */
|
||||
@@ -31,6 +31,7 @@
|
||||
"@nestjs/swagger": "^11.2.6",
|
||||
"@prisma/adapter-pg": "^7.3.0",
|
||||
"@prisma/client": "^7.3.0",
|
||||
"bcrypt": "^6.0.0",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.3",
|
||||
"pg": "^8.18.0",
|
||||
@@ -46,6 +47,7 @@
|
||||
"@nestjs/testing": "^11.0.1",
|
||||
"@swc/cli": "^0.6.0",
|
||||
"@swc/core": "^1.10.7",
|
||||
"@types/bcrypt": "^6.0.0",
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^22.10.7",
|
||||
|
||||
34
pnpm-lock.yaml
generated
34
pnpm-lock.yaml
generated
@@ -29,6 +29,9 @@ importers:
|
||||
'@prisma/client':
|
||||
specifier: ^7.3.0
|
||||
version: 7.3.0(prisma@7.3.0(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3)
|
||||
bcrypt:
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0
|
||||
class-transformer:
|
||||
specifier: ^0.5.1
|
||||
version: 0.5.1
|
||||
@@ -69,6 +72,9 @@ importers:
|
||||
'@swc/core':
|
||||
specifier: ^1.10.7
|
||||
version: 1.15.11
|
||||
'@types/bcrypt':
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0
|
||||
'@types/express':
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.6
|
||||
@@ -1128,6 +1134,9 @@ packages:
|
||||
'@types/babel__traverse@7.28.0':
|
||||
resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==}
|
||||
|
||||
'@types/bcrypt@6.0.0':
|
||||
resolution: {integrity: sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==}
|
||||
|
||||
'@types/body-parser@1.19.6':
|
||||
resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==}
|
||||
|
||||
@@ -1537,6 +1546,10 @@ packages:
|
||||
resolution: {integrity: sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==}
|
||||
hasBin: true
|
||||
|
||||
bcrypt@6.0.0:
|
||||
resolution: {integrity: sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
bin-version-check@5.1.0:
|
||||
resolution: {integrity: sha512-bYsvMqJ8yNGILLz1KP9zKLzQ6YpljV3ln1gqhuLkUtyfGi3qXKGuK2p+U4NAvjVFzDFiBBtOpCOSFNuYYEGZ5g==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -2895,12 +2908,20 @@ packages:
|
||||
node-abort-controller@3.1.1:
|
||||
resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==}
|
||||
|
||||
node-addon-api@8.5.0:
|
||||
resolution: {integrity: sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==}
|
||||
engines: {node: ^18 || ^20 || >= 21}
|
||||
|
||||
node-emoji@1.11.0:
|
||||
resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==}
|
||||
|
||||
node-fetch-native@1.6.7:
|
||||
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
|
||||
|
||||
node-gyp-build@4.8.4:
|
||||
resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==}
|
||||
hasBin: true
|
||||
|
||||
node-int64@0.4.0:
|
||||
resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
|
||||
|
||||
@@ -4958,6 +4979,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/types': 7.29.0
|
||||
|
||||
'@types/bcrypt@6.0.0':
|
||||
dependencies:
|
||||
'@types/node': 22.19.10
|
||||
|
||||
'@types/body-parser@1.19.6':
|
||||
dependencies:
|
||||
'@types/connect': 3.4.38
|
||||
@@ -5499,6 +5524,11 @@ snapshots:
|
||||
|
||||
baseline-browser-mapping@2.9.19: {}
|
||||
|
||||
bcrypt@6.0.0:
|
||||
dependencies:
|
||||
node-addon-api: 8.5.0
|
||||
node-gyp-build: 4.8.4
|
||||
|
||||
bin-version-check@5.1.0:
|
||||
dependencies:
|
||||
bin-version: 6.0.0
|
||||
@@ -7034,12 +7064,16 @@ snapshots:
|
||||
|
||||
node-abort-controller@3.1.1: {}
|
||||
|
||||
node-addon-api@8.5.0: {}
|
||||
|
||||
node-emoji@1.11.0:
|
||||
dependencies:
|
||||
lodash: 4.17.23
|
||||
|
||||
node-fetch-native@1.6.7: {}
|
||||
|
||||
node-gyp-build@4.8.4: {}
|
||||
|
||||
node-int64@0.4.0: {}
|
||||
|
||||
node-releases@2.0.27: {}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
import { Public } from './auth/decorators';
|
||||
|
||||
@Controller()
|
||||
@Controller('')
|
||||
@Public(true)
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
|
||||
import { Logger, MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { UserModule } from './user/user.module';
|
||||
@@ -7,6 +7,10 @@ import { RequestContextMiddleware } from 'core/als/request-context.middleware';
|
||||
import { RequestContextModule } from 'core/als/request-context.module';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { PrismaModule } from './prisma/prisma.module';
|
||||
import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
|
||||
import { ResponseInterceptor } from 'common/interceptors/response.interceptor';
|
||||
import { ExceptionsHandler } from '@nestjs/core/exceptions/exceptions-handler';
|
||||
import { HttpExceptionFilter } from 'common/exceptions/exception-filter';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -19,7 +23,18 @@ import { PrismaModule } from './prisma/prisma.module';
|
||||
PrismaModule,
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
providers: [
|
||||
AppService,
|
||||
Logger,
|
||||
{
|
||||
provide: APP_INTERCEPTOR,
|
||||
useClass: ResponseInterceptor,
|
||||
},
|
||||
{
|
||||
provide: APP_FILTER,
|
||||
useClass: HttpExceptionFilter,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class AppModule implements NestModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
|
||||
@@ -1,4 +1,55 @@
|
||||
import { Controller } from '@nestjs/common';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
Post,
|
||||
Res,
|
||||
} from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
import { ApiOperation } from '@nestjs/swagger';
|
||||
import {
|
||||
LoginUserRequestDTO,
|
||||
LoginUserResponseDTO,
|
||||
RegisterUserRequestDTO,
|
||||
} from './dto';
|
||||
import { Response } from 'express';
|
||||
import { DataResponse } from 'common/http';
|
||||
|
||||
@Controller('auth')
|
||||
export class AuthController {}
|
||||
export class AuthController {
|
||||
constructor(private readonly authService: AuthService) {}
|
||||
|
||||
@ApiOperation({ summary: 'User login' })
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Post('/login')
|
||||
async login(
|
||||
@Body() body: LoginUserRequestDTO,
|
||||
@Res({ passthrough: true }) response: Response,
|
||||
): Promise<DataResponse<LoginUserResponseDTO>> {
|
||||
const { accessToken, refreshToken, user } =
|
||||
await this.authService.login(body);
|
||||
|
||||
response.cookie('accessToken', accessToken);
|
||||
|
||||
return new DataResponse(
|
||||
new LoginUserResponseDTO(user, accessToken, refreshToken),
|
||||
'Login successfull',
|
||||
);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: 'User register' })
|
||||
@HttpCode(HttpStatus.CREATED)
|
||||
@Post('/register')
|
||||
async register(@Body() body: RegisterUserRequestDTO): Promise<string> {
|
||||
await this.authService.register(body);
|
||||
|
||||
return 'Registered successfully. Login to continue.';
|
||||
}
|
||||
|
||||
logout() {}
|
||||
|
||||
forgotPassword() {}
|
||||
|
||||
regenTokens() {}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
import { APP_GUARD, Reflector } from '@nestjs/core';
|
||||
import { AuthGuard } from './guards/auth.guard';
|
||||
import { UserModule } from 'src/user/user.module';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { RequestContextModule } from 'core/als/request-context.module';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [
|
||||
AuthService,
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
useClass: AuthGuard,
|
||||
useFactory: () => AuthGuard,
|
||||
inject: [Reflector],
|
||||
},
|
||||
],
|
||||
controllers: [AuthController],
|
||||
imports: [UserModule, JwtModule, RequestContextModule],
|
||||
})
|
||||
export class AuthModule {}
|
||||
|
||||
@@ -1,13 +1,74 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { Public } from './decorators';
|
||||
import { LoginUserRequestDTO, RegisterUserRequestDTO } from './dto';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import { UserService } from 'src/user/user.service';
|
||||
import { TokenInputType } from './types';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
|
||||
@Injectable()
|
||||
@Public()
|
||||
export class AuthService {
|
||||
async login() {}
|
||||
constructor(
|
||||
private readonly userService: UserService,
|
||||
private readonly jwtService: JwtService,
|
||||
) {}
|
||||
|
||||
async signup() {}
|
||||
async register(dto: RegisterUserRequestDTO) {
|
||||
const hashedPassword = await bcrypt.hash(dto.password, 10);
|
||||
await this.userService.createUserWithPassword({
|
||||
...dto,
|
||||
password: hashedPassword,
|
||||
});
|
||||
|
||||
@Public(false)
|
||||
async logout() {}
|
||||
return true;
|
||||
}
|
||||
|
||||
async login(dto: LoginUserRequestDTO) {
|
||||
const user = await this.userService.findUserForAuth(dto.email);
|
||||
if (!user) throw new UnauthorizedException('Invalid credentials.');
|
||||
|
||||
const passwordMatch = await bcrypt.compare(dto.password, user.password);
|
||||
if (!passwordMatch) throw new UnauthorizedException('Invalid credentials.');
|
||||
|
||||
const token = {
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
role: user.role,
|
||||
};
|
||||
|
||||
// TODO: Store more info: orgId, orgRole, etc
|
||||
const { accessToken, refreshToken } = await this.genSignedTokens(token);
|
||||
const hashedRefreshToken = await bcrypt.hash(refreshToken, 10);
|
||||
|
||||
await this.userService.updateRefreshToken(user.id, hashedRefreshToken);
|
||||
|
||||
return {
|
||||
accessToken,
|
||||
refreshToken,
|
||||
user,
|
||||
};
|
||||
}
|
||||
|
||||
logout() {}
|
||||
|
||||
resetPassword() {}
|
||||
|
||||
// TODO: Use nest jwt
|
||||
private async genSignedTokens(token: TokenInputType) {
|
||||
const accessToken = await this.jwtService.signAsync(token, {
|
||||
secret: 'demo',
|
||||
});
|
||||
|
||||
const refreshToken = await this.jwtService.signAsync(
|
||||
{
|
||||
userId: token.userId,
|
||||
},
|
||||
{
|
||||
secret: 'demo',
|
||||
},
|
||||
);
|
||||
|
||||
return { accessToken, refreshToken };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
export * from './register-user.dto';
|
||||
export * from './login-user.dto';
|
||||
export * from './login-response.dto';
|
||||
|
||||
14
src/auth/dto/login-response.dto.ts
Normal file
14
src/auth/dto/login-response.dto.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { User } from 'prisma/generated/prisma/client';
|
||||
import { UserDTO } from 'src/user/dtos';
|
||||
|
||||
export class LoginUserResponseDTO {
|
||||
readonly accessToken: string;
|
||||
readonly refreshToken: string;
|
||||
readonly user: UserDTO;
|
||||
|
||||
constructor(user: User, accessToken: string, refreshToken: string) {
|
||||
this.user = new UserDTO(user);
|
||||
this.accessToken = accessToken;
|
||||
this.refreshToken = refreshToken;
|
||||
}
|
||||
}
|
||||
24
src/auth/dto/login-user.dto.ts
Normal file
24
src/auth/dto/login-user.dto.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsEmail, IsNotEmpty, IsString, MinLength } from 'class-validator';
|
||||
|
||||
export class LoginUserRequestDTO {
|
||||
@ApiProperty({
|
||||
description: "User's email",
|
||||
example: 'user@example.com',
|
||||
type: 'string',
|
||||
})
|
||||
@IsEmail()
|
||||
@IsNotEmpty()
|
||||
email: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "User's password",
|
||||
example: '123456',
|
||||
type: 'string',
|
||||
minLength: 6,
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MinLength(6)
|
||||
password: string;
|
||||
}
|
||||
@@ -12,9 +12,9 @@ import { PUBLIC_KEY } from 'common/keys';
|
||||
|
||||
export class AuthGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly reflector: Reflector,
|
||||
private readonly jwtService: JwtService,
|
||||
private readonly requestContext: RequestContextService,
|
||||
private readonly reflector: Reflector,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext) {
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './jwt';
|
||||
export * from './role';
|
||||
export * from './token';
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { UserRoleType } from './role';
|
||||
import { ORG_ROLE, USER_ROLE } from 'prisma/generated/prisma/enums';
|
||||
|
||||
export interface JwtPayload {
|
||||
iat?: number;
|
||||
exp?: number;
|
||||
orgId?: string;
|
||||
orgRole?: ORG_ROLE;
|
||||
userId: string;
|
||||
email: string;
|
||||
role: UserRoleType;
|
||||
permission?: string[];
|
||||
role: USER_ROLE;
|
||||
permissions?: string[];
|
||||
}
|
||||
|
||||
11
src/auth/types/token.ts
Normal file
11
src/auth/types/token.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export interface TokenInputType {
|
||||
userId: string;
|
||||
email: string;
|
||||
role: string;
|
||||
}
|
||||
|
||||
export interface AccessTokenPayloadType extends TokenInputType {}
|
||||
|
||||
export interface RefreshTokenPayloadType {
|
||||
userId: string;
|
||||
}
|
||||
37
src/main.ts
37
src/main.ts
@@ -1,10 +1,47 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
|
||||
const swaggerConfig = new DocumentBuilder()
|
||||
.setTitle('Kaa Khane')
|
||||
.setDescription(`API Documentation for Kaa Khane`)
|
||||
.setVersion('0.0.1')
|
||||
.addGlobalResponse(
|
||||
{
|
||||
status: 500,
|
||||
description: 'Internal Server Error',
|
||||
},
|
||||
{
|
||||
status: 401,
|
||||
description: 'Unauthorized',
|
||||
},
|
||||
{
|
||||
status: 403,
|
||||
description: 'Forbidden',
|
||||
},
|
||||
)
|
||||
.addBearerAuth(
|
||||
{
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
bearerFormat: 'JWT',
|
||||
},
|
||||
'access-token',
|
||||
)
|
||||
.build();
|
||||
|
||||
const documentFactory = () =>
|
||||
SwaggerModule.createDocument(app, swaggerConfig);
|
||||
SwaggerModule.setup('/docs', app, documentFactory, {
|
||||
swaggerOptions: {
|
||||
persistAuthorization: true,
|
||||
},
|
||||
});
|
||||
|
||||
const config = app.get(ConfigService);
|
||||
const port = config.get<number>('PORT') ?? 3000;
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ export class PrismaService
|
||||
const connectionPool = new Pool({
|
||||
connectionString,
|
||||
});
|
||||
console.log(connectionString);
|
||||
|
||||
const adapter = new PrismaPg(connectionPool);
|
||||
|
||||
|
||||
@@ -5,5 +5,6 @@ import { PrismaModule } from 'src/prisma/prisma.module';
|
||||
@Module({
|
||||
providers: [UserService],
|
||||
imports: [PrismaModule],
|
||||
exports: [UserService],
|
||||
})
|
||||
export class UserModule {}
|
||||
|
||||
Reference in New Issue
Block a user