Add AdminId decorator for extracting adminId from requests; refactor AdminController to utilize new decorator for fetching admin details and restaurant-specific admins; update AdminService to handle admin creation and retrieval by restaurant; remove unused role and permission entities for cleaner architecture.

This commit is contained in:
2025-11-18 16:45:44 +03:30
parent 9abab789fc
commit 8a217f6034
18 changed files with 254 additions and 206 deletions
+33 -71
View File
@@ -6,12 +6,10 @@ import { Role } from '../../roles/entities/role.entity';
import { Restaurant } from '../../restaurants/entities/restaurant.entity';
import { EntityManager } from '@mikro-orm/postgresql';
import { CacheService } from '../../utils/cache.service';
import { AdminDetailTransformer, AdminDetailResponse } from '../transformers/admin-detail.transformer';
import { CreateMyRestaurantAdminDto } from '../dto/create-my-restaurant-admin.dto';
@Injectable()
export class AdminService {
private readonly ADMIN_PERMISSIONS_KEY = 'admin-perms';
constructor(
@InjectRepository(Admin)
private readonly adminRepository: EntityRepository<Admin>,
@@ -23,24 +21,44 @@ export class AdminService {
return this.adminRepository.findOne({ phone });
}
async findById(id: string): Promise<AdminDetailResponse | null> {
const admin = await this.adminRepository.findOne({ id }, { populate: ['role', 'role.permissions', 'restaurant'] });
async findById(id: string): Promise<Admin | null> {
const admin = await this.adminRepository.findOne({ id }, { populate: ['roles', 'roles.role'] });
return admin;
}
if (!admin) {
return null;
async findAllByRestaurantId(restId: string): Promise<Admin[]> {
return this.adminRepository.find({ roles: { restaurant: { id: restId } } }, { populate: ['roles', 'roles.role'] });
}
async createAdminForMyRestaurant(restId: string, dto: CreateMyRestaurantAdminDto) {
const { phone, firstName, lastName, roleId } = dto;
const currentAdmin = await this.adminRepository.findOne({
phone,
roles: { role: { id: roleId }, restaurant: { id: restId } },
});
if (currentAdmin) {
return currentAdmin;
}
return AdminDetailTransformer.transform(admin);
}
async findAll(): Promise<Admin[]> {
return this.adminRepository.findAll();
const role = await this.em.findOne(Role, { id: roleId });
if (!role) throw new Error('Role not found');
const restaurant = await this.em.findOne(Restaurant, { id: restId });
if (!restaurant) throw new Error('Restaurant not found');
const admin = this.adminRepository.create({ phone, firstName, lastName, roles: [{ role, restaurant }] });
await this.em.persistAndFlush(admin);
return admin;
}
async create(data: { phone: string; firstName?: string; lastName?: string; roleId: string; restId: string }) {
async createRestaurantBySuperAdmin(data: {
phone: string;
firstName?: string;
lastName?: string;
roleId: string;
restId: string;
}) {
const { phone, firstName, lastName, roleId, restId } = data;
// check existing
const existing = await this.adminRepository.findOne({ phone, restaurant: { id: restId } });
const existing = await this.adminRepository.findOne({ phone, roles: { restaurant: { id: restId } } });
if (existing) {
return existing;
}
@@ -52,64 +70,8 @@ export class AdminService {
const restaurant = await this.em.findOne(Restaurant, { id: restId });
if (!restaurant) throw new Error('Restaurant not found');
const admin = this.adminRepository.create({ phone, firstName, lastName, role, restaurant });
const admin = this.adminRepository.create({ phone, firstName, lastName, roles: [{ role, restaurant }] });
await this.em.persistAndFlush(admin);
return admin;
}
/**
* Get admin permissions from cache or database
* @param adminId - The admin ID
* @param restId - The restaurant ID
* @returns Array of permission names (string[])
*/
async getAdminPermissions(adminId: string, restId: string): Promise<string[]> {
const cacheKey = `${this.ADMIN_PERMISSIONS_KEY}:${adminId}:${restId}`;
// Try to get from cache first
const cachedPermissions = await this.cacheService.get<string>(cacheKey);
if (cachedPermissions) {
try {
const parsed: unknown = JSON.parse(cachedPermissions);
// Ensure it's an array of strings
if (Array.isArray(parsed) && parsed.every((p): p is string => typeof p === 'string')) {
return parsed;
}
// If invalid format, continue to fetch from DB
} catch {
// If parsing fails, continue to fetch from DB
}
}
// If not in cache, fetch from database
const admin = await this.adminRepository.findOne(
{ id: adminId, restaurant: { id: restId } },
{ populate: ['role', 'role.permissions'] },
);
if (!admin || !admin.role) {
return [];
}
// Extract permission names as array of strings
const permissions = await admin.role.permissions.loadItems();
const permissionNames: string[] = permissions
.map(p => p.name)
.filter((name): name is string => typeof name === 'string');
// Store in cache as JSON stringified array of strings (1 hour TTL)
await this.cacheService.set(cacheKey, JSON.stringify(permissionNames), 3600);
return permissionNames;
}
/**
* Invalidate admin permissions cache
* @param adminId - The admin ID
* @param restId - The restaurant ID
*/
async invalidateAdminPermissionsCache(adminId: string, restId: string): Promise<void> {
const cacheKey = `${this.ADMIN_PERMISSIONS_KEY}:${adminId}:${restId}`;
await this.cacheService.del(cacheKey);
}
}