Neo platform - First Version
This commit is contained in:
82
backend/src/auth/auth.controller.ts
Normal file
82
backend/src/auth/auth.controller.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import {
|
||||
Controller,
|
||||
Post,
|
||||
Body,
|
||||
UnauthorizedException,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import { IsEmail, IsString, MinLength, IsOptional } from 'class-validator';
|
||||
import { AuthService } from './auth.service';
|
||||
import { TenantId } from '../tenant/tenant.decorator';
|
||||
|
||||
class LoginDto {
|
||||
@IsEmail()
|
||||
email: string;
|
||||
|
||||
@IsString()
|
||||
@MinLength(6)
|
||||
password: string;
|
||||
}
|
||||
|
||||
class RegisterDto {
|
||||
@IsEmail()
|
||||
email: string;
|
||||
|
||||
@IsString()
|
||||
@MinLength(6)
|
||||
password: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
firstName?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
lastName?: string;
|
||||
}
|
||||
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
constructor(private authService: AuthService) {}
|
||||
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Post('login')
|
||||
async login(@TenantId() tenantId: string, @Body() loginDto: LoginDto) {
|
||||
if (!tenantId) {
|
||||
throw new UnauthorizedException('Tenant ID is required');
|
||||
}
|
||||
|
||||
const user = await this.authService.validateUser(
|
||||
tenantId,
|
||||
loginDto.email,
|
||||
loginDto.password,
|
||||
);
|
||||
|
||||
if (!user) {
|
||||
throw new UnauthorizedException('Invalid credentials');
|
||||
}
|
||||
|
||||
return this.authService.login(user);
|
||||
}
|
||||
|
||||
@Post('register')
|
||||
async register(
|
||||
@TenantId() tenantId: string,
|
||||
@Body() registerDto: RegisterDto,
|
||||
) {
|
||||
if (!tenantId) {
|
||||
throw new UnauthorizedException('Tenant ID is required');
|
||||
}
|
||||
|
||||
const user = await this.authService.register(
|
||||
tenantId,
|
||||
registerDto.email,
|
||||
registerDto.password,
|
||||
registerDto.firstName,
|
||||
registerDto.lastName,
|
||||
);
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
24
backend/src/auth/auth.module.ts
Normal file
24
backend/src/auth/auth.module.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { PassportModule } from '@nestjs/passport';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { AuthService } from './auth.service';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { JwtStrategy } from './jwt.strategy';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
PassportModule,
|
||||
JwtModule.registerAsync({
|
||||
inject: [ConfigService],
|
||||
useFactory: (config: ConfigService) => ({
|
||||
secret: config.get<string>('JWT_SECRET', 'devsecret'),
|
||||
signOptions: { expiresIn: '24h' },
|
||||
}),
|
||||
}),
|
||||
],
|
||||
providers: [AuthService, JwtStrategy],
|
||||
controllers: [AuthController],
|
||||
exports: [AuthService],
|
||||
})
|
||||
export class AuthModule {}
|
||||
92
backend/src/auth/auth.service.ts
Normal file
92
backend/src/auth/auth.service.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(
|
||||
private prisma: PrismaService,
|
||||
private jwtService: JwtService,
|
||||
) {}
|
||||
|
||||
async validateUser(
|
||||
tenantId: string,
|
||||
email: string,
|
||||
password: string,
|
||||
): Promise<any> {
|
||||
const user = await this.prisma.user.findUnique({
|
||||
where: {
|
||||
tenantId_email: {
|
||||
tenantId,
|
||||
email,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
tenant: true,
|
||||
userRoles: {
|
||||
include: {
|
||||
role: {
|
||||
include: {
|
||||
rolePermissions: {
|
||||
include: {
|
||||
permission: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (user && (await bcrypt.compare(password, user.password))) {
|
||||
const { password, ...result } = user;
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
async login(user: any) {
|
||||
const payload = {
|
||||
sub: user.id,
|
||||
email: user.email,
|
||||
tenantId: user.tenantId,
|
||||
};
|
||||
|
||||
return {
|
||||
access_token: this.jwtService.sign(payload),
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
tenantId: user.tenantId,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async register(
|
||||
tenantId: string,
|
||||
email: string,
|
||||
password: string,
|
||||
firstName?: string,
|
||||
lastName?: string,
|
||||
) {
|
||||
const hashedPassword = await bcrypt.hash(password, 10);
|
||||
|
||||
const user = await this.prisma.user.create({
|
||||
data: {
|
||||
tenantId,
|
||||
email,
|
||||
password: hashedPassword,
|
||||
firstName,
|
||||
lastName,
|
||||
},
|
||||
});
|
||||
|
||||
const { password: _, ...result } = user;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
8
backend/src/auth/current-user.decorator.ts
Normal file
8
backend/src/auth/current-user.decorator.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
||||
|
||||
export const CurrentUser = createParamDecorator(
|
||||
(data: unknown, ctx: ExecutionContext) => {
|
||||
const request = ctx.switchToHttp().getRequest();
|
||||
return request.user;
|
||||
},
|
||||
);
|
||||
5
backend/src/auth/jwt-auth.guard.ts
Normal file
5
backend/src/auth/jwt-auth.guard.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
|
||||
@Injectable()
|
||||
export class JwtAuthGuard extends AuthGuard('jwt') {}
|
||||
30
backend/src/auth/jwt.strategy.ts
Normal file
30
backend/src/auth/jwt.strategy.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { Strategy } from 'passport-jwt';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { FastifyRequest } from 'fastify';
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(private configService: ConfigService) {
|
||||
super({
|
||||
jwtFromRequest: (request: FastifyRequest) => {
|
||||
const authHeader = request.headers.authorization;
|
||||
if (authHeader && authHeader.startsWith('Bearer ')) {
|
||||
return authHeader.substring(7);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
ignoreExpiration: false,
|
||||
secretOrKey: configService.get<string>('JWT_SECRET', 'devsecret'),
|
||||
});
|
||||
}
|
||||
|
||||
async validate(payload: any) {
|
||||
return {
|
||||
userId: payload.sub,
|
||||
email: payload.email,
|
||||
tenantId: payload.tenantId,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user