feat: User services
This commit is contained in:
@@ -1,9 +1,17 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
import { AuthGuard } from './guards/auth.guard';
|
||||
|
||||
@Module({
|
||||
providers: [AuthService],
|
||||
controllers: [AuthController]
|
||||
providers: [
|
||||
AuthService,
|
||||
{
|
||||
provide: APP_GUARD,
|
||||
useClass: AuthGuard,
|
||||
},
|
||||
],
|
||||
controllers: [AuthController],
|
||||
})
|
||||
export class AuthModule {}
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Public } from './decorators';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {}
|
||||
@Public()
|
||||
export class AuthService {
|
||||
async login() {}
|
||||
|
||||
async signup() {}
|
||||
|
||||
@Public(false)
|
||||
async logout() {}
|
||||
}
|
||||
|
||||
2
src/auth/decorators/index.ts
Normal file
2
src/auth/decorators/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './public.decorator';
|
||||
export * from './role.decorator';
|
||||
5
src/auth/decorators/public.decorator.ts
Normal file
5
src/auth/decorators/public.decorator.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { SetMetadata } from '@nestjs/common';
|
||||
import { PUBLIC_KEY } from 'common/keys';
|
||||
|
||||
export const Public = (isPublic?: boolean) =>
|
||||
SetMetadata(PUBLIC_KEY, isPublic ?? true);
|
||||
4
src/auth/decorators/role.decorator.ts
Normal file
4
src/auth/decorators/role.decorator.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { SetMetadata } from '@nestjs/common';
|
||||
import { ROLE_KEY } from 'common/keys';
|
||||
|
||||
export const Roles = (role: string) => SetMetadata(ROLE_KEY, role);
|
||||
1
src/auth/dto/index.ts
Normal file
1
src/auth/dto/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './register-user.dto';
|
||||
57
src/auth/dto/register-user.dto.ts
Normal file
57
src/auth/dto/register-user.dto.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import {
|
||||
IsEmail,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
MinLength,
|
||||
} from 'class-validator';
|
||||
|
||||
export class RegisterUserRequestDTO {
|
||||
@ApiProperty({
|
||||
description: "User's firstName",
|
||||
example: 'John',
|
||||
type: 'string',
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
firstName: string;
|
||||
|
||||
@ApiPropertyOptional({
|
||||
description: "User's middleName",
|
||||
example: 'Kumar',
|
||||
type: 'string',
|
||||
})
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
middleName?: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: "User's lastName",
|
||||
example: 'Doe',
|
||||
type: 'string',
|
||||
})
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
lastName: string;
|
||||
|
||||
@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;
|
||||
}
|
||||
46
src/auth/guards/auth.guard.ts
Normal file
46
src/auth/guards/auth.guard.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { RequestContextService } from 'core/als/request-context.service';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { JwtPayload } from '../types';
|
||||
import { Request } from 'express';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { PUBLIC_KEY } from 'common/keys';
|
||||
|
||||
export class AuthGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly jwtService: JwtService,
|
||||
private readonly requestContext: RequestContextService,
|
||||
private readonly reflector: Reflector,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext) {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
|
||||
const isPublicRoute = this.reflector.getAllAndOverride<boolean>(
|
||||
PUBLIC_KEY,
|
||||
[context.getHandler(), context.getClass()],
|
||||
);
|
||||
if (isPublicRoute) return true;
|
||||
|
||||
const token = this.extractTokenFromHeader(request);
|
||||
if (!token) throw new UnauthorizedException();
|
||||
|
||||
try {
|
||||
const payload: JwtPayload = await this.jwtService.verifyAsync(token);
|
||||
this.requestContext.set('user', payload);
|
||||
|
||||
return true;
|
||||
} catch (err) {
|
||||
throw new UnauthorizedException();
|
||||
}
|
||||
}
|
||||
|
||||
private extractTokenFromHeader(request: Request): string | undefined {
|
||||
const [type, token] = request.headers.authorization?.split(' ') ?? [];
|
||||
return type === 'Bearer' ? token : undefined;
|
||||
}
|
||||
}
|
||||
0
src/auth/guards/index.ts
Normal file
0
src/auth/guards/index.ts
Normal file
26
src/auth/guards/rbac.guard.ts
Normal file
26
src/auth/guards/rbac.guard.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { ROLE_KEY } from 'common/keys';
|
||||
import { RequestContextService } from 'core/als/request-context.service';
|
||||
|
||||
export class RbacGuard implements CanActivate {
|
||||
constructor(
|
||||
private readonly reflector: Reflector,
|
||||
private readonly requestContext: RequestContextService,
|
||||
) {}
|
||||
canActivate(context: ExecutionContext) {
|
||||
const requiredRole = this.reflector.getAllAndOverride<string>(ROLE_KEY, [
|
||||
context.getHandler(),
|
||||
context.getClass(),
|
||||
]);
|
||||
|
||||
const user = this.requestContext.user;
|
||||
if (!user) throw new UnauthorizedException();
|
||||
|
||||
return user.role === requiredRole;
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export * from './jwt';
|
||||
export * from './role';
|
||||
|
||||
@@ -1,26 +1,11 @@
|
||||
export interface UserPayload {
|
||||
import { UserRoleType } from './role';
|
||||
|
||||
export interface JwtPayload {
|
||||
iat?: number;
|
||||
exp?: number;
|
||||
orgId?: string;
|
||||
userId: string;
|
||||
email: string;
|
||||
role: 'user';
|
||||
role: UserRoleType;
|
||||
permission?: string[];
|
||||
}
|
||||
|
||||
// For restaurant owners, also resId
|
||||
export interface StaffPayload {
|
||||
iat?: number;
|
||||
exp?: number;
|
||||
userId: string;
|
||||
email: string;
|
||||
role: 'staff';
|
||||
}
|
||||
|
||||
export interface AdminPayload {
|
||||
iat?: number;
|
||||
exp?: number;
|
||||
userId: string;
|
||||
email: string;
|
||||
role: 'admin';
|
||||
}
|
||||
|
||||
export type AuthPayload = UserPayload | StaffPayload | AdminPayload;
|
||||
|
||||
1
src/auth/types/role.ts
Normal file
1
src/auth/types/role.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type UserRoleType = 'user' | 'admin';
|
||||
Reference in New Issue
Block a user