chage api use token

This commit is contained in:
JakkrapartXD 2026-01-14 13:42:54 +07:00
parent 6239159099
commit d8a9909eb9
4 changed files with 17 additions and 23 deletions

View file

@ -1,4 +1,4 @@
import { Body, Post, Route, Tags, SuccessResponse, Response, Example, Controller, Security } from 'tsoa'; import { Body, Post, Route, Tags, SuccessResponse, Response, Example, Controller, Security, Request } from 'tsoa';
import { AuthService } from '../services/auth.service'; import { AuthService } from '../services/auth.service';
import { import {
LoginRequest, LoginRequest,
@ -170,7 +170,7 @@ export class AuthController {
if (error) { if (error) {
throw new ValidationError(error.details[0].message); throw new ValidationError(error.details[0].message);
} }
return await this.authService.resetPassword(body.id, body.token, body.password); return await this.authService.resetPassword(body.token, body.password);
} }
/** /**
@ -183,11 +183,15 @@ export class AuthController {
@Security('jwt') @Security('jwt')
@SuccessResponse('200', 'Password changed successfully') @SuccessResponse('200', 'Password changed successfully')
@Response('401', 'Invalid or expired reset token') @Response('401', 'Invalid or expired reset token')
public async changePassword(@Body() body: ChangePassword): Promise<{ message: string }> { public async changePassword(@Request() request: any, @Body() body: ChangePassword): Promise<{ message: string }> {
const { error } = changePasswordSchema.validate(body); const { error } = changePasswordSchema.validate(body);
if (error) { if (error) {
throw new ValidationError(error.details[0].message); throw new ValidationError(error.details[0].message);
} }
return await this.authService.changePassword(body.id, body.oldPassword, body.newPassword); const token = request.headers.authorization?.replace('Bearer ', '');
if (!token) {
throw new ValidationError('No token provided');
}
return await this.authService.changePassword(token, body.oldPassword, body.newPassword);
} }
} }

View file

@ -188,7 +188,7 @@ export class AuthService {
const token = jwt.sign({ id: user.id, email: user.email }, config.jwt.secret, { expiresIn: '1h' }); const token = jwt.sign({ id: user.id, email: user.email }, config.jwt.secret, { expiresIn: '1h' });
// Create reset URL // Create reset URL
const resetURL = `${process.env.FRONTEND_URL || 'http://localhost:3000'}/reset-password?id=${user.id}&token=${token}`; const resetURL = `${process.env.FRONTEND_URL || 'http://localhost:3000'}/reset-password?token=${token}`;
// Create transporter // Create transporter
const transporter = nodemailer.createTransport({ const transporter = nodemailer.createTransport({
@ -231,9 +231,10 @@ export class AuthService {
} }
} }
async resetPassword(id: number, token: string, password: string): Promise<ResetPasswordResponse> { async resetPassword(token: string, password: string): Promise<ResetPasswordResponse> {
try { try {
const user = await prisma.user.findUnique({ where: { id } }); const decoded = jwt.verify(token, config.jwt.secret) as { id: number; email: string };
const user = await prisma.user.findUnique({ where: { id: decoded.id } });
if (!user) throw new UnauthorizedError('User not found'); if (!user) throw new UnauthorizedError('User not found');
const secret = config.jwt.secret; const secret = config.jwt.secret;
@ -254,14 +255,15 @@ export class AuthService {
message: 'Password reset successfully' message: 'Password reset successfully'
}; };
} catch (error) { } catch (error) {
logger.error('Failed to reset password', { id, error }); logger.error('Failed to reset password', { error });
throw error; throw error;
} }
} }
async changePassword(id: number, oldPassword: string, newPassword: string): Promise<ChangePasswordResponse> { async changePassword(token: string, oldPassword: string, newPassword: string): Promise<ChangePasswordResponse> {
try { try {
const user = await prisma.user.findUnique({ where: { id } }); const decoded = jwt.verify(token, config.jwt.secret) as { id: number; username: string; email: string; roleCode: string };
const user = await prisma.user.findUnique({ where: { id: decoded.id } });
if (!user) throw new UnauthorizedError('User not found'); if (!user) throw new UnauthorizedError('User not found');
const isPasswordValid = await bcrypt.compare(oldPassword, user.password); const isPasswordValid = await bcrypt.compare(oldPassword, user.password);
@ -280,7 +282,7 @@ export class AuthService {
message: 'Password changed successfully' message: 'Password changed successfully'
}; };
} catch (error) { } catch (error) {
logger.error('Failed to change password', { id, error }); logger.error('Failed to change password', { error });
throw error; throw error;
} }
} }

View file

@ -48,13 +48,11 @@ export interface ResetRequest {
} }
export interface ResetPasswordRequest { export interface ResetPasswordRequest {
id: number;
token: string; token: string;
password: string; password: string;
} }
export interface ChangePassword { export interface ChangePassword {
id: number;
oldPassword: string; oldPassword: string;
newPassword: string; newPassword: string;
} }

View file

@ -81,11 +81,6 @@ export const refreshTokenSchema = Joi.object({
}); });
export const resetPasswordSchema = Joi.object({ export const resetPasswordSchema = Joi.object({
id: Joi.number()
.required()
.messages({
'any.required': 'User ID is required'
}),
token: Joi.string() token: Joi.string()
.required() .required()
.messages({ .messages({
@ -103,11 +98,6 @@ export const resetPasswordSchema = Joi.object({
}); });
export const changePasswordSchema = Joi.object({ export const changePasswordSchema = Joi.object({
id: Joi.number()
.required()
.messages({
'any.required': 'User ID is required'
}),
oldPassword: Joi.string() oldPassword: Joi.string()
.min(6) .min(6)
.max(100) .max(100)