api login
This commit is contained in:
parent
bd0daf5858
commit
1a7473362b
8 changed files with 613 additions and 60 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { Body, Post, Route, Tags, SuccessResponse, Response, Example } from 'tsoa';
|
||||
import { Body, Post, Route, Tags, SuccessResponse, Response, Example, Controller } from 'tsoa';
|
||||
import { AuthService } from '../services/auth.service';
|
||||
import {
|
||||
LoginRequest,
|
||||
|
|
@ -18,7 +18,7 @@ export class AuthController {
|
|||
|
||||
/**
|
||||
* User login
|
||||
* @summary Login with username and password
|
||||
* @summary Login with email and password
|
||||
* @param body Login credentials
|
||||
* @returns JWT token and user information
|
||||
*/
|
||||
|
|
@ -52,7 +52,8 @@ export class AuthController {
|
|||
})
|
||||
public async login(@Body() body: LoginRequest): Promise<LoginResponse> {
|
||||
// Validate input
|
||||
const { error } = loginSchema.validate(body);
|
||||
const { error, value } = loginSchema.validate(body);
|
||||
|
||||
if (error) {
|
||||
throw new ValidationError(error.details[0].message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,34 +19,33 @@ export class AuthService {
|
|||
* User login
|
||||
*/
|
||||
async login(data: LoginRequest): Promise<LoginResponse> {
|
||||
const { username, password } = data;
|
||||
const { email, password } = data;
|
||||
|
||||
// Find user with role and profile
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { username },
|
||||
where: { email },
|
||||
include: {
|
||||
role: true,
|
||||
profile: true
|
||||
}
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
logger.warn('Login attempt with invalid username', { username });
|
||||
throw new UnauthorizedError('Invalid username or password');
|
||||
logger.warn('Login attempt with invalid email', { email });
|
||||
throw new UnauthorizedError('Invalid email or password');
|
||||
}
|
||||
|
||||
// Verify password
|
||||
const isPasswordValid = await bcrypt.compare(password, user.password);
|
||||
if (!isPasswordValid) {
|
||||
logger.warn('Login attempt with invalid password', { username });
|
||||
throw new UnauthorizedError('Invalid username or password');
|
||||
logger.warn('Login attempt with invalid password', { email });
|
||||
throw new UnauthorizedError('Invalid email or password');
|
||||
}
|
||||
|
||||
// Generate tokens
|
||||
const token = this.generateAccessToken(user.id, user.username, user.email, user.role.code);
|
||||
const token = this.generateAccessToken(user.id, user.email, user.email, user.role.code);
|
||||
const refreshToken = this.generateRefreshToken(user.id);
|
||||
|
||||
logger.info('User logged in successfully', { userId: user.id, username: user.username });
|
||||
logger.info('User logged in successfully', { userId: user.id, email: user.email });
|
||||
|
||||
return {
|
||||
token,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
|
||||
export interface LoginRequest {
|
||||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
|
|
@ -17,6 +17,7 @@ export interface RegisterRequest {
|
|||
th?: string;
|
||||
en?: string;
|
||||
};
|
||||
phone: string;
|
||||
}
|
||||
|
||||
export interface LoginResponse {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
import Joi from 'joi';
|
||||
|
||||
export const loginSchema = Joi.object({
|
||||
username: Joi.string()
|
||||
.min(3)
|
||||
.max(50)
|
||||
email: Joi.string()
|
||||
.email({ tlds: { allow: false } }) // Allow any TLD including .local
|
||||
.required()
|
||||
.messages({
|
||||
'string.min': 'Username must be at least 3 characters',
|
||||
'string.max': 'Username must not exceed 50 characters',
|
||||
'any.required': 'Username is required'
|
||||
'string.email': 'Please provide a valid email address',
|
||||
'any.required': 'Email is required'
|
||||
}),
|
||||
password: Joi.string()
|
||||
.min(6)
|
||||
|
|
@ -47,6 +45,10 @@ export const registerSchema = Joi.object({
|
|||
'string.max': 'Password must not exceed 100 characters',
|
||||
'any.required': 'Password is required'
|
||||
}),
|
||||
prefix: Joi.object({
|
||||
th: Joi.string().optional(),
|
||||
en: Joi.string().optional()
|
||||
}).optional(),
|
||||
first_name: Joi.string()
|
||||
.min(1)
|
||||
.max(100)
|
||||
|
|
@ -61,10 +63,13 @@ export const registerSchema = Joi.object({
|
|||
.messages({
|
||||
'any.required': 'Last name is required'
|
||||
}),
|
||||
prefix: Joi.object({
|
||||
th: Joi.string().optional(),
|
||||
en: Joi.string().optional()
|
||||
}).optional()
|
||||
phone: Joi.string()
|
||||
.min(10)
|
||||
.max(15)
|
||||
.required()
|
||||
.messages({
|
||||
'any.required': 'Phone number is required'
|
||||
})
|
||||
});
|
||||
|
||||
export const refreshTokenSchema = Joi.object({
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue