feat: User services

This commit is contained in:
sauravdhakal12
2026-02-20 15:53:45 +05:45
parent 9561693cb4
commit f6bce78aee
39 changed files with 6509 additions and 66 deletions

View File

@@ -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 {}

View File

@@ -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() {}
}

View File

@@ -0,0 +1,2 @@
export * from './public.decorator';
export * from './role.decorator';

View 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);

View 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
View File

@@ -0,0 +1 @@
export * from './register-user.dto';

View 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;
}

View 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
View File

View 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;
}
}

View File

@@ -1 +1,2 @@
export * from './jwt';
export * from './role';

View File

@@ -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
View File

@@ -0,0 +1 @@
export type UserRoleType = 'user' | 'admin';