elearning/Backend/src/controllers/AuthController.ts
JakkrapartXD d7f824f353 feat: standardize login response format with code, message, and data wrapper
Update login endpoint to return consistent API response structure with code, message, and data fields. Wrap token, refreshToken, and user data inside a data object to match the standardized response format used across other endpoints.
2026-01-30 17:54:43 +07:00

226 lines
7.4 KiB
TypeScript

import { Body, Post, Route, Tags, SuccessResponse, Response, Example, Controller, Security, Request } from 'tsoa';
import { AuthService } from '../services/auth.service';
import {
LoginRequest,
RegisterRequest,
RefreshTokenRequest,
ResetRequest,
ResetPasswordRequest,
LoginResponse,
RegisterResponse,
RefreshTokenResponse
} from '../types/auth.types';
import { loginSchema, registerSchema, refreshTokenSchema, resetRequestSchema, resetPasswordSchema } from '../validators/auth.validator';
import { ValidationError } from '../middleware/errorHandler';
@Route('api/auth')
@Tags('Authentication')
export class AuthController {
private authService = new AuthService();
/**
* User login
* @summary Login with email and password
* @param body Login credentials
* @returns JWT token and user information
*/
@Post('login')
@SuccessResponse('200', 'Login successful')
@Response('401', 'Invalid credentials')
@Example<LoginResponse>({
code: 200,
message: 'Login successful',
data: {
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
refreshToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
user: {
id: 1,
username: 'admin',
email: 'admin@elearning.local',
updated_at: new Date('2024-01-01T00:00:00Z'),
created_at: new Date('2024-01-01T00:00:00Z'),
role: {
code: 'ADMIN',
name: {
th: 'ผู้ดูแลระบบ',
en: 'Administrator'
}
},
profile: {
prefix: {
th: 'นาย',
en: 'Mr.'
},
first_name: 'Admin',
last_name: 'User',
phone: null,
avatar_url: null,
birth_date: null
}
}
}
})
public async login(@Body() body: LoginRequest): Promise<LoginResponse> {
// Validate input
const { error, value } = loginSchema.validate(body);
if (error) {
throw new ValidationError(error.details[0].message);
}
return await this.authService.login(body);
}
/**
* User registration
* @summary Register a new instructor account
* @param body Registration data
* @returns Created user information
*/
@Post('register-instructor')
@SuccessResponse('201', 'Registration successful')
@Response('400', 'Validation error')
@Response('409', 'Username or email already exists')
@Example<RegisterResponse>({
user: {
id: 4,
username: 'newinstructor',
email: 'instructor@example.com',
updated_at: new Date('2024-01-01T00:00:00Z'),
created_at: new Date('2024-01-01T00:00:00Z'),
role: {
code: 'INSTRUCTOR',
name: {
th: 'ผู้สอน',
en: 'Instructor'
}
},
profile: {
prefix: {
th: 'นาย',
en: 'Mr.'
},
first_name: 'John',
last_name: 'Doe',
phone: null,
avatar_url: null,
birth_date: null
}
},
message: 'Registration successful'
})
public async registerInstructor(@Body() body: RegisterRequest): Promise<RegisterResponse> {
// Validate input
const { error } = registerSchema.validate(body);
if (error) {
throw new ValidationError(error.details[0].message);
}
return await this.authService.registerInstructor(body);
}
/**
* User registration
* @summary Register a new student account
* @param body Registration data
* @returns Created user information
*/
@Post('register-learner')
@SuccessResponse('201', 'Registration successful')
@Response('400', 'Validation error')
@Response('409', 'Username or email already exists')
@Example<RegisterResponse>({
user: {
id: 4,
username: 'newstudent',
email: 'student@example.com',
updated_at: new Date('2024-01-01T00:00:00Z'),
created_at: new Date('2024-01-01T00:00:00Z'),
role: {
code: 'STUDENT',
name: {
th: 'นักเรียน',
en: 'Student'
}
},
profile: {
prefix: {
th: 'นาย',
en: 'Mr.'
},
first_name: 'John',
last_name: 'Doe',
phone: null,
avatar_url: null,
birth_date: null
}
},
message: 'Registration successful'
})
public async register(@Body() body: RegisterRequest): Promise<RegisterResponse> {
// Validate input
const { error } = registerSchema.validate(body);
if (error) {
throw new ValidationError(error.details[0].message);
}
return await this.authService.register(body);
}
/**
* Refresh access token
* @summary Get a new access token using refresh token
* @param body Refresh token
* @returns New access token and refresh token
*/
@Post('refresh')
@SuccessResponse('200', 'Token refreshed')
@Response('401', 'Invalid or expired refresh token')
@Example<RefreshTokenResponse>({
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
refreshToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
})
public async refreshToken(@Body() body: RefreshTokenRequest): Promise<RefreshTokenResponse> {
// Validate input
const { error } = refreshTokenSchema.validate(body);
if (error) {
throw new ValidationError(error.details[0].message);
}
return await this.authService.refreshToken(body.refreshToken);
}
/**
* Reset password request
* @summary Request a password reset
* @param body Email address
* @returns Success message
*/
@Post('reset-request')
@SuccessResponse('200', 'Reset request successful')
@Response('401', 'Invalid or expired refresh token')
public async resetRequest(@Body() body: ResetRequest): Promise<{ message: string }> {
const { error } = resetRequestSchema.validate(body);
if (error) {
throw new ValidationError(error.details[0].message);
}
return await this.authService.resetRequest(body.email);
}
/**
* Reset password
* @summary Reset password using reset token
* @param body Reset token and new password
* @returns Success message
*/
@Post('reset-password')
@SuccessResponse('200', 'Password reset successful')
@Response('401', 'Invalid or expired reset token')
public async resetPassword(@Body() body: ResetPasswordRequest): Promise<{ message: string }> {
const { error } = resetPasswordSchema.validate(body);
if (error) {
throw new ValidationError(error.details[0].message);
}
return await this.authService.resetPassword(body.token, body.password);
}
}