diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index f0a9581b0..1c853a285 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -1,25 +1,25 @@ -import { Module } from '@nestjs/common'; -import { ConfigModule, ConfigService } from '@nestjs/config'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; -import { AssetCategoriesModule } from './asset-categories/asset-categories.module'; -import { SettingsModule } from './settings/settings.module'; -import { AssetCategory } from './asset-categories/asset-category.entity'; -import { DepartmentsModule } from './departments/departments.module'; -import { Department } from './departments/department.entity'; -import { UsersModule } from './users/users.module'; -import { User } from './users/entities/user.entity'; -import { SearchModule } from './search/search.module'; -import { AuthModule } from './auth/auth.module'; -import { RiskModule } from './risk/risk.module'; -import { ReportingModule } from './reporting/reporting.module'; -import { AssetTransfersModule } from './asset-transfers/asset-transfers.module'; -import { FileUpload } from './file-uploads/entities/file-upload.entity'; -import { Asset } from './assets/entities/assest.entity'; -import { Supplier } from './suppliers/entities/supplier.entity'; -import { QrBarcodeModule } from './qr-barcode/qr-barcode.module'; -import { VendorContractsModule } from './vendor-contracts/vendor-contracts.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"; @Module({ imports: [ @@ -54,10 +54,20 @@ import { VendorContractsModule } from './vendor-contracts/vendor-contracts.modul UsersModule, SearchModule, AuthModule, - RiskModule, - ReportingModule, - QrBarcodeModule, - VendorContractsModule, + ApiKeysModule, + OrganizationUnitsModule, + ChangeLogModule, + BarcodeModule, + ComplianceModule, + MobileDevicesModule, + PolicyDocumentsModule, + DeviceHealthModule, + QRCodeModule, + NotificationsModule, + StatusHistoryModule, + DisposalRegistryModule, + VendorDirectoryModule, + WebhooksModule, ], controllers: [AppController], providers: [AppService], diff --git a/backend/src/webhooks/assesst/assets.service.ts b/backend/src/webhooks/assesst/assets.service.ts new file mode 100644 index 000000000..45b9a1796 --- /dev/null +++ b/backend/src/webhooks/assesst/assets.service.ts @@ -0,0 +1,14 @@ + +import { EventEmitter2 } from '@nestjs/event-emitter'; + +export class AssetsService { + constructor(private repo: any, private eventEmitter: EventEmitter2) { + // Initialization logic if needed + } + + createAsset(data) { + const asset = this.repo.save(data); + this.eventEmitter.emit('asset.created', asset); + } +} + diff --git a/backend/src/webhooks/dto/create-webhook.dto.ts b/backend/src/webhooks/dto/create-webhook.dto.ts new file mode 100644 index 000000000..724e6de60 --- /dev/null +++ b/backend/src/webhooks/dto/create-webhook.dto.ts @@ -0,0 +1,10 @@ +// webhooks/dto/create-webhook.dto.ts +import { IsUrl, IsString } from 'class-validator'; + +export class CreateWebhookDto { + @IsUrl() + url: string; + + @IsString() + eventType: string; +} diff --git a/backend/src/webhooks/entities/webhook.entity.ts b/backend/src/webhooks/entities/webhook.entity.ts new file mode 100644 index 000000000..508697df1 --- /dev/null +++ b/backend/src/webhooks/entities/webhook.entity.ts @@ -0,0 +1,17 @@ +// webhooks/entities/webhook.entity.ts +import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; + +@Entity('webhooks') +export class Webhook { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column() + url: string; + + @Column() // e.g. "asset.created", "asset.updated" + eventType: string; + + @Column({ default: true }) + isActive: boolean; +} diff --git a/backend/src/webhooks/webhooks.controller.spec.ts b/backend/src/webhooks/webhooks.controller.spec.ts new file mode 100644 index 000000000..0d81b44a5 --- /dev/null +++ b/backend/src/webhooks/webhooks.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { WebhooksController } from './webhooks.controller'; + +describe('WebhooksController', () => { + let controller: WebhooksController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [WebhooksController], + }).compile(); + + controller = module.get(WebhooksController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/backend/src/webhooks/webhooks.controller.ts b/backend/src/webhooks/webhooks.controller.ts new file mode 100644 index 000000000..a2be99717 --- /dev/null +++ b/backend/src/webhooks/webhooks.controller.ts @@ -0,0 +1,19 @@ + +import { Controller, Post, Body, Get } from '@nestjs/common'; +import { WebhooksService } from './webhooks.service'; +import { CreateWebhookDto } from './dto/create-webhook.dto'; + +@Controller('webhooks') +export class WebhooksController { + constructor(private webhookService: WebhooksService) {} + + @Post() + registerWebhook(@Body() dto: CreateWebhookDto) { + return this.webhookService.register(dto); + } + + @Get() + findAll() { + return this.webhookService.findAll(); + } +} diff --git a/backend/src/webhooks/webhooks.listener.ts b/backend/src/webhooks/webhooks.listener.ts new file mode 100644 index 000000000..080bbfc43 --- /dev/null +++ b/backend/src/webhooks/webhooks.listener.ts @@ -0,0 +1,14 @@ +// webhooks/webhooks.listener.ts +import { OnEvent } from '@nestjs/event-emitter'; +import { WebhooksService } from './webhooks.service'; +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class WebhookListener { + constructor(private webhookService: WebhooksService) {} + + @OnEvent('asset.created') + handleNewAsset(asset) { + this.webhookService.triggerEvent('asset.created', asset); + } +} diff --git a/backend/src/webhooks/webhooks.module.ts b/backend/src/webhooks/webhooks.module.ts new file mode 100644 index 000000000..386ad6b35 --- /dev/null +++ b/backend/src/webhooks/webhooks.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { WebhooksController } from './webhooks.controller'; +import { WebhooksService } from './webhooks.service'; +import { WebhookListener } from './webhooks.listener'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { Webhook } from './entities/webhook.entity'; + +@Module({ + imports: [TypeOrmModule.forFeature([Webhook])], + controllers: [WebhooksController], + providers: [WebhooksService, WebhookListener] +}) +export class WebhooksModule {} diff --git a/backend/src/webhooks/webhooks.service.spec.ts b/backend/src/webhooks/webhooks.service.spec.ts new file mode 100644 index 000000000..88c224989 --- /dev/null +++ b/backend/src/webhooks/webhooks.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { WebhooksService } from './webhooks.service'; + +describe('WebhooksService', () => { + let service: WebhooksService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [WebhooksService], + }).compile(); + + service = module.get(WebhooksService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/backend/src/webhooks/webhooks.service.ts b/backend/src/webhooks/webhooks.service.ts new file mode 100644 index 000000000..3090960f4 --- /dev/null +++ b/backend/src/webhooks/webhooks.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@nestjs/common'; +import { Repository } from 'typeorm'; +import { Webhook } from './entities/webhook.entity'; +import { InjectRepository } from '@nestjs/typeorm'; +import { CreateWebhookDto } from './dto/create-webhook.dto'; +import axios from 'axios'; + +@Injectable() +export class WebhooksService { + constructor( + @InjectRepository(Webhook) private repo: Repository, + ) {} + + register(dto: CreateWebhookDto) { + return this.repo.save(dto); + } + + findAll() { + return this.repo.find(); + } + + async triggerEvent(eventType: string, payload: any) { + const webhooks = await this.repo.find({ where: { eventType, isActive: true }}); + + for (const hook of webhooks) { + try { + await axios.post(hook.url, payload); + } catch (e) { + console.error(`Webhook failed: ${hook.url}`, e.message); + } + } + } +}