fix auth login

This commit is contained in:
2026-02-09 10:59:15 +03:30
parent 2394cb5c23
commit eaff963872
24 changed files with 93073 additions and 5787 deletions
+37 -2
View File
@@ -1,4 +1,4 @@
import { ConflictException, Injectable, NotFoundException } from '@nestjs/common';
import { BadRequestException, ConflictException, Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@mikro-orm/nestjs';
import { EntityRepository } from '@mikro-orm/core';
import { Admin } from '../entities/admin.entity';
@@ -20,7 +20,7 @@ export class AdminService {
private readonly adminRoleRepository: EntityRepository<AdminRole>,
private readonly em: EntityManager,
private readonly cacheService: CacheService,
) {}
) { }
async findByPhone(phone: string): Promise<Admin | null> {
const normalizedPhone = normalizePhone(phone);
@@ -194,4 +194,39 @@ export class AdminService {
}
return this.em.removeAndFlush(adminRole);
}
async findByPhoneAndShopSlug(phone: string, shopSlug: string): Promise<Admin> {
const normalizedPhone = normalizePhone(phone);
console.log(phone, shopSlug);
const admin = await this.adminRepository.findOne(
{
phone: normalizedPhone,
roles: {
shop: {
slug: shopSlug,
},
},
},
{ populate: ['roles', 'roles.role', 'roles.shop'] },
);
console.log(admin);
if (!admin) {
throw new BadRequestException('Admin not found');
}
// Ensure all roles are populated
// await adminRole.admin.roles.loadItems();
// for (const role of adminRole.admin.roles.getItems()) {
// if (role.role && role.role.permissions && !role.role.permissions.isInitialized()) {
// await role.role.permissions.loadItems();
// }
// }
return admin;
}
}
@@ -1,52 +1,15 @@
import { EntityManager, EntityRepository } from '@mikro-orm/postgresql';
import { Admin } from '../entities/admin.entity';
import { AdminRole } from '../entities/adminRole.entity';
import { ShopRepository } from '../../shops/repositories/rest.repository';
import { Injectable } from '@nestjs/common';
import { normalizePhone } from '../../utils/phone.util';
@Injectable()
export class AdminRepository extends EntityRepository<Admin> {
constructor(
readonly em: EntityManager,
private readonly shopRepository: ShopRepository,
) {
super(em, Admin);
}
async findByPhoneAndRestaurantSlug(phone: string, slug: string): Promise<Admin | null> {
console.log('phone', phone);
const normalizedPhone = normalizePhone(phone);
// First, find the shop by slug using the same repository as auth service
const shop = await this.shopRepository.findOne({ slug });
if (!shop) {
return null;
}
// Find AdminRole that matches the admin phone and shop
const adminRole = await this.em.findOne(
AdminRole,
{
admin: { phone: normalizedPhone },
shop: { id: shop.id },
},
{ populate: ['admin', 'admin.roles', 'role', 'role.permissions', 'shop'] },
);
if (!adminRole || !adminRole.admin) {
return null;
}
// Ensure all roles are populated
await adminRole.admin.roles.loadItems();
for (const role of adminRole.admin.roles.getItems()) {
if (role.role && role.role.permissions && !role.role.permissions.isInitialized()) {
await role.role.permissions.loadItems();
}
}
return adminRole.admin;
}
async findAdminsWithPermission(restaurantId: string, permission: string): Promise<Admin[]> {
const admins = await this.em.find(
Admin,
+1 -1
View File
@@ -10,6 +10,6 @@ export class RequestOtpDto {
@IsNotEmpty()
@IsString()
@ApiProperty({ example: 'zhivan', description: 'shop slug' })
@ApiProperty({ example: 'boote', description: 'shop slug' })
slug: string;
}
+1 -1
View File
@@ -16,6 +16,6 @@ export class VerifyOtpDto {
@IsNotEmpty()
@IsString()
@ApiProperty({ example: 'zhivan', description: 'shop slug' })
@ApiProperty({ example: 'boote', description: 'shop slug' })
slug: string;
}
+6 -8
View File
@@ -12,6 +12,7 @@ import { AdminLoginTransformer } from '../transformers/admin-login.transformer';
import { UserLoginTransformer } from '../transformers/user-login.transformer';
import { ConfigService } from '@nestjs/config';
import { normalizePhone } from 'src/modules/utils/phone.util';
import { AdminService } from 'src/modules/admin/providers/admin.service';
@Injectable()
export class AuthService {
@@ -24,6 +25,7 @@ export class AuthService {
private readonly tokensService: TokensService,
private readonly shopRepository: ShopRepository,
private readonly adminRepository: AdminRepository,
private readonly adminService: AdminService,
private readonly configService: ConfigService,
) {
this.OTP_EXPIRATION_TIME = this.configService.get<number>('OTP_EXPIRATION_TIME') ?? 240;
@@ -44,9 +46,9 @@ export class AuthService {
const key = isAdmin ? this.adminOtpKey(slug, normalizedPhone) : this.userOtpKey(slug, normalizedPhone);
await this.cacheService.set(key, code, this.OTP_EXPIRATION_TIME);
await this.smsService.sendotp(normalizedPhone, code);
// await this.smsService.sendotp(normalizedPhone, code);
return { code: null };
return { code };
}
async verifyOtp(phone: string, slug: string, code: string) {
@@ -83,11 +85,7 @@ export class AuthService {
if (cachedCode !== code) throw new BadRequestException('Invalid OTP');
await this.cacheService.del(key);
const admin = await this.adminRepository.findByPhoneAndRestaurantSlug(normalizedPhone, slug);
if (!admin) {
throw new BadRequestException(AuthMessage.ADMIN_NOT_FOUND);
}
const admin = await this.adminService.findByPhoneAndShopSlug(normalizedPhone, slug);
const rest = await this.shopRepository.findOne({ slug });
@@ -107,7 +105,7 @@ export class AuthService {
*/
async loginAdminForDsc(phone: string, slug: string) {
const normalizedPhone = normalizePhone(phone);
const admin = await this.adminRepository.findByPhoneAndRestaurantSlug(normalizedPhone, slug);
const admin = await this.adminService.findByPhoneAndShopSlug(normalizedPhone, slug);
if (!admin) {
throw new BadRequestException(AuthMessage.ADMIN_NOT_FOUND);
@@ -1,6 +1,5 @@
import { Type } from 'class-transformer';
import {
ArrayUnique,
IsArray,
IsBoolean,
IsEnum,
@@ -16,7 +15,8 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
export class CreateVariantDto {
@IsString()
@ApiProperty()
@IsOptional()
@ApiPropertyOptional()
id: string
@IsString()
@@ -49,9 +49,9 @@ export class CreateProductDto {
@ApiPropertyOptional({
type: CreateVariantDto,
example: [{
id: '',
value: 'قرمز',
price: 100000,
stock: 20
}]
})
variants: CreateVariantDto[]
@@ -88,12 +88,6 @@ export class CreateProductDto {
@ApiPropertyOptional({ example: 0 })
discount?: number;
@IsOptional()
@IsNumber()
@Min(0)
@Type(() => Number)
@ApiProperty({ example: 50 })
stock: number;
@IsOptional()
@IsBoolean()
+2 -2
View File
@@ -15,7 +15,7 @@ export class AdminsSeeder {
const role = rolesMap.get(adminData.roleName);
if (!role) continue;
const shop = adminData.restaurantSlug ? restaurantsMap.get(adminData.restaurantSlug) : null;
const shop = adminData.shopSlug ? restaurantsMap.get(adminData.shopSlug) : null;
// For shop role, shop is required
if (!shop) continue;
@@ -31,7 +31,7 @@ export class AdminsSeeder {
const adminRole = em.create(AdminRole, {
admin,
role,
shop: adminData.restaurantSlug ? shop : null,
shop: adminData.shopSlug ? shop : null,
});
em.persist(adminRole);
admin.roles.add(adminRole);
+6 -6
View File
@@ -3,7 +3,7 @@ export interface AdminData {
firstName: string;
lastName: string;
roleName: string;
restaurantSlug: string | null;
shopSlug: string | null;
}
export const adminsData: AdminData[] = [
@@ -12,27 +12,27 @@ export const adminsData: AdminData[] = [
firstName: 'مرتضی',
lastName: 'مرتضایی',
roleName: 'مدیر ( پلن ویژه)',
restaurantSlug: 'boote',
shopSlug: 'boote',
},
{
phone: '09185290775',
firstName: 'حمید',
lastName: 'ضرقامی',
roleName: 'مدیر ( پلن ویژه)',
restaurantSlug: 'boote',
roleName: 'مدیر ( پلن ویژه)',
shopSlug: 'boote',
},
{
phone: '09129283395',
firstName: 'مهرداد',
lastName: 'مظفری',
roleName: 'مدیر ( پلن ویژه)',
restaurantSlug: 'boote',
shopSlug: 'boote',
},
{
phone: '09184317567',
firstName: 'ادمین',
lastName: 'هنر',
roleName: 'مدیر ( پلن ویژه)',
restaurantSlug: 'boote',
shopSlug: 'boote',
},
];
+4 -4
View File
@@ -40,9 +40,9 @@ export const shopsData: ShopData[] =[
subscriptionId: 'sub_seed_zhivan_001',
},
{
name: 'سپنتا',
slug: 'sepanta',
domain: 'https://dmenu-plus-front.dev.danakcorp.com/sepanta',
name: 'بوته',
slug: 'boote',
domain: 'https://dmenu-plus-front.dev.danakcorp.com/boote',
isActive: true,
phone: '',
logo: 'https://dmenu.danakcorp.com/uploads/images/store/store_1641199914860_61d2b72b2e8bd97126a70b5f.png',
@@ -56,6 +56,6 @@ export const shopsData: ShopData[] =[
marriageDateScore: '0',
referrerScore: '0',
},
subscriptionId: 'sub_seed_sepanta_001',
subscriptionId: 'sub_seed_boote_001',
},
]