Admin User v1
This commit is contained in:
130
backend/src/features/admin/domain/admin.service.ts
Normal file
130
backend/src/features/admin/domain/admin.service.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
/**
|
||||
* @ai-summary Admin feature business logic
|
||||
* @ai-context Handles admin user management with audit logging
|
||||
*/
|
||||
|
||||
import { AdminRepository } from '../data/admin.repository';
|
||||
import { AdminUser, AdminAuditLog } from './admin.types';
|
||||
import { logger } from '../../../core/logging/logger';
|
||||
|
||||
export class AdminService {
|
||||
constructor(private repository: AdminRepository) {}
|
||||
|
||||
async getAdminByAuth0Sub(auth0Sub: string): Promise<AdminUser | null> {
|
||||
try {
|
||||
return await this.repository.getAdminByAuth0Sub(auth0Sub);
|
||||
} catch (error) {
|
||||
logger.error('Error getting admin by auth0_sub', { error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getAdminByEmail(email: string): Promise<AdminUser | null> {
|
||||
try {
|
||||
return await this.repository.getAdminByEmail(email);
|
||||
} catch (error) {
|
||||
logger.error('Error getting admin by email', { error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getAllAdmins(): Promise<AdminUser[]> {
|
||||
try {
|
||||
return await this.repository.getAllAdmins();
|
||||
} catch (error) {
|
||||
logger.error('Error getting all admins', { error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getActiveAdmins(): Promise<AdminUser[]> {
|
||||
try {
|
||||
return await this.repository.getActiveAdmins();
|
||||
} catch (error) {
|
||||
logger.error('Error getting active admins', { error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async createAdmin(email: string, role: string, auth0Sub: string, createdBy: string): Promise<AdminUser> {
|
||||
try {
|
||||
// Check if admin already exists
|
||||
const normalizedEmail = email.trim().toLowerCase();
|
||||
const existing = await this.repository.getAdminByEmail(normalizedEmail);
|
||||
if (existing) {
|
||||
throw new Error(`Admin user with email ${normalizedEmail} already exists`);
|
||||
}
|
||||
|
||||
// Create new admin
|
||||
const admin = await this.repository.createAdmin(auth0Sub, normalizedEmail, role, createdBy);
|
||||
|
||||
// Log audit action
|
||||
await this.repository.logAuditAction(createdBy, 'CREATE', admin.auth0Sub, 'admin_user', admin.email, {
|
||||
email,
|
||||
role
|
||||
});
|
||||
|
||||
logger.info('Admin user created', { email, role });
|
||||
return admin;
|
||||
} catch (error) {
|
||||
logger.error('Error creating admin', { error, email });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async revokeAdmin(auth0Sub: string, revokedBy: string): Promise<AdminUser> {
|
||||
try {
|
||||
// Check that at least one active admin will remain
|
||||
const activeAdmins = await this.repository.getActiveAdmins();
|
||||
if (activeAdmins.length <= 1) {
|
||||
throw new Error('Cannot revoke the last active admin');
|
||||
}
|
||||
|
||||
// Revoke the admin
|
||||
const admin = await this.repository.revokeAdmin(auth0Sub);
|
||||
|
||||
// Log audit action
|
||||
await this.repository.logAuditAction(revokedBy, 'REVOKE', auth0Sub, 'admin_user', admin.email);
|
||||
|
||||
logger.info('Admin user revoked', { auth0Sub, email: admin.email });
|
||||
return admin;
|
||||
} catch (error) {
|
||||
logger.error('Error revoking admin', { error, auth0Sub });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async reinstateAdmin(auth0Sub: string, reinstatedBy: string): Promise<AdminUser> {
|
||||
try {
|
||||
// Reinstate the admin
|
||||
const admin = await this.repository.reinstateAdmin(auth0Sub);
|
||||
|
||||
// Log audit action
|
||||
await this.repository.logAuditAction(reinstatedBy, 'REINSTATE', auth0Sub, 'admin_user', admin.email);
|
||||
|
||||
logger.info('Admin user reinstated', { auth0Sub, email: admin.email });
|
||||
return admin;
|
||||
} catch (error) {
|
||||
logger.error('Error reinstating admin', { error, auth0Sub });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async getAuditLogs(limit: number = 100, offset: number = 0): Promise<{ logs: AdminAuditLog[]; total: number }> {
|
||||
try {
|
||||
return await this.repository.getAuditLogs(limit, offset);
|
||||
} catch (error) {
|
||||
logger.error('Error fetching audit logs', { error });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async linkAdminAuth0Sub(email: string, auth0Sub: string): Promise<AdminUser> {
|
||||
try {
|
||||
return await this.repository.updateAuth0SubByEmail(email.trim().toLowerCase(), auth0Sub);
|
||||
} catch (error) {
|
||||
logger.error('Error linking admin auth0_sub to email', { error, email, auth0Sub });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user