diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index 1c853a2..ae1d9ef 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -1,25 +1,38 @@ -import { Module } from "@nestjs/common"; -import { ConfigModule, ConfigService } from "@nestjs/config"; -import { TypeOrmModule } from "@nestjs/typeorm"; -import { ScheduleModule } from "@nestjs/schedule"; -import { AppController } from "./app.controller"; -import { AppService } from "./app.service"; -import { UsersModule } from "./users/users.module"; -import { AuthModule } from "./auth/auth.module"; -import { ApiKeysModule } from "./api-keys/api-keys.module"; -import { OrganizationUnitsModule } from "./organization-units/organization-units.module"; -import { ChangeLogModule } from "./change-log/change-log.module"; -import { BarcodeModule } from "./barcode/barcode.module"; -import { ComplianceModule } from "./compliance/compliance.module"; -import { MobileDevicesModule } from "./mobile-devices/mobile-devices.module"; -import { PolicyDocumentsModule } from "./policy-documents/policy-documents.module"; -import { DeviceHealthModule } from "./device-health/device-health.module"; -import { QRCodeModule } from "./QR-Code/qrcode.module"; -import { NotificationsModule } from "./notifications/notifications.module"; -import { StatusHistoryModule } from "./status-history/status-history.module"; -import { DisposalRegistryModule } from "./disposal-registry/disposal-registry.module"; -import { VendorDirectoryModule } from "./vendor-directory/vendor-directory.module"; -import { WebhooksModule } from "./webhooks/webhooks.module"; +import { Module } from '@nestjs/common'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { ScheduleModule } from '@nestjs/schedule'; +import { AppController } from './app.controller'; +import { AppService } from './app.service'; +import { UsersModule } from './users/users.module'; +import { AuthModule } from './auth/auth.module'; +// import { ApiKeysModule } from "./api-keys/api-keys.module"; +// import { OrganizationUnitsModule } from "./organization-units/organization-units.module"; +// import { ChangeLogModule } from "./change-log/change-log.module"; +// import { BarcodeModule } from "./barcode/barcode.module"; +// import { ComplianceModule } from "./compliance/compliance.module"; +// import { MobileDevicesModule } from "./mobile-devices/mobile-devices.module"; +// import { PolicyDocumentsModule } from "./policy-documents/policy-documents.module"; +// import { DeviceHealthModule } from "./device-health/device-health.module"; +// import { QRCodeModule } from "./QR-Code/qrcode.module"; +// import { NotificationsModule } from "./notifications/notifications.module"; +// import { StatusHistoryModule } from "./status-history/status-history.module"; +// import { DisposalRegistryModule } from "./disposal-registry/disposal-registry.module"; +// import { VendorDirectoryModule } from "./vendor-directory/vendor-directory.module"; +import { WebhooksModule } from './webhooks/webhooks.module'; +import { AuditLogsModule } from './audit-logs/audit-logs.module'; +import { AuditLoggingInterceptor } from './audit-logs/audit-logging.interceptor'; +import { APP_INTERCEPTOR } from '@nestjs/core'; +import { AssetCategory } from './asset-categories/asset-category.entity'; +import { Department } from './departments/department.entity'; +import { User } from './users/entities/user.entity'; +import { FileUpload } from './file-uploads/entities/file-upload.entity'; +import { Asset } from './assets/entities/assest.entity'; +import { Supplier } from './suppliers/entities/supplier.entity'; +import { AssetCategoriesModule } from './asset-categories/asset-categories.module'; +import { DepartmentsModule } from './departments/departments.module'; +import { AssetTransfersModule } from './asset-transfers/asset-transfers.module'; +import { SearchModule } from './search/search.module'; @Module({ imports: [ @@ -54,22 +67,29 @@ import { WebhooksModule } from "./webhooks/webhooks.module"; UsersModule, SearchModule, AuthModule, - ApiKeysModule, - OrganizationUnitsModule, - ChangeLogModule, - BarcodeModule, - ComplianceModule, - MobileDevicesModule, - PolicyDocumentsModule, - DeviceHealthModule, - QRCodeModule, - NotificationsModule, - StatusHistoryModule, - DisposalRegistryModule, - VendorDirectoryModule, + // ApiKeysModule, + // OrganizationUnitsModule, + // ChangeLogModule, + // BarcodeModule, + // ComplianceModule, + // MobileDevicesModule, + // PolicyDocumentsModule, + // DeviceHealthModule, + // QRCodeModule, + // NotificationsModule, + // StatusHistoryModule, + // DisposalRegistryModule, + // VendorDirectoryModule, WebhooksModule, + AuditLogsModule, ], controllers: [AppController], - providers: [AppService], + providers: [ + { + provide: APP_INTERCEPTOR, + useClass: AuditLoggingInterceptor, + }, + AppService, + ], }) export class AppModule {} diff --git a/backend/src/audit-logs/audit-logging.interceptor.ts b/backend/src/audit-logs/audit-logging.interceptor.ts new file mode 100644 index 0000000..f9730d5 --- /dev/null +++ b/backend/src/audit-logs/audit-logging.interceptor.ts @@ -0,0 +1,54 @@ +import { + Injectable, + NestInterceptor, + ExecutionContext, + CallHandler, +} from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; +import { AuditLogsService } from './audit-logs.service'; + +@Injectable() +export class AuditLoggingInterceptor implements NestInterceptor { + constructor(private readonly auditLogsService: AuditLogsService) {} + + intercept(context: ExecutionContext, next: CallHandler): Observable { + const req = context.switchToHttp().getRequest(); + + const user = req.user; // Assumes authentication middleware adds user info + const userId = user ? user.id : null; + + const method = req.method; + const url = req.url; + + // Customize this logic to map HTTP verbs to action names: + const actionMap = { + GET: 'Read', + POST: 'Create', + PATCH: 'Update', + PUT: 'Replace', + DELETE: 'Delete', + }; + const action = actionMap[method] || method; + + // Extract entity info from URL, e.g., /branches, /assets/123 + const entity = url.split('/')[1] || 'unknown'; + + return next.handle().pipe( + tap(async () => { + // Optionally, you can add more metadata like request params or body here + await this.auditLogsService.createLog({ + action, + entity, + userId, + timestamp: new Date(), + metadata: { + params: req.params, + query: req.query, + body: req.body, + }, + }); + }), + ); + } +} diff --git a/backend/src/audit-logs/audit-logs.module.ts b/backend/src/audit-logs/audit-logs.module.ts new file mode 100644 index 0000000..fa867e7 --- /dev/null +++ b/backend/src/audit-logs/audit-logs.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { AuditLog } from './entities/audit-log.entity'; +import { AuditLogsService } from './audit-logs.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([AuditLog])], + providers: [AuditLogsService], + exports: [AuditLogsService], +}) +export class AuditLogsModule {} diff --git a/backend/src/audit-logs/audit-logs.service.ts b/backend/src/audit-logs/audit-logs.service.ts new file mode 100644 index 0000000..45ec8ce --- /dev/null +++ b/backend/src/audit-logs/audit-logs.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { AuditLog } from './entities/audit-log.entity'; + +@Injectable() +export class AuditLogsService { + constructor( + @InjectRepository(AuditLog) + private auditLogRepository: Repository, + ) {} + + async createLog(data: Partial): Promise { + const log = this.auditLogRepository.create(data); + return this.auditLogRepository.save(log); + } +} diff --git a/backend/src/audit-logs/entities/audit-log.entity.ts b/backend/src/audit-logs/entities/audit-log.entity.ts new file mode 100644 index 0000000..cc52834 --- /dev/null +++ b/backend/src/audit-logs/entities/audit-log.entity.ts @@ -0,0 +1,27 @@ +import { + Entity, + Column, + PrimaryGeneratedColumn, + CreateDateColumn, +} from 'typeorm'; + +@Entity('audit_logs') +export class AuditLog { + @PrimaryGeneratedColumn() + id: number; + + @Column() + action: string; + + @Column() + entity: string; + + @Column({ nullable: true }) + userId: number; + + @CreateDateColumn() + timestamp: Date; + + @Column({ type: 'jsonb', nullable: true }) + metadata?: Record; // Optional JSON metadata about the action +}