read permissions from cache
This commit is contained in:
@@ -12,10 +12,10 @@ import { AdminLoginTransformer } from '../transformers/admin-login.transformer';
|
||||
import { UserLoginTransformer } from '../transformers/user-login.transformer';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { normalizePhone } from '../../utils/phone.util';
|
||||
import { PermissionsService } from 'src/modules/roles/providers/permissions.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
readonly ADMIN_PERMISSIONS_KEY = 'admin-perms';
|
||||
private OTP_EXPIRATION_TIME: number;
|
||||
constructor(
|
||||
private readonly cacheService: CacheService,
|
||||
@@ -25,6 +25,7 @@ export class AuthService {
|
||||
private readonly restRepository: RestRepository,
|
||||
private readonly adminRepository: AdminRepository,
|
||||
private readonly configService: ConfigService,
|
||||
private readonly permissionsService: PermissionsService,
|
||||
) {
|
||||
this.OTP_EXPIRATION_TIME = this.configService.get<number>('OTP_EXPIRATION_TIME') ?? 240;
|
||||
}
|
||||
@@ -106,6 +107,7 @@ export class AuthService {
|
||||
}
|
||||
|
||||
const tokens = await this.tokensService.generateAccessAndRefreshToken(admin.id, rest.id, true, slug);
|
||||
await this.permissionsService.refreshAdminPermissionsCache(admin.id, rest.id);
|
||||
|
||||
const adminResponse = await AdminLoginTransformer.transform(admin, rest);
|
||||
|
||||
@@ -130,6 +132,7 @@ export class AuthService {
|
||||
}
|
||||
|
||||
const tokens = await this.tokensService.generateAccessAndRefreshToken(admin.id, rest.id, true, slug);
|
||||
await this.permissionsService.refreshAdminPermissionsCache(admin.id, rest.id);
|
||||
|
||||
const adminResponse = await AdminLoginTransformer.transform(admin, rest);
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ export class RolesController {
|
||||
|
||||
@Get('admin/roles/permissions')
|
||||
@UseGuards(AdminAuthGuard)
|
||||
@Permissions(Permission.MANAGE_ROLES)
|
||||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: 'Get all permissions that the admin has' })
|
||||
async findAllPermissions(@AdminId() adminId: string, @RestId() restId: string) {
|
||||
|
||||
@@ -31,44 +31,80 @@ export class PermissionsService {
|
||||
* @returns Array of permission names (string[])
|
||||
*/
|
||||
async getAdminPermissions(adminId: string, restId: string): Promise<string[]> {
|
||||
const cacheKey = `${this.ADMIN_PERMISSIONS_KEY}:${adminId}:${restId}`;
|
||||
const cachedPermissions = await this.readAdminPermissionsFromCache(adminId, restId);
|
||||
if (cachedPermissions !== undefined) {
|
||||
return cachedPermissions;
|
||||
}
|
||||
|
||||
// Try to get from cache first
|
||||
const cachedPermissions = await this.cacheService.get<string>(cacheKey);
|
||||
if (cachedPermissions) {
|
||||
const permissionNames = await this.fetchAdminPermissionsFromDb(adminId, restId);
|
||||
await this.cacheAdminPermissions(adminId, restId, permissionNames);
|
||||
return permissionNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh admin permissions in Redis (used after login or role changes)
|
||||
*/
|
||||
async refreshAdminPermissionsCache(adminId: string, restId: string): Promise<string[]> {
|
||||
await this.invalidateAdminPermissionsCache(adminId, restId);
|
||||
const permissionNames = await this.fetchAdminPermissionsFromDb(adminId, restId);
|
||||
await this.cacheAdminPermissions(adminId, restId, permissionNames);
|
||||
return permissionNames;
|
||||
}
|
||||
|
||||
private getCacheKey(adminId: string, restId: string): string {
|
||||
return `${this.ADMIN_PERMISSIONS_KEY}:${adminId}:${restId}`;
|
||||
}
|
||||
|
||||
private async readAdminPermissionsFromCache(adminId: string, restId: string): Promise<string[] | undefined> {
|
||||
const cacheKey = this.getCacheKey(adminId, restId);
|
||||
const cachedPermissions = await this.cacheService.get<string | string[]>(cacheKey);
|
||||
if (cachedPermissions === undefined || cachedPermissions === null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (Array.isArray(cachedPermissions) && cachedPermissions.every((p): p is string => typeof p === 'string')) {
|
||||
return cachedPermissions;
|
||||
}
|
||||
|
||||
if (typeof cachedPermissions === 'string') {
|
||||
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
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// If not in cache, fetch from database
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private async cacheAdminPermissions(adminId: string, restId: string, permissionNames: string[]): Promise<void> {
|
||||
const cacheKey = this.getCacheKey(adminId, restId);
|
||||
await this.cacheService.set(cacheKey, JSON.stringify(permissionNames), this.ADMIN_PERMISSIONS_TTL);
|
||||
}
|
||||
|
||||
private async fetchAdminPermissionsFromDb(adminId: string, restId: string): Promise<string[]> {
|
||||
const admin = await this.adminRepository.findOne(
|
||||
{ id: adminId, roles: { restaurant: { id: restId } } },
|
||||
{ populate: ['roles', 'roles.role', 'roles.role.permissions'] },
|
||||
{
|
||||
populate: ['roles', 'roles.role', 'roles.role.permissions'],
|
||||
populateFilter: { roles: { restaurant: { id: restId } } },
|
||||
},
|
||||
);
|
||||
|
||||
if (!admin || !admin.roles) {
|
||||
await this.cacheService.set(cacheKey, JSON.stringify([]), this.ADMIN_PERMISSIONS_TTL);
|
||||
if (!admin) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Ensure roles collection is loaded
|
||||
await admin.roles.loadItems();
|
||||
|
||||
// Extract permission names as array of strings
|
||||
const permissions = await Promise.all(
|
||||
admin.roles
|
||||
.getItems()
|
||||
.filter(r => r.role) // Filter out any null/undefined roles
|
||||
.filter(r => r.role)
|
||||
.map(async r => {
|
||||
// Ensure permissions collection is initialized
|
||||
if (!r.role.permissions.isInitialized()) {
|
||||
await r.role.permissions.loadItems();
|
||||
}
|
||||
@@ -76,10 +112,7 @@ export class PermissionsService {
|
||||
}),
|
||||
);
|
||||
|
||||
const permissionNames = permissions.flat().map(p => p.name);
|
||||
await this.cacheService.set(cacheKey, JSON.stringify(permissionNames), this.ADMIN_PERMISSIONS_TTL);
|
||||
|
||||
return permissionNames;
|
||||
return permissions.flat().map(p => p.name);
|
||||
}
|
||||
|
||||
async getAdminFullPermissions(adminId: string, restId: string): Promise<Permission[]> {
|
||||
@@ -104,7 +137,6 @@ export class PermissionsService {
|
||||
* @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);
|
||||
await this.cacheService.del(this.getCacheKey(adminId, restId));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user