import { Injectable, BadRequestException } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; import { PrismaService } from '../prisma/prisma.service'; import { RedisService } from '../redis/redis.service'; import { SendOtpDto } from './dto/send-otp.dto'; import { VerifyOtpDto } from './dto/verify-otp.dto'; import { SmsService } from '../common/services/sms.service'; import * as bcrypt from 'bcryptjs'; @Injectable() export class AuthService { constructor( private prisma: PrismaService, private jwtService: JwtService, private redisService: RedisService, private smsService: SmsService, ) {} async sendOtp(sendOtpDto: SendOtpDto) { const { phoneNumber } = sendOtpDto; const code = Math.floor(10000 + Math.random() * 90000).toString(); await this.redisService.set(`otp:${phoneNumber}`, code, 120); // Dispatch OTP via MeliPayamak Pattern SMS await this.smsService.sendOtp(phoneNumber, code); return { success: true, message: 'کد تایید با موفقیت به شماره شما پیامک شد.', }; } async verifyOtp(verifyOtpDto: VerifyOtpDto) { const { phoneNumber, code } = verifyOtpDto; const savedCode = await this.redisService.get(`otp:${phoneNumber}`); if (!savedCode) { throw new BadRequestException({ message: 'کد تایید منقضی شده است', error: 'OTP_EXPIRED', }); } if (savedCode !== code) { throw new BadRequestException({ message: 'کد تایید اشتباه است', error: 'OTP_INVALID', }); } await this.redisService.del(`otp:${phoneNumber}`); let user = await this.prisma.user.findUnique({ where: { mobile: phoneNumber }, }); if (!user) { user = await this.prisma.user.create({ data: { mobile: phoneNumber, firstName: 'کاربر', lastName: 'جدید', email: `${phoneNumber}@temp.local`, }, }); } const payload = { sub: user.id, phoneNumber: user.mobile }; const accessToken = this.jwtService.sign(payload); return { success: true, data: { user, accessToken, }, }; } async register(registerDto: any) { const { firstName, lastName, email, mobile, password } = registerDto; const existingUser = await this.prisma.user.findUnique({ where: { mobile }, }); if (existingUser) { throw new BadRequestException({ message: 'کاربری با این شماره موبایل قبلا ثبت نام کرده است', error: 'MOBILE_EXISTS', }); } if (email) { const existingEmail = await this.prisma.user.findUnique({ where: { email }, }); if (existingEmail) { throw new BadRequestException({ message: 'کاربری با این ایمیل قبلا ثبت نام کرده است', error: 'EMAIL_EXISTS', }); } } const hashedPassword = await bcrypt.hash(password, 10); const user = await this.prisma.user.create({ data: { firstName, lastName, email: email || null, mobile, password: hashedPassword, }, }); const payload = { sub: user.id, phoneNumber: user.mobile }; const accessToken = this.jwtService.sign(payload); return { success: true, data: { user, accessToken, }, }; } async login(loginDto: any) { const { mobile, password } = loginDto; const user = await this.prisma.user.findUnique({ where: { mobile } }); if (!user) { throw new BadRequestException({ message: 'نام کاربری یا رمز عبور اشتباه است', error: 'INVALID_CREDENTIALS', }); } if (!user.password) { throw new BadRequestException({ message: 'شما با رمز عبور ثبت نام نکرده‌اید. لطفاً با موبایل وارد شوید', error: 'NO_PASSWORD', }); } const isMatch = await bcrypt.compare(password, user.password); if (!isMatch) { throw new BadRequestException({ message: 'نام کاربری یا رمز عبور اشتباه است', error: 'INVALID_CREDENTIALS', }); } const payload = { sub: user.id, phoneNumber: user.mobile }; const accessToken = this.jwtService.sign(payload); return { success: true, data: { user, accessToken, }, }; } async adminLogin(body: any) { const adminEmail = process.env.ADMIN_EMAIL || 'admin@canina-iran.com'; const adminPassword = process.env.ADMIN_PASSWORD || 'admin123'; // First check database for user with role 'Admin' or 'SUPER_ADMIN' const dbAdmin = await this.prisma.user.findFirst({ where: { email: body.email, role: { in: ['Admin', 'SUPER_ADMIN', 'ADMIN'] } }, }); if (dbAdmin && dbAdmin.password) { const isMatch = await bcrypt.compare(body.password, dbAdmin.password); if (isMatch) { const payload = { sub: dbAdmin.id, email: dbAdmin.email, role: dbAdmin.role }; return { success: true, data: { user: dbAdmin, accessToken: this.jwtService.sign(payload), }, }; } } // Fallback to validated environment variable credentials if (body.email === adminEmail && body.password === adminPassword) { const payload = { sub: '12345678-1234-1234-1234-123456789012', email: body.email, role: 'Admin', }; return { success: true, data: { user: { id: '12345678-1234-1234-1234-123456789012', email: body.email, role: 'Admin', }, accessToken: this.jwtService.sign(payload), }, }; } throw new BadRequestException({ message: 'ایمیل یا رمز عبور اشتباه است', error: 'INVALID_CREDENTIALS', }); } }