feat: remove OTP gate from transactions, fix categories auth, add implementation plan
- Remove OtpGateGuard from transactions controller (OTP verified at login) - Fix categories controller to use authenticated user instead of TEMP_USER_ID - Add comprehensive implementation plan document - Update .env.example with WEB_APP_URL - Prepare for admin dashboard development
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Injectable, BadRequestException, UnauthorizedException } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { getTempUserId } from '../common/user.util';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
@@ -10,4 +11,101 @@ export class UsersService {
|
||||
const userId = getTempUserId();
|
||||
return this.prisma.user.findUnique({ where: { id: userId } });
|
||||
}
|
||||
}
|
||||
|
||||
async updateProfile(userId: string, data: { name?: string; phone?: string }) {
|
||||
try {
|
||||
const user = await this.prisma.user.update({
|
||||
where: { id: userId },
|
||||
data: {
|
||||
...(data.name !== undefined && { name: data.name }),
|
||||
...(data.phone !== undefined && { phone: data.phone }),
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
email: true,
|
||||
name: true,
|
||||
phone: true,
|
||||
avatarUrl: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Profile updated successfully',
|
||||
user,
|
||||
};
|
||||
} catch (error: any) {
|
||||
if (error.code === 'P2002') {
|
||||
throw new BadRequestException('Phone number already in use');
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getAuthInfo(userId: string) {
|
||||
// Get user with password hash and avatar
|
||||
const user = await this.prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
select: {
|
||||
passwordHash: true,
|
||||
avatarUrl: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Check if user has Google OAuth (avatar from Google or starts with /avatars/)
|
||||
const hasGoogleAuth =
|
||||
user?.avatarUrl?.includes('googleusercontent.com') ||
|
||||
user?.avatarUrl?.startsWith('/avatars/') ||
|
||||
false;
|
||||
|
||||
return {
|
||||
hasGoogleAuth,
|
||||
hasPassword: user?.passwordHash !== null,
|
||||
};
|
||||
}
|
||||
|
||||
async deleteAccount(userId: string, password: string) {
|
||||
// Get user with password hash
|
||||
const user = await this.prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
select: {
|
||||
passwordHash: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new BadRequestException('User not found');
|
||||
}
|
||||
|
||||
if (!user.passwordHash) {
|
||||
throw new BadRequestException(
|
||||
'Cannot delete account without password. Please set a password first.',
|
||||
);
|
||||
}
|
||||
|
||||
// Verify password
|
||||
const isValid = await bcrypt.compare(password, user.passwordHash);
|
||||
if (!isValid) {
|
||||
throw new UnauthorizedException('Incorrect password');
|
||||
}
|
||||
|
||||
// Delete related data first (to avoid foreign key constraint errors)
|
||||
// Delete AuthAccount records
|
||||
await this.prisma.authAccount.deleteMany({
|
||||
where: { userId: userId },
|
||||
});
|
||||
|
||||
// Delete other related data if any
|
||||
// Add more deleteMany calls here for other tables that reference User
|
||||
|
||||
// Finally, delete the user
|
||||
await this.prisma.user.delete({
|
||||
where: { id: userId },
|
||||
});
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Account deleted successfully',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user