UNPKG

dt-common-device

Version:

A secure and robust device management library for IoT applications

844 lines (843 loc) 39.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) { function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; } var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value"; var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null; var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {}); var _, done = false; for (var i = decorators.length - 1; i >= 0; i--) { var context = {}; for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p]; for (var p in contextIn.access) context.access[p] = contextIn.access[p]; context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); }; var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context); if (kind === "accessor") { if (result === void 0) continue; if (result === null || typeof result !== "object") throw new TypeError("Object expected"); if (_ = accept(result.get)) descriptor.get = _; if (_ = accept(result.set)) descriptor.set = _; if (_ = accept(result.init)) initializers.unshift(_); } else if (_ = accept(result)) { if (kind === "field") initializers.unshift(_); else descriptor[key] = _; } } if (target) Object.defineProperty(target, contextIn.name, descriptor); done = true; }; var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) { var useValue = arguments.length > 2; for (var i = 0; i < initializers.length; i++) { value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg); } return useValue ? value : void 0; }; var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) { if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : ""; return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AlertService = void 0; const typedi_1 = __importStar(require("typedi")); const Alert_repository_1 = require("./Alert.repository"); const Alert_model_1 = require("./Alert.model"); const alert_types_1 = require("./alert.types"); const AlertBuilder_1 = require("./AlertBuilder"); const constants_1 = require("../constants"); const issues_1 = require("../issues"); const audit_1 = require("../audit"); let AlertService = (() => { let _classDecorators = [(0, typedi_1.Service)()]; let _classDescriptor; let _classExtraInitializers = []; let _classThis; var AlertService = _classThis = class { constructor() { this.alertRepository = typedi_1.default.get(Alert_repository_1.AlertRepository); } /** * Create an operations alert using AlertBuilder */ async raiseOperationsAlert(data) { const alertBuilder = AlertBuilder_1.AlertBuilder.createOperationsAlert() .setPropertyId(data.propertyId) .setTitle(data.title) .setDescription(data.description); if (data.entityId) alertBuilder.setEntityId(data.entityId); if (data.entityType) alertBuilder.setEntityType(data.entityType); if (data.entitySubType) alertBuilder.setEntitySubType(data.entitySubType); if (data.createdBy) alertBuilder.setCreatedBy(data.createdBy); if (data.zoneId) alertBuilder.setZoneId(data.zoneId); return await this.createAlert(alertBuilder); } /** * Create a security alert using AlertBuilder */ async raiseSecurityAlert(data) { const alertBuilder = AlertBuilder_1.AlertBuilder.createSecurityAlert() .setPropertyId(data.propertyId) .setTitle(data.title) .setDescription(data.description); if (data.entityId) alertBuilder.setEntityId(data.entityId); if (data.entityType) alertBuilder.setEntityType(data.entityType); if (data.entitySubType) alertBuilder.setEntitySubType(data.entitySubType); if (data.createdBy) alertBuilder.setCreatedBy(data.createdBy); if (data.zoneId) alertBuilder.setZoneId(data.zoneId); return await this.createAlert(alertBuilder); } async raiseDeviceAlert(data) { const alertBuilder = AlertBuilder_1.AlertBuilder.createDeviceAlert(data.entityId || "", data.propertyId) .setTitle(data.title) .setDescription(data.description); if (data.category) alertBuilder.setCategory(data.category); if (data.severity) alertBuilder.setSeverity(data.severity); if (data.type) alertBuilder.setType(data.type); if (data.createdBy) alertBuilder.setCreatedBy(data.createdBy); if (data.entitySubType) alertBuilder.setEntitySubType(data.entitySubType); if (data.zoneId) alertBuilder.setZoneId(data.zoneId); return await this.createAlert(alertBuilder); } /** * Create a hub-specific alert using AlertBuilder */ async raiseHubAlert(data) { const alertBuilder = AlertBuilder_1.AlertBuilder.createHubAlert(data.entityId || "", data.propertyId) .setTitle(data.title) .setDescription(data.description); if (data.category) alertBuilder.setCategory(data.category); if (data.severity) alertBuilder.setSeverity(data.severity); if (data.type) alertBuilder.setType(data.type); if (data.createdBy) alertBuilder.setCreatedBy(data.createdBy); if (data.entitySubType) alertBuilder.setEntitySubType(data.entitySubType); if (data.zoneId) alertBuilder.setZoneId(data.zoneId); return await this.createAlert(alertBuilder); } async raiseDeviceTamperAttemptAlert(device, zone, source) { return await this.createAlert({ entityId: device.deviceId, entityType: alert_types_1.EntityType.DEVICE, entitySubType: device.deviceType.type, propertyId: device.propertyId, zoneId: device.zoneId, title: "Device Tamper Attempt", description: `System detected a tamper attempt on ${device.name}.`, createdBy: source, category: alert_types_1.AlertCategory.SECURITY, severity: alert_types_1.AlertSeverity.CRITICAL, type: alert_types_1.AlertType.DEVICE_TAMPER_ATTEMPT, }); } async raiseDoorLeftOpenAlert(device, zone, openThreshold, source) { return await this.createAlert({ entityId: device.deviceId, entityType: alert_types_1.EntityType.DEVICE, entitySubType: device.deviceType.type, propertyId: device.propertyId, zoneId: device.zoneId, title: "Door Left Open", description: `${zone?.name} has a door left open, for more than ${openThreshold} minutes.`, createdBy: source, category: alert_types_1.AlertCategory.SECURITY, severity: alert_types_1.AlertSeverity.CRITICAL, type: alert_types_1.AlertType.DOOR_LEFT_OPEN, }); } async raiseIncorrectCodeAlert(device, zone, source, accessCount, timeWindow) { return await this.createAlert({ entityId: device.deviceId, entityType: alert_types_1.EntityType.DEVICE, entitySubType: device.deviceType.type, propertyId: device.propertyId, zoneId: device.zoneId, title: "Incorrect Code Used Too Many Times", description: `Incorrect code used ${accessCount} times on ${zone?.name}. Between ${timeWindow.startDate} and ${timeWindow.endDate}.`, createdBy: source, category: alert_types_1.AlertCategory.SECURITY, severity: alert_types_1.AlertSeverity.CRITICAL, type: alert_types_1.AlertType.INCORRECT_CODE_USED, }); } async raiseNewDeviceAlert(connection, propertyId, source) { return await this.createAlert({ entityId: connection.id, entityType: alert_types_1.EntityType.CLOUD_DEVICE_ACCOUNT, entitySubType: connection.provider, propertyId: propertyId, title: "New Device Detected", description: `${connection.deviceCount} new device(s), have been detected on the ${connection.provider} account.`, createdBy: source, category: alert_types_1.AlertCategory.OPERATIONS, severity: alert_types_1.AlertSeverity.INFO, type: alert_types_1.AlertType.ACCOUNT_NEW_DEVICE, }); } async raiseDoorOpenFrequentAlert(device, zone, accessTimes, openThreshold, source) { return await this.createAlert({ entityId: device.deviceId, entityType: alert_types_1.EntityType.DEVICE, entitySubType: device.deviceType.type, propertyId: device.propertyId, zoneId: device.zoneId, title: "Door Open Frequently", description: `${zone?.name} has been opened ${accessTimes} times in the last ${openThreshold} minutes.`, createdBy: source, category: alert_types_1.AlertCategory.OPERATIONS, severity: alert_types_1.AlertSeverity.HIGH, type: alert_types_1.AlertType.DOOR_OPEN_FREQUENT, }); } async raiseDoorOpenOutsideBusinessHoursAlert(device, zone, source) { return await this.createAlert({ entityId: device.deviceId, entityType: alert_types_1.EntityType.DEVICE, entitySubType: device.deviceType.type, propertyId: device.propertyId, zoneId: device.zoneId, title: "Door Open Outside Business Hours", description: `${zone?.name} has been opened outside business hours.`, createdBy: source, category: alert_types_1.AlertCategory.SECURITY, severity: alert_types_1.AlertSeverity.HIGH, type: alert_types_1.AlertType.DOOR_OPEN_OUTSIDE_BIZ_HOURS, }); } /** * Raise alert for device coming online (OPERATIONAL only) */ async raiseDeviceOnlineAlert(device, source) { return await this.raiseDeviceAlert({ entityId: device.deviceId, propertyId: device.propertyId, zoneId: device.zoneId, title: "Device Online", description: `Device ${device.name} is now online.`, category: alert_types_1.AlertCategory.OPERATIONS, severity: alert_types_1.AlertSeverity.INFO, createdBy: source, entityType: alert_types_1.EntityType.DEVICE, entitySubType: device.deviceType.type, type: alert_types_1.AlertType.DEVICE_ONLINE, }); } async raiseLockAccessEmergencyCodeAlert(device, zone, source) { return await this.createAlert({ entityId: device.deviceId, entityType: alert_types_1.EntityType.DEVICE, entitySubType: device.deviceType.type, propertyId: device.propertyId, zoneId: device.zoneId, title: "Emergency Code Used", description: `${zone?.name} has been accessed using the emergency code.`, createdBy: source, category: alert_types_1.AlertCategory.SECURITY, severity: alert_types_1.AlertSeverity.HIGH, type: alert_types_1.AlertType.LOCK_ACCESS_EMERGENCY_CODE, }); } async raiseLockAccessEmergencyRfidAlert(device, zone, source) { return await this.createAlert({ entityId: device.deviceId, entityType: alert_types_1.EntityType.DEVICE, entitySubType: device.deviceType.type, propertyId: device.propertyId, zoneId: device.zoneId, title: "Emergency RFID Card Used", description: `${zone?.name} has been accessed using the emergency RFID card.`, createdBy: source, category: alert_types_1.AlertCategory.SECURITY, severity: alert_types_1.AlertSeverity.HIGH, type: alert_types_1.AlertType.LOCK_ACCESS_EMERGENCY_RFID, }); } async raiseLockAccessMasterRfidAlert(device, zone, source) { return await this.createAlert({ entityId: device.deviceId, entityType: alert_types_1.EntityType.DEVICE, entitySubType: device.deviceType.type, propertyId: device.propertyId, zoneId: device.zoneId, title: "Master RFID Card Used", description: `${zone?.name} has been accessed using the master RFID card.`, createdBy: source, category: alert_types_1.AlertCategory.SECURITY, severity: alert_types_1.AlertSeverity.HIGH, type: alert_types_1.AlertType.LOCK_ACCESS_MASTER_RFID, }); } async raiseLockAccessMasterCodeAlert(device, zone, source) { return await this.createAlert({ entityId: device.deviceId, entityType: alert_types_1.EntityType.DEVICE, entitySubType: device.deviceType.type, propertyId: device.propertyId, zoneId: device.zoneId, title: "Master Code Used", description: `${zone?.name} has been accessed using the master code.`, createdBy: source, category: alert_types_1.AlertCategory.SECURITY, severity: alert_types_1.AlertSeverity.HIGH, type: alert_types_1.AlertType.LOCK_ACCESS_MASTER_CODE, }); } async raiseSpecificDoorAccessAlert(userName, device, zone, source) { return await this.createAlert({ entityId: device.deviceId, entityType: alert_types_1.EntityType.DEVICE, entitySubType: device.deviceType.type, propertyId: device.propertyId, zoneId: device.zoneId, title: "Specific Door Access", description: `${userName} has accessed ${zone?.name}.`, createdBy: source, category: alert_types_1.AlertCategory.OTHER, severity: alert_types_1.AlertSeverity.INFO, type: alert_types_1.AlertType.SPECIFIC_DOOR_ACCESS, }); } async raiseGuestLockFirstAccessAlert(userName, device, zone, source) { return await this.createAlert({ entityId: device.deviceId, entityType: alert_types_1.EntityType.DEVICE, entitySubType: device.deviceType.type, propertyId: device.propertyId, zoneId: device.zoneId, title: "Guest Used Code for First Time", description: `${userName} has used the code for the first time to access ${zone?.name}.`, createdBy: source, category: alert_types_1.AlertCategory.OTHER, severity: alert_types_1.AlertSeverity.INFO, type: alert_types_1.AlertType.GUEST_LOCK_FIRST_ACCESS, }); } async raiseZoneNotMappedToAccessGroupAlert(zone, source) { return await this.createAlert({ zoneId: zone.zoneId, propertyId: zone.propertyId, entityId: zone.zoneId, entityType: alert_types_1.EntityType.ZONE, entitySubType: issues_1.EntitySubType.ZONE, title: `Zone Not Mapped to Access Group`, description: `${zone?.name} is not mapped to any access group. Only Emergency Access Modes Generated. Add to an Access group to generate Guest and Staff Access Modes!`, createdBy: source, category: alert_types_1.AlertCategory.OPERATIONS, severity: alert_types_1.AlertSeverity.INFO, type: alert_types_1.AlertType.ZONE_NOT_MAPPED_TO_ACCESS_GROUP, }); } /** * Create a new alert with business logic validation * Accepts either a CreateAlertData object or an AlertBuilder instance */ async createAlert(alertData) { let processedAlertData; try { // Handle AlertBuilder instance if (alertData instanceof AlertBuilder_1.AlertBuilder) { processedAlertData = alertData.build(); } else { processedAlertData = alertData; } // Business logic: Validate alert data this.validateAlertData(processedAlertData); // Business logic: Set default severity if not provided if (!processedAlertData.severity) { processedAlertData.severity = this.determineDefaultSeverity(processedAlertData.category, processedAlertData.type); } // Business logic: Validate snooze date is in the future if (processedAlertData.snoozeUntil && new Date(processedAlertData.snoozeUntil) <= new Date()) { throw new Error("Snooze date must be in the future"); } const existingAlert = await this.queryAlerts({ propertyId: processedAlertData.propertyId, zoneId: processedAlertData.zoneId, entityId: processedAlertData.entityId, entityType: processedAlertData.entityType, entitySubType: processedAlertData.entitySubType, isActive: true, }); if (existingAlert.length > 0) { (0, audit_1.pushAudit)({ auditType: constants_1.DT_EVENT_TYPES.ALERT.CREATE.SKIPPED, auditData: { reason: "Alert already exists", resource: audit_1.Resource.ALERT, source: processedAlertData.createdBy, propertyId: processedAlertData.propertyId, entityId: processedAlertData.entityId, entityType: processedAlertData.entityType, entitySubType: processedAlertData.entitySubType, }, }); return null; } const alert = await this.alertRepository.create(processedAlertData); (0, audit_1.pushAudit)({ auditType: constants_1.DT_EVENT_TYPES.ALERT.CREATE.SUCCESS, auditData: { resource: audit_1.Resource.ALERT, source: constants_1.Source.USER, propertyId: processedAlertData.propertyId, zoneId: processedAlertData.zoneId, entityId: processedAlertData.entityId, entityType: processedAlertData.entityType, entitySubType: processedAlertData.entitySubType, type: processedAlertData.type, createdBy: processedAlertData.createdBy, createdAt: new Date(), }, }); return alert; } catch (error) { const createAlertData = alertData instanceof AlertBuilder_1.AlertBuilder ? alertData.build() : alertData; (0, audit_1.pushAudit)({ auditType: constants_1.DT_EVENT_TYPES.ALERT.CREATE.FAILED, auditData: { resource: audit_1.Resource.ALERT, source: constants_1.Source.USER, propertyId: createAlertData?.propertyId || "", zoneId: createAlertData?.zoneId || "", errorMessage: error instanceof Error ? error.message : "Unknown error", errorStack: error instanceof Error ? error.stack : "unknown error", createdBy: createAlertData?.createdBy || "", }, }); throw error; } } /** * Get alert by ID with business logic */ async getAlertById(id, includeDeleted = false) { if (!id) { throw new Error("Alert ID is required"); } const alert = await this.alertRepository.findById(id, includeDeleted); // Business logic: Check if alert is snoozed and expired if (alert?.snoozeUntil && new Date(alert.snoozeUntil) <= new Date()) { console.warn(`Alert ${id} snooze has expired`); } return alert; } /** * Get all alerts with business logic filtering */ async queryAlerts(filters = {}) { // Business logic: Validate filters this.validateFilters(filters); // Business logic: Apply business rules to filters const enhancedFilters = this.applyBusinessRules(filters); return await this.alertRepository.query(enhancedFilters); } async alertCount(filters) { // Business logic: Apply business rules to filters const enhancedFilters = this.applyBusinessRules(filters); return await this.alertRepository.count(enhancedFilters); } /** * Update an alert with business logic validation */ async updateAlert(id, updateData) { if (!id) { throw new Error("Alert ID is required"); } // Business logic: Validate update data this.validateUpdateData(updateData); // Business logic: Check if alert exists and is not deleted const existingAlert = await this.alertRepository.findById(id); if (!existingAlert) { throw new Error("Alert not found"); } // Business logic: Handle snooze validation if (updateData.snoozeUntil) { this.validateSnoozeDate(new Date(updateData.snoozeUntil)); } return await this.alertRepository.update(id, updateData); } async deleteAlerts(filters, deletedBy, softDelete = true) { try { if (!filters || Object.keys(filters).length === 0) { throw new Error("Filters are required"); } if (!deletedBy) { throw new Error("Deleted by user is required"); } const alerts = await this.queryAlerts(filters); if (softDelete) { for (const alert of alerts) { await this.alertRepository.softDelete(alert.id || alert._id, deletedBy); } } else { for (const alert of alerts) { await this.alertRepository.hardDelete(alert.id || alert._id); } } return true; } catch (error) { throw new Error(`Failed to delete alerts: ${error instanceof Error ? error.message : error}`); } } /** * Soft delete an alert with business logic */ async deleteAlert(id, deletedBy) { if (!id || !deletedBy) { throw new Error("Alert ID and deleted by user are required"); } // Business logic: Check if alert can be deleted const alert = await this.alertRepository.findById(id); if (!alert) { throw new Error("Alert not found"); } // Business logic: Prevent deletion of critical alerts (optional rule) if (alert.severity === alert_types_1.AlertSeverity.CRITICAL) { throw new Error("Cannot delete critical alerts"); } return await this.alertRepository.softDelete(id, deletedBy); } /** * Mark alert as read with business logic */ async markAsRead(id, updatedBy) { if (!id || !updatedBy) { throw new Error("Alert ID and updated by user are required"); } const alertModel = await Alert_model_1.AlertModel.findById(id); if (!alertModel) return null; alertModel.markAsRead(updatedBy); return await alertModel.save(); } /** * Mark alert as unread with business logic */ async markAsUnread(id, updatedBy) { if (!id || !updatedBy) { throw new Error("Alert ID and updated by user are required"); } const alertModel = await Alert_model_1.AlertModel.findById(id); if (!alertModel) return null; alertModel.markAsUnread(updatedBy); return await alertModel.save(); } /** * Activate an alert with business logic */ async activateAlert(id, updatedBy) { if (!id || !updatedBy) { throw new Error("Alert ID and updated by user are required"); } const alertModel = await Alert_model_1.AlertModel.findById(id); if (!alertModel) return null; alertModel.activate(updatedBy); return await alertModel.save(); } /** * Deactivate an alert with business logic */ async deactivateAlert(query, updatedBy) { if (!query || !updatedBy) throw new Error("Alert query and updated by user are required"); const alerts = await this.queryAlerts(query); if (!alerts.length) return null; for (const alert of alerts) { const alertModel = await Alert_model_1.AlertModel.findById(alert._id || alert.id); if (!alertModel) return null; alertModel.deactivate(updatedBy); await alertModel.save(); } return null; } /** * Snooze an alert with business logic */ async snoozeAlert(id, updatedBy, until) { if (!id || !updatedBy) { throw new Error("Alert ID and updated by user are required"); } until ?? (until = new Date(Date.now() + 1000 * 60 * 60 * 24)); // 1 day // Business logic: Validate snooze date this.validateSnoozeDate(until); const alertModel = await Alert_model_1.AlertModel.findById(id); if (!alertModel) return null; alertModel.snooze(until, updatedBy); return await alertModel.save(); } /** * Unsnooze an alert with business logic */ async unsnoozeAlert(id, updatedBy) { if (!id || !updatedBy) { throw new Error("Alert ID and updated by user are required"); } const alertModel = await Alert_model_1.AlertModel.findById(id); if (!alertModel) return null; alertModel.unsnooze(updatedBy); return await alertModel.save(); } /** * Get alerts by category with business logic */ async getAlertsByCategory(category, includeDeleted = false) { if (!category) { throw new Error("Alert category is required"); } return await this.alertRepository.findByCategory(category, includeDeleted); } /** * Get snoozed alerts with business logic */ async getSnoozedAlerts(includeDeleted = false) { return await this.alertRepository.findSnoozed(includeDeleted); } /** * Get expired snooze alerts with business logic */ async getExpiredSnoozeAlerts(includeDeleted = false) { const expiredAlerts = await this.alertRepository.findExpiredSnooze(includeDeleted); // Business logic: Log expired snooze alerts if (expiredAlerts.length > 0) { console.warn(`Found ${expiredAlerts.length} alerts with expired snooze`); } return expiredAlerts; } /** * Get alert statistics with business logic */ async getAlertStatistics(propertyId, zoneId) { const stats = await this.alertRepository.getStatistics(propertyId, zoneId); // Business logic: Add alerts for critical metrics if (stats.unread > 0) { console.warn(`Alert: ${stats.unread} unread alerts require attention`); } if (stats.bySeverity[alert_types_1.AlertSeverity.CRITICAL] > 0) { console.error(`Alert: ${stats.bySeverity[alert_types_1.AlertSeverity.CRITICAL]} critical alerts require immediate attention`); } if (stats.bySeverity[alert_types_1.AlertSeverity.HIGH] > 0) { console.warn(`Alert: ${stats.bySeverity[alert_types_1.AlertSeverity.HIGH]} high severity alerts require attention`); } return stats; } /** * Get alerts by zone ID */ async getAlertsByZoneId(zoneId, includeDeleted = false) { if (!zoneId) { throw new Error("Zone ID is required"); } return await this.alertRepository.findByZoneId(zoneId, includeDeleted); } /** * Get alerts by zone ID and category */ async getAlertsByZoneIdAndCategory(zoneId, category, includeDeleted = false) { if (!zoneId) { throw new Error("Zone ID is required"); } if (!category) { throw new Error("Alert category is required"); } return await this.alertRepository.findByZoneIdAndCategory(zoneId, category, includeDeleted); } /** * Get alerts by zone ID and severity */ async getAlertsByZoneIdAndSeverity(zoneId, severity, includeDeleted = false) { if (!zoneId) { throw new Error("Zone ID is required"); } if (!severity) { throw new Error("Alert severity is required"); } return await this.alertRepository.findByZoneIdAndSeverity(zoneId, severity, includeDeleted); } /** * Get alerts by multiple zone IDs */ async getAlertsByZoneIds(zoneIds, includeDeleted = false) { if (!zoneIds || zoneIds.length === 0) { throw new Error("Zone IDs array is required and cannot be empty"); } return await this.alertRepository.findByZoneIds(zoneIds, includeDeleted); } /** * Get alert statistics by zone ID and severity */ async getAlertStatisticsByZoneAndSeverity(zoneId, severity) { if (!zoneId) { throw new Error("Zone ID is required"); } const stats = await this.alertRepository.getStatisticsByZoneAndSeverity(zoneId, severity); // Business logic: Add alerts for critical metrics if (stats.unread > 0) { console.warn(`Alert: ${stats.unread} unread alerts in zone ${zoneId} require attention`); } if (severity === alert_types_1.AlertSeverity.CRITICAL && stats.total > 0) { console.error(`Alert: ${stats.total} critical alerts in zone ${zoneId} require immediate attention`); } return stats; } /** * Get alerts by zone ID and active status */ async getAlertsByZoneIdAndActiveStatus(zoneId, isActive, includeDeleted = false) { if (!zoneId) { throw new Error("Zone ID is required"); } return await this.alertRepository.findByZoneIdAndActiveStatus(zoneId, isActive, includeDeleted); } /** * Get alerts by zone ID and read status */ async getAlertsByZoneIdAndReadStatus(zoneId, isRead, includeDeleted = false) { if (!zoneId) { throw new Error("Zone ID is required"); } return await this.alertRepository.findByZoneIdAndReadStatus(zoneId, isRead, includeDeleted); } // Private business logic methods validateAlertData(data) { if (!data.title || data.title.trim().length < 3) { throw new Error("Alert title must be at least 3 characters long"); } if (!data.description || data.description.trim().length < 5) { throw new Error("Alert description must be at least 5 characters long"); } if (!data.propertyId) { throw new Error("Property ID is required"); } if (!data.entityType) { throw new Error("Entity type is required"); } if (!data.type) { throw new Error("Alert type is required"); } } validateFilters(filters) { if (filters.limit && (filters.limit < 1 || filters.limit > 100)) { throw new Error("Limit must be between 1 and 100"); } if (filters.skip && filters.skip < 0) { throw new Error("Skip must be non-negative"); } // Validate date filters if (filters.startDate && filters.endDate && filters.startDate > filters.endDate) { throw new Error("Start date must be before or equal to end date"); } } validateUpdateData(data) { if (data.title && data.title.trim().length < 3) { throw new Error("Alert title must be at least 3 characters long"); } if (data.description && data.description.trim().length < 5) { throw new Error("Alert description must be at least 5 characters long"); } } validateSnoozeDate(snoozeUntil) { if (snoozeUntil && snoozeUntil <= new Date()) { throw new Error("Snooze date must be in the future"); } } determineDefaultSeverity(category, type) { // Business logic: Determine default severity based on category const categorySeverities = { [alert_types_1.AlertCategory.OPERATIONS]: alert_types_1.AlertSeverity.HIGH, [alert_types_1.AlertCategory.SECURITY]: alert_types_1.AlertSeverity.CRITICAL, [alert_types_1.AlertCategory.ENERGY]: alert_types_1.AlertSeverity.LOW, [alert_types_1.AlertCategory.OTHER]: alert_types_1.AlertSeverity.LOW, }; return categorySeverities[category] || alert_types_1.AlertSeverity.LOW; } applyBusinessRules(filters) { // Business logic: Apply additional filters based on business rules const enhancedFilters = { ...filters }; // Example: Always exclude deleted alerts unless explicitly requested if (!enhancedFilters.includeDeleted) { enhancedFilters.includeDeleted = false; } // Example: Always exclude snoozed alerts unless explicitly requested if (!enhancedFilters.includeSnoozed) { enhancedFilters.includeSnoozed = false; } return enhancedFilters; } }; __setFunctionName(_classThis, "AlertService"); (() => { const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0; __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers); AlertService = _classThis = _classDescriptor.value; if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata }); __runInitializers(_classThis, _classExtraInitializers); })(); return AlertService = _classThis; })(); exports.AlertService = AlertService;