feat: updade workspace

This commit is contained in:
thanhnv
2026-07-11 15:56:31 +09:00
parent 4fc72332f5
commit 193a449829
120 changed files with 868 additions and 350 deletions
@@ -0,0 +1,25 @@
import { Body, Controller, Inject, Post, Res, ValidationPipe } from '@nestjs/common';
import type { Response } from 'express';
import { ok } from '../common/api-response.js';
import { AuthService } from './auth.service.js';
import { LoginDto } from './dto/login.dto.js';
@Controller('auth')
export class AuthController {
constructor(@Inject(AuthService) private readonly authService: AuthService) {}
@Post('login')
async login(
@Body(new ValidationPipe({ expectedType: LoginDto, whitelist: true, forbidNonWhitelisted: true })) dto: LoginDto,
@Res({ passthrough: true }) response: Response,
) {
const result = await this.authService.login(dto.username, dto.password);
response.cookie('okr_token', result.token, {
httpOnly: true,
sameSite: 'lax',
secure: false,
maxAge: 2 * 60 * 60 * 1000,
});
return ok(result);
}
}
+11
View File
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { PrismaModule } from '../prisma/prisma.module.js';
import { AuthController } from './auth.controller.js';
import { AuthService } from './auth.service.js';
@Module({
imports: [PrismaModule],
controllers: [AuthController],
providers: [AuthService],
})
export class AuthModule {}
+57
View File
@@ -0,0 +1,57 @@
import { Inject, Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import bcrypt from 'bcryptjs';
import { PrismaService } from '../prisma/prisma.service.js';
export interface LoginResult {
token: string;
user: {
id: number;
name: string;
email: string;
username: string;
role: string;
};
}
@Injectable()
export class AuthService {
constructor(
@Inject(PrismaService) private readonly prisma: PrismaService,
@Inject(JwtService) private readonly jwtService: JwtService,
) {}
async login(usernameOrEmail: string, password: string): Promise<LoginResult> {
const user = await this.prisma.user.findFirst({
where: {
OR: [{ username: usernameOrEmail }, { email: usernameOrEmail }],
},
});
if (user === null) {
throw new UnauthorizedException('Invalid credentials');
}
const validPassword = await bcrypt.compare(password, user.passwordHash);
if (!validPassword) {
throw new UnauthorizedException('Invalid credentials');
}
const token = await this.jwtService.signAsync({
sub: user.id,
email: user.email,
role: user.role,
name: user.name,
});
return {
token,
user: {
id: user.id,
name: user.name,
username: user.username,
email: user.email,
role: user.role,
},
};
}
}
@@ -0,0 +1,10 @@
import { IsString, MinLength } from 'class-validator';
export class LoginDto {
@IsString()
username!: string;
@IsString()
@MinLength(8)
password!: string;
}