This commit is contained in:
JakkrapartXD 2026-01-13 03:10:44 +00:00
parent a3da5c9d55
commit f026c14f0c
9 changed files with 7286 additions and 6 deletions

View file

@ -10,9 +10,13 @@ import {
LoginResponse,
RegisterResponse,
RefreshTokenResponse,
UserResponse
UserResponse,
ResetPasswordResponse,
ChangePasswordResponse,
ResetRequestResponse
} from '../types/auth.types';
import { UnauthorizedError, ValidationError, ForbiddenError } from '../middleware/errorHandler';
import nodemailer from 'nodemailer';
export class AuthService {
/**
@ -172,6 +176,115 @@ export class AuthService {
}
}
/**
* Reset request
*/
async resetRequest(email: string): Promise<ResetRequestResponse> {
try {
// Find user
const user = await prisma.user.findUnique({ where: { email } });
if (!user) throw new UnauthorizedError('User not found');
const token = jwt.sign({ id: user.id, email: user.email }, config.jwt.secret, { expiresIn: '1h' });
// Create reset URL
const resetURL = `${process.env.FRONTEND_URL || 'http://localhost:3000'}/reset-password?id=${user.id}&token=${token}`;
// Create transporter
const transporter = nodemailer.createTransport({
host: config.smtp.host,
port: config.smtp.port,
secure: config.smtp.secure,
...(config.smtp.user && config.smtp.pass && {
auth: {
user: config.smtp.user,
pass: config.smtp.pass
}
})
});
// Send email
const mailOptions = {
from: config.smtp.from,
to: user.email,
subject: 'Password Reset Request',
text: `You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\nPlease click on the following link, or paste this into your browser to complete the process:\n\n${resetURL}\n\nIf you did not request this, please ignore this email and your password will remain unchanged.\n`,
html: `
<h2>Password Reset Request</h2>
<p>You are receiving this because you (or someone else) have requested the reset of the password for your account.</p>
<p>Please click on the following link to complete the process:</p>
<p><a href="${resetURL}">${resetURL}</a></p>
<p>If you did not request this, please ignore this email and your password will remain unchanged.</p>
`
};
await transporter.sendMail(mailOptions);
logger.info('Password reset email sent', { email: user.email });
return {
code: 200,
message: 'Password reset email sent successfully'
};
} catch (error) {
logger.error('Failed to send password reset email', { email, error });
throw error;
}
}
async resetPassword(id: number, token: string, password: string): Promise<ResetPasswordResponse> {
try {
const user = await prisma.user.findUnique({ where: { id } });
if (!user) throw new UnauthorizedError('User not found');
const secret = config.jwt.secret;
const verify = jwt.verify(token, secret);
const encryptedPassword = await bcrypt.hash(password, 10);
if (!verify) throw new UnauthorizedError('Invalid token');
await prisma.user.update({
where: { id: user.id },
data: { password: encryptedPassword }
});
logger.info('Password reset successfully', { userId: user.id });
return {
code: 200,
message: 'Password reset successfully'
};
} catch (error) {
logger.error('Failed to reset password', { id, error });
throw error;
}
}
async changePassword(id: number, oldPassword: string, newPassword: string): Promise<ChangePasswordResponse> {
try {
const user = await prisma.user.findUnique({ where: { id } });
if (!user) throw new UnauthorizedError('User not found');
const isPasswordValid = await bcrypt.compare(oldPassword, user.password);
if (!isPasswordValid) throw new UnauthorizedError('Invalid password');
const encryptedPassword = await bcrypt.hash(newPassword, 10);
await prisma.user.update({
where: { id: user.id },
data: { password: encryptedPassword }
});
logger.info('Password changed successfully', { userId: user.id });
return {
code: 200,
message: 'Password changed successfully'
};
} catch (error) {
logger.error('Failed to change password', { id, error });
throw error;
}
}
/**
* Generate access token (JWT)
*/
@ -222,4 +335,6 @@ export class AuthService {
} : undefined
};
}
}