const SERVICE_NAME = "recoveryModule"; class RecoveryModule { constructor({ User, RecoveryToken, crypto, stringService }) { this.User = User; this.RecoveryToken = RecoveryToken; this.crypto = crypto; this.stringService = stringService; } requestRecoveryToken = async (email) => { try { // Delete any existing tokens await this.RecoveryToken.deleteMany({ email }); let recoveryToken = new this.RecoveryToken({ email, token: this.crypto.randomBytes(32).toString("hex"), }); await recoveryToken.save(); return recoveryToken; } catch (error) { error.service = SERVICE_NAME; error.method = "requestRecoveryToken"; throw error; } }; validateRecoveryToken = async (candidateToken) => { try { const recoveryToken = await this.RecoveryToken.findOne({ token: candidateToken, }); if (recoveryToken !== null) { return recoveryToken; } else { throw new Error(this.stringService.dbTokenNotFound); } } catch (error) { error.service = SERVICE_NAME; error.method = "validateRecoveryToken"; throw error; } }; resetPassword = async (password, candidateToken) => { try { const newPassword = password; // Validate token again const recoveryToken = await this.validateRecoveryToken(candidateToken); const user = await this.User.findOne({ email: recoveryToken.email }); if (user === null) { throw new Error(this.stringService.dbUserNotFound); } const match = await user.comparePassword(newPassword); if (match === true) { throw new Error("Password cannot be the same as the old password"); } user.password = newPassword; await user.save(); await this.RecoveryToken.deleteMany({ email: recoveryToken.email }); // Fetch the user again without the password const userWithoutPassword = await this.User.findOne({ email: recoveryToken.email, }) .select("-password") .select("-profileImage"); return userWithoutPassword; } catch (error) { error.service = SERVICE_NAME; error.method = "resetPassword"; throw error; } }; } export default RecoveryModule;