bb-inspired
Version:
Core library for BB-inspired NestJS backend
398 lines • 16 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var AuthSyncService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthSyncService = void 0;
const common_1 = require("@nestjs/common");
const database_service_1 = require("../../database/database.service");
const messaging_service_1 = require("../../messaging/messaging.service");
const cache_service_1 = require("../../cache/cache.service");
const logger_1 = require("../../../utils/logger");
let AuthSyncService = AuthSyncService_1 = class AuthSyncService {
constructor(options, databaseService, messagingService, cacheService) {
this.options = options;
this.databaseService = databaseService;
this.messagingService = messagingService;
this.cacheService = cacheService;
this.logger = new logger_1.AppLogger(AuthSyncService_1.name);
this.isSyncing = false;
}
onModuleInit() {
var _a;
if ((_a = this.options.synchronization) === null || _a === void 0 ? void 0 : _a.enabled) {
this.logger.log(`Auth synchronization enabled with strategy: ${this.options.synchronization.strategy}`);
if (this.options.synchronization.strategy === 'polling') {
this.setupPolling();
}
else if (this.options.synchronization.strategy === 'events') {
this.setupEventListeners();
}
}
}
setupPolling() {
const interval = this.options.synchronization.interval || 60000;
this.logger.log(`Setting up auth data polling with interval: ${interval}ms`);
this.syncInterval = setInterval(() => {
this.syncAuthData().catch(error => {
this.logger.error(`Auth sync error: ${error.message}`, error.stack);
});
}, interval);
}
setupEventListeners() {
if (!this.messagingService) {
this.logger.warn('Cannot set up event listeners: MessagingService not provided');
return;
}
this.logger.log('Setting up auth event listeners');
const authEvents = [
'auth.user.created',
'auth.user.updated',
'auth.user.deleted',
'auth.role.changed',
'auth.permission.changed',
'auth.logout',
'auth.token.revoked',
];
authEvents.forEach(eventName => {
this.messagingService.subscribe(eventName, async (data) => {
this.logger.verbose(`Received auth event: ${eventName}`, { metadata: data });
switch (eventName) {
case 'auth.user.created':
case 'auth.user.updated':
await this.syncUser(data.userId);
break;
case 'auth.user.deleted':
await this.removeUser(data.userId);
break;
case 'auth.role.changed':
await this.syncRoles(data.userId);
break;
case 'auth.permission.changed':
await this.syncPermissions(data.userId);
break;
case 'auth.token.revoked':
await this.invalidateToken(data.token);
break;
default:
await this.syncAuthData(data);
}
});
});
this.logger.log(`Subscribed to ${authEvents.length} auth events`);
}
async syncAuthData(data) {
if (this.isSyncing) {
return;
}
this.isSyncing = true;
try {
this.logger.log('Starting auth data synchronization');
if (!this.databaseService) {
this.logger.warn('Cannot sync auth data: DatabaseService not provided');
return;
}
if (data === null || data === void 0 ? void 0 : data.userId) {
await this.syncUser(data.userId);
}
else if (data === null || data === void 0 ? void 0 : data.roleId) {
await this.syncRole(data.roleId);
}
else {
await this.syncAllAuthData();
}
this.logger.log('Auth data synchronization completed');
}
catch (error) {
this.logger.error(`Auth data synchronization failed: ${error.message}`, error.stack);
}
finally {
this.isSyncing = false;
}
}
async syncAllAuthData() {
await Promise.all([
this.syncAllUsers(),
this.syncAllRoles(),
this.syncAllPermissions(),
]);
}
async syncUser(userId) {
try {
const user = await this.databaseService.executeQuery('user', 'findUnique', {
where: { id: userId },
include: {
roles: true,
permissions: true,
},
}, { source: 'primary' });
if (!user) {
this.logger.warn(`User sync failed: User with ID ${userId} not found`);
return;
}
await this.databaseService.executeQuery('user', 'upsert', {
where: { id: userId },
update: {
...user,
roles: undefined,
permissions: undefined,
},
create: {
...user,
roles: undefined,
permissions: undefined,
},
}, { source: 'secondary' });
await this.syncRoles(userId);
await this.syncPermissions(userId);
this.logger.verbose(`User synchronized: ${userId}`);
}
catch (error) {
this.logger.error(`Error synchronizing user ${userId}: ${error.message}`, error.stack);
}
}
async removeUser(userId) {
try {
await this.databaseService.executeQuery('user', 'delete', {
where: { id: userId },
}, { source: 'secondary' });
this.logger.verbose(`User removed from secondary database: ${userId}`);
}
catch (error) {
this.logger.error(`Error removing user ${userId}: ${error.message}`, error.stack);
}
}
async syncRoles(userId) {
try {
const userRoles = await this.databaseService.executeQuery('userRole', 'findMany', {
where: { userId },
include: { role: true },
}, { source: 'primary' });
await this.databaseService.executeQuery('userRole', 'deleteMany', {
where: { userId },
}, { source: 'secondary' });
for (const userRole of userRoles) {
await this.syncRole(userRole.roleId);
await this.databaseService.executeQuery('userRole', 'create', {
data: {
userId,
roleId: userRole.roleId,
},
}, { source: 'secondary' });
}
this.logger.verbose(`Roles synchronized for user: ${userId}`);
}
catch (error) {
this.logger.error(`Error synchronizing roles for user ${userId}: ${error.message}`, error.stack);
}
}
async syncPermissions(userId) {
try {
const userPermissions = await this.databaseService.executeQuery('userPermission', 'findMany', {
where: { userId },
include: { permission: true },
}, { source: 'primary' });
const userRoles = await this.databaseService.executeQuery('userRole', 'findMany', {
where: { userId },
include: {
role: {
include: {
rolePermissions: {
include: { permission: true },
},
},
},
},
}, { source: 'primary' });
await this.databaseService.executeQuery('userPermission', 'deleteMany', {
where: { userId },
}, { source: 'secondary' });
for (const userPermission of userPermissions) {
await this.databaseService.executeQuery('userPermission', 'create', {
data: {
userId,
permissionId: userPermission.permissionId,
},
}, { source: 'secondary' });
}
this.logger.verbose(`Permissions synchronized for user: ${userId}`);
}
catch (error) {
this.logger.error(`Error synchronizing permissions for user ${userId}: ${error.message}`, error.stack);
}
}
async syncRole(roleId) {
try {
const role = await this.databaseService.executeQuery('role', 'findUnique', {
where: { id: roleId },
include: {
permissions: true,
},
}, { source: 'primary' });
if (!role) {
this.logger.warn(`Role sync failed: Role with ID ${roleId} not found`);
return;
}
await this.databaseService.executeQuery('role', 'upsert', {
where: { id: roleId },
update: {
...role,
permissions: undefined,
},
create: {
...role,
permissions: undefined,
},
}, { source: 'secondary' });
this.logger.verbose(`Role synchronized: ${roleId}`);
}
catch (error) {
this.logger.error(`Error synchronizing role ${roleId}: ${error.message}`, error.stack);
}
}
async syncUsersWithRole(roleId) {
try {
const usersWithRole = await this.databaseService.executeQuery('userRole', 'findMany', {
where: { roleId },
include: { user: true },
}, { source: 'primary' });
for (const userRole of usersWithRole) {
await this.syncUser(userRole.userId);
}
this.logger.verbose(`Users with role ${roleId} synchronized`);
}
catch (error) {
this.logger.error(`Error synchronizing users with role ${roleId}: ${error.message}`, error.stack);
}
}
async syncAllUsers() {
try {
const users = await this.databaseService.executeQuery('user', 'findMany', {
include: {
roles: true,
permissions: true,
},
}, { source: 'primary' });
for (const user of users) {
await this.databaseService.executeQuery('user', 'upsert', {
where: { id: user.id },
update: {
...user,
roles: undefined,
permissions: undefined,
},
create: {
...user,
roles: undefined,
permissions: undefined,
},
}, { source: 'secondary' });
}
for (const user of users) {
await this.syncRoles(user.id);
await this.syncPermissions(user.id);
}
this.logger.verbose(`All users synchronized (${users.length} users)`);
}
catch (error) {
this.logger.error(`Error synchronizing all users: ${error.message}`, error.stack);
}
}
async syncAllRoles() {
try {
const roles = await this.databaseService.executeQuery('role', 'findMany', {
include: {
permissions: true,
users: true,
},
}, { source: 'primary' });
for (const role of roles) {
await this.databaseService.executeQuery('role', 'upsert', {
where: { id: role.id },
update: {
...role,
permissions: undefined,
users: undefined,
},
create: {
...role,
permissions: undefined,
users: undefined,
},
}, { source: 'secondary' });
}
this.logger.verbose(`All roles synchronized (${roles.length} roles)`);
}
catch (error) {
this.logger.error(`Error synchronizing all roles: ${error.message}`, error.stack);
}
}
async syncAllPermissions() {
try {
const permissions = await this.databaseService.executeQuery('permission', 'findMany', {
include: {
roles: true,
users: true,
},
}, { source: 'primary' });
for (const permission of permissions) {
await this.databaseService.executeQuery('permission', 'upsert', {
where: { id: permission.id },
update: {
...permission,
roles: undefined,
users: undefined,
},
create: {
...permission,
roles: undefined,
users: undefined,
},
}, { source: 'secondary' });
}
this.logger.verbose(`All permissions synchronized (${permissions.length} permissions)`);
}
catch (error) {
this.logger.error(`Error synchronizing all permissions: ${error.message}`, error.stack);
}
}
async invalidateToken(token) {
if (!this.cacheService) {
return;
}
try {
await this.cacheService.delete(`auth:token:${token}`);
this.logger.verbose(`Token invalidated in cache`);
}
catch (error) {
this.logger.error(`Error invalidating token: ${error.message}`, error.stack);
}
}
onModuleDestroy() {
if (this.syncInterval) {
clearInterval(this.syncInterval);
}
}
};
exports.AuthSyncService = AuthSyncService;
exports.AuthSyncService = AuthSyncService = AuthSyncService_1 = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, common_1.Inject)('AUTH_OPTIONS')),
__param(1, (0, common_1.Optional)()),
__param(2, (0, common_1.Optional)()),
__param(3, (0, common_1.Optional)()),
__metadata("design:paramtypes", [Object, database_service_1.DatabaseService,
messaging_service_1.MessagingService,
cache_service_1.CacheService])
], AuthSyncService);
//# sourceMappingURL=auth-sync.service.js.map