forked from noxious/server
166 lines
4.6 KiB
TypeScript
166 lines
4.6 KiB
TypeScript
import bcrypt from 'bcryptjs'
|
|
import UserRepository from '../repositories/userRepository'
|
|
import PasswordResetTokenRepository from '../repositories/passwordResetTokenRepository'
|
|
import prisma from '../utilities/prisma'
|
|
import { User } from '@prisma/client'
|
|
import config from '../utilities/config'
|
|
import NodeMailer from 'nodemailer'
|
|
import { httpLogger } from '../utilities/logger'
|
|
import PasswordResetTokenService from './passwordResetTokenService'
|
|
|
|
/**
|
|
* User service
|
|
* Handles user login and registration
|
|
* @class UserService
|
|
*/
|
|
class UserService {
|
|
/**
|
|
* Login user
|
|
* @param username
|
|
* @param password
|
|
*/
|
|
async login(username: string, password: string): Promise<boolean | User> {
|
|
try {
|
|
const user = await UserRepository.getByUsername(username)
|
|
if (!user) {
|
|
return false
|
|
}
|
|
|
|
const passwordMatch = await bcrypt.compare(password, user.password)
|
|
if (!passwordMatch) {
|
|
httpLogger.error(`Failed to login user: ${username}`)
|
|
return false
|
|
}
|
|
|
|
return user
|
|
} catch (error: any) {
|
|
httpLogger.error(`Error logging in user: ${error instanceof Error ? error.message : String(error)}`)
|
|
return false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register user
|
|
* @param username
|
|
* @param email
|
|
* @param password
|
|
*/
|
|
async register(username: string, email: string, password: string): Promise<boolean | User> {
|
|
try {
|
|
const user = await UserRepository.getByUsername(username)
|
|
if (user) {
|
|
return false
|
|
}
|
|
|
|
const userByEmail = await UserRepository.getByEmail(email)
|
|
if (userByEmail) {
|
|
httpLogger.error(`User already exists: ${email}`)
|
|
return false
|
|
}
|
|
|
|
const hashedPassword = await bcrypt.hash(password, 10)
|
|
return prisma.user.create({
|
|
data: {
|
|
username,
|
|
email,
|
|
password: hashedPassword
|
|
}
|
|
})
|
|
} catch (error: any) {
|
|
httpLogger.error(`Error registering user: ${error instanceof Error ? error.message : String(error)}`)
|
|
return false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reset password
|
|
* @param email
|
|
*/
|
|
async requestPasswordReset(email: string): Promise<boolean> {
|
|
try {
|
|
const user = await UserRepository.getByEmail(email)
|
|
if (!user) return false
|
|
|
|
const token = await bcrypt.hash(new Date().getTime().toString(), 10)
|
|
const latestToken = await PasswordResetTokenRepository.getByUserId(user.id)
|
|
|
|
// Check if password reset has been requested recently
|
|
if (latestToken) {
|
|
const tokenExpiryDate = new Date(Date.now() - 24 * 60 * 60 * 1000) // 24 hours
|
|
const isTokenExpired = latestToken.createdAt < tokenExpiryDate
|
|
|
|
if (!isTokenExpired) return false
|
|
|
|
await prisma.passwordResetToken.delete({
|
|
where: {
|
|
id: latestToken.id
|
|
}
|
|
})
|
|
}
|
|
|
|
await prisma.passwordResetToken.create({
|
|
data: {
|
|
userId: user.id,
|
|
token: token
|
|
}
|
|
})
|
|
|
|
const transporter = NodeMailer.createTransport({
|
|
host: config.SMTP_HOST,
|
|
port: config.SMTP_PORT,
|
|
secure: false,
|
|
auth: {
|
|
user: config.SMTP_USER,
|
|
pass: config.SMTP_PASSWORD
|
|
}
|
|
})
|
|
|
|
await transporter.sendMail({
|
|
from: config.SMTP_USER,
|
|
to: email,
|
|
subject: 'Reset your password',
|
|
text: 'A password reset has been requested, reset your password here: ' + config.CLIENT_URL + '#' + token, // Plain text body
|
|
html: "<p>A password reset has been requested, reset your password here: <a href='" + config.CLIENT_URL + '#' + token + "'>" + config.CLIENT_URL + '#' + token + '</a></p>' // Html body
|
|
})
|
|
|
|
return true
|
|
} catch (error: any) {
|
|
httpLogger.error(`Error sending password reset email: ${error instanceof Error ? error.message : String(error)}`)
|
|
return false
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set new password
|
|
* @param urlToken
|
|
* @param password
|
|
*/
|
|
async resetPassword(urlToken: string, password: string): Promise<boolean> {
|
|
try {
|
|
const tokenData = await PasswordResetTokenRepository.getByToken(urlToken)
|
|
if (!tokenData) {
|
|
return false
|
|
}
|
|
|
|
const hashedPassword = await bcrypt.hash(password, 10)
|
|
await prisma.user.update({
|
|
where: { id: tokenData.userId },
|
|
data: {
|
|
password: hashedPassword
|
|
}
|
|
})
|
|
|
|
// Delete the token
|
|
const passwordResetTokenService = new PasswordResetTokenService()
|
|
await passwordResetTokenService.delete(urlToken)
|
|
|
|
return true
|
|
} catch (error: any) {
|
|
httpLogger.error(`Error setting new password: ${error instanceof Error ? error.message : String(error)}`)
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
export default UserService
|