reset forgoten password tweak

This commit is contained in:
dimitar 2025-04-01 04:51:58 +02:00
parent 239cd14521
commit b38261e387
4 changed files with 127 additions and 105 deletions

View File

@ -34,3 +34,4 @@ DEFAULT_ADMIN_NAME=admin
# CORS_ORIGIN=http://localhost:5173
# NODE_ENV=development
FRONTEND_URL=https://placebo.mk

View File

@ -1,11 +1,17 @@
import { Injectable, ConflictException, Logger, UnauthorizedException, NotFoundException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { PrismaService } from '../prisma/prisma.service';
import * as bcrypt from 'bcrypt';
import { CreateUserDto } from '../dto/create-user.dto';
import { ConfigService } from '@nestjs/config';
import { EmailService } from '../email/email.service';
import { randomBytes } from 'crypto';
import {
Injectable,
ConflictException,
Logger,
UnauthorizedException,
NotFoundException,
} from "@nestjs/common";
import { JwtService } from "@nestjs/jwt";
import { PrismaService } from "../prisma/prisma.service";
import * as bcrypt from "bcrypt";
import { CreateUserDto } from "../dto/create-user.dto";
import { ConfigService } from "@nestjs/config";
import { EmailService } from "../email/email.service";
import { randomBytes } from "crypto";
@Injectable()
export class AuthService {
@ -17,11 +23,11 @@ export class AuthService {
private configService: ConfigService,
private emailService: EmailService,
) {
this.logger.log('AuthService initialized with EmailService');
this.logger.log("AuthService initialized with EmailService");
}
async validateUser(email: string, password: string): Promise<any> {
this.logger.debug('Validating user:', { email });
this.logger.debug("Validating user:", { email });
try {
const user = await this.prisma.user.findUnique({
@ -35,32 +41,34 @@ export class AuthService {
},
});
this.logger.debug('Database query result:', {
this.logger.debug("Database query result:", {
userFound: !!user,
userData: user ? {
id: user.id,
email: user.email,
name: user.name,
isAdmin: user.isAdmin,
} : null,
userData: user
? {
id: user.id,
email: user.email,
name: user.name,
isAdmin: user.isAdmin,
}
: null,
});
if (!user) {
this.logger.debug('User not found:', { email });
this.logger.debug("User not found:", { email });
return null;
}
const isPasswordValid = await bcrypt.compare(password, user.password);
this.logger.debug('Password validation result:', { isPasswordValid });
this.logger.debug("Password validation result:", { isPasswordValid });
if (!isPasswordValid) {
this.logger.debug('Invalid password for user:', { email });
this.logger.debug("Invalid password for user:", { email });
return null;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { password: _, ...result } = user;
this.logger.debug('User validated successfully:', {
this.logger.debug("User validated successfully:", {
id: result.id,
email: result.email,
name: result.name,
@ -69,7 +77,7 @@ export class AuthService {
return result;
} catch (error) {
this.logger.error('Error validating user:', {
this.logger.error("Error validating user:", {
error: error.message,
stack: error.stack,
});
@ -78,7 +86,7 @@ export class AuthService {
}
async login(user: any) {
this.logger.debug('Login called with user:', {
this.logger.debug("Login called with user:", {
id: user.id,
email: user.email,
name: user.name,
@ -91,13 +99,13 @@ export class AuthService {
sub: user.id,
};
this.logger.debug('Generated JWT payload:', payload);
this.logger.debug("Generated JWT payload:", payload);
const token = this.jwtService.sign(payload, {
secret: this.configService.get<string>('JWT_SECRET'),
secret: this.configService.get<string>("JWT_SECRET"),
});
this.logger.debug('JWT token generated successfully');
this.logger.debug("JWT token generated successfully");
return {
access_token: token,
@ -109,7 +117,7 @@ export class AuthService {
},
};
} catch (error) {
this.logger.error('Error generating JWT token:', {
this.logger.error("Error generating JWT token:", {
error: error.message,
stack: error.stack,
});
@ -121,33 +129,41 @@ export class AuthService {
createUserDto: CreateUserDto,
isAdmin: boolean = false,
): Promise<any> {
console.log('=== Starting user creation process ===');
this.logger.log('=== Starting user creation process ===');
console.log('Creating user:', { ...createUserDto, isAdmin, password: '[REDACTED]' });
this.logger.log('Creating user:', { ...createUserDto, isAdmin, password: '[REDACTED]' });
console.log("=== Starting user creation process ===");
this.logger.log("=== Starting user creation process ===");
console.log("Creating user:", {
...createUserDto,
isAdmin,
password: "[REDACTED]",
});
this.logger.log("Creating user:", {
...createUserDto,
isAdmin,
password: "[REDACTED]",
});
try {
// Check for existing user
console.log('Checking for existing user...');
this.logger.log('Checking for existing user...');
console.log("Checking for existing user...");
this.logger.log("Checking for existing user...");
const existingUser = await this.prisma.user.findUnique({
where: { email: createUserDto.email },
});
if (existingUser) {
console.log('User already exists:', createUserDto.email);
this.logger.warn('User already exists:', createUserDto.email);
throw new ConflictException('Email already exists');
console.log("User already exists:", createUserDto.email);
this.logger.warn("User already exists:", createUserDto.email);
throw new ConflictException("Email already exists");
}
// Hash password
console.log('Hashing password...');
this.logger.log('Hashing password...');
console.log("Hashing password...");
this.logger.log("Hashing password...");
const hashedPassword = await bcrypt.hash(createUserDto.password, 10);
// Create user
console.log('Creating user in database...');
this.logger.log('Creating user in database...');
console.log("Creating user in database...");
this.logger.log("Creating user in database...");
const newUser = await this.prisma.user.create({
data: {
email: createUserDto.email,
@ -157,22 +173,28 @@ export class AuthService {
},
});
console.log('User created successfully:', { id: newUser.id, email: newUser.email });
this.logger.log('User created successfully:', { id: newUser.id, email: newUser.email });
console.log("User created successfully:", {
id: newUser.id,
email: newUser.email,
});
this.logger.log("User created successfully:", {
id: newUser.id,
email: newUser.email,
});
// Send welcome email
console.log('Attempting to send welcome email...');
this.logger.log('Attempting to send welcome email...');
console.log("Attempting to send welcome email...");
this.logger.log("Attempting to send welcome email...");
try {
console.log('Calling EmailService.sendWelcomeEmail...');
this.logger.log('Calling EmailService.sendWelcomeEmail...');
console.log("Calling EmailService.sendWelcomeEmail...");
this.logger.log("Calling EmailService.sendWelcomeEmail...");
await this.emailService.sendWelcomeEmail(newUser.email, newUser.name);
console.log('Welcome email sent successfully');
this.logger.log('Welcome email sent successfully');
console.log("Welcome email sent successfully");
this.logger.log("Welcome email sent successfully");
} catch (emailError) {
console.error('Failed to send welcome email:', emailError);
this.logger.error('Failed to send welcome email:', {
console.error("Failed to send welcome email:", emailError);
this.logger.error("Failed to send welcome email:", {
error: emailError.message,
code: emailError.code,
command: emailError.command,
@ -184,12 +206,12 @@ export class AuthService {
// Return user data
const { password, ...result } = newUser;
console.log('=== User creation completed ===');
this.logger.log('=== User creation completed ===');
console.log("=== User creation completed ===");
this.logger.log("=== User creation completed ===");
return result;
} catch (error) {
console.error('Error in createUser:', error);
this.logger.error('Error in createUser:', {
console.error("Error in createUser:", error);
this.logger.error("Error in createUser:", {
error: error.message,
code: error.code,
command: error.command,
@ -212,25 +234,25 @@ export class AuthService {
// }
async getUserInfo(userId: number) {
if (!userId) {
throw new Error('User ID is required');
throw new Error("User ID is required");
}
return this.prisma.user.findUnique({
where: {
id: userId, // Make sure userId is properly passed and converted to number if needed
id: userId, // Make sure userId is properly passed and converted to number if needed
},
select: {
id: true,
name: true,
email: true,
isAdmin: true
}
isAdmin: true,
},
});
}
async sendPasswordResetToken(email: string) {
this.logger.log('=== Starting password reset token process ===');
this.logger.debug('Reset token requested for:', email);
this.logger.log("=== Starting password reset token process ===");
this.logger.debug("Reset token requested for:", email);
try {
// Find the user
@ -244,13 +266,15 @@ export class AuthService {
});
if (!user) {
this.logger.warn('User not found for password reset:', email);
this.logger.warn("User not found for password reset:", email);
// Return success anyway to prevent email enumeration
return { message: 'If an account exists, a password reset link has been sent' };
return {
message: "If an account exists, a password reset link has been sent",
};
}
// Generate reset token
const token = randomBytes(32).toString('hex');
const token = randomBytes(32).toString("hex");
const expiresAt = new Date();
expiresAt.setHours(expiresAt.getHours() + 1); // Token expires in 1 hour
@ -268,11 +292,11 @@ export class AuthService {
await this.emailService.sendPasswordResetEmail(
user.email,
user.name,
token
token,
);
this.logger.debug('Password reset email sent successfully');
this.logger.debug("Password reset email sent successfully");
} catch (emailError) {
this.logger.error('Failed to send password reset email:', {
this.logger.error("Failed to send password reset email:", {
error: emailError.message,
code: emailError.code,
stack: emailError.stack,
@ -280,9 +304,11 @@ export class AuthService {
throw emailError;
}
return { message: 'If an account exists, a password reset link has been sent' };
return {
message: "If an account exists, a password reset link has been sent",
};
} catch (error) {
this.logger.error('Error in sendPasswordResetToken:', {
this.logger.error("Error in sendPasswordResetToken:", {
error: error.message,
code: error.code,
stack: error.stack,
@ -292,8 +318,8 @@ export class AuthService {
}
async resetPasswordWithToken(token: string, newPassword: string) {
this.logger.log('=== Starting password reset with token process ===');
this.logger.debug('Password reset attempted with token');
this.logger.log("=== Starting password reset with token process ===");
this.logger.debug("Password reset attempted with token");
try {
// Find valid reset token
@ -311,8 +337,8 @@ export class AuthService {
});
if (!resetRecord) {
this.logger.warn('Invalid or expired reset token used');
throw new UnauthorizedException('Invalid or expired reset token');
this.logger.warn("Invalid or expired reset token used");
throw new UnauthorizedException("Invalid or expired reset token");
}
// Hash new password
@ -330,27 +356,29 @@ export class AuthService {
}),
]);
this.logger.log('Password reset successful for user:', resetRecord.userId);
this.logger.log(
"Password reset successful for user:",
resetRecord.userId,
);
// Send confirmation email
try {
await this.emailService.sendPasswordChangeConfirmation(
resetRecord.user.email,
resetRecord.user.name
resetRecord.user.name,
);
this.logger.debug('Password change confirmation email sent');
this.logger.debug("Password change confirmation email sent");
} catch (emailError) {
this.logger.error('Failed to send password change confirmation:', {
this.logger.error("Failed to send password change confirmation:", {
error: emailError.message,
code: emailError.code,
stack: emailError.stack,
});
// Don't throw error here as password is already changed
}
return { message: 'Password reset successful' };
return { message: "Password reset successful" };
} catch (error) {
this.logger.error('Error in resetPasswordWithToken:', {
this.logger.error("Error in resetPasswordWithToken:", {
error: error.message,
code: error.code,
stack: error.stack,

View File

@ -1,17 +1,10 @@
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { Injectable } from "@nestjs/common";
import { PrismaService } from "../prisma/prisma.service";
@Injectable()
export class ClientService {
constructor(private prisma: PrismaService) {}
// async getClientDocuments(userId: string) {
// return this.prisma.document.findMany({
// where: {
// authorId: Number(userId),
// },
// });
// }
async getDocuments(userId: string) {
return this.prisma.document.findMany({
where: {

View File

@ -19,7 +19,7 @@ export class EmailService {
const pass = this.configService.get<string>("SMTP_PASS");
this.from = this.configService.get<string>("EMAIL_FROM");
console.log("Email Config:", { host, port, user, from: this.from }); // Direct console log
console.log("Email Config:", { host, port, user, from: this.from });
this.logger.log("Email Config:", { host, port, user, from: this.from });
this.transporter = nodemailer.createTransport({
@ -27,8 +27,8 @@ export class EmailService {
host: host,
port: port,
auth: { user, pass },
debug: true, // Enable debug logs
logger: true, // Enable transport level logging
debug: true,
logger: true,
});
// Verify connection
@ -37,15 +37,15 @@ export class EmailService {
private async verifyConnection() {
try {
console.log("Verifying SMTP connection..."); // Direct console log
console.log("Verifying SMTP connection...");
this.logger.log("Verifying SMTP connection...");
const verification = await this.transporter.verify();
console.log("SMTP connection verified successfully!", verification); // Direct console log
console.log("SMTP connection verified successfully!", verification);
this.logger.log("SMTP connection verified successfully!", verification);
} catch (error) {
console.error("SMTP connection failed:", error); // Direct console log
console.error("SMTP connection failed:", error);
this.logger.error("SMTP connection failed:", {
error: error.message,
code: error.code,
@ -59,7 +59,7 @@ export class EmailService {
}
async sendWelcomeEmail(userEmail: string, username: string): Promise<void> {
console.log(`Sending welcome email to ${userEmail}...`); // Direct console log
console.log(`Sending welcome email to ${userEmail}...`);
this.logger.log(`Sending welcome email to ${userEmail}...`);
const mailOptions = {