Neo platform - First Version

This commit is contained in:
Francisco Gaona
2025-11-25 12:21:14 +01:00
commit 484af68571
59 changed files with 3699 additions and 0 deletions

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

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

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

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

View File

@@ -0,0 +1,5 @@
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}

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