"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 NotificationsService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.NotificationsService = void 0; const common_1 = require("@nestjs/common"); const typeorm_1 = require("@nestjs/typeorm"); const typeorm_2 = require("typeorm"); const entities_1 = require("../entities"); const email_1 = require("@modules/email"); let NotificationsService = NotificationsService_1 = class NotificationsService { constructor(notificationRepository, templateRepository, preferenceRepository, emailService) { this.notificationRepository = notificationRepository; this.templateRepository = templateRepository; this.preferenceRepository = preferenceRepository; this.emailService = emailService; this.logger = new common_1.Logger(NotificationsService_1.name); } async create(dto, tenantId) { const notification = this.notificationRepository.create({ user_id: dto.userId, tenant_id: tenantId, type: dto.type || 'info', channel: dto.channel || 'in_app', title: dto.title, message: dto.message, data: dto.data || null, action_url: dto.actionUrl || null, delivery_status: 'pending', }); const saved = await this.notificationRepository.save(notification); if (saved.channel === 'in_app') { saved.delivery_status = 'delivered'; saved.sent_at = new Date(); await this.notificationRepository.save(saved); } if (saved.channel === 'email' && dto.email) { try { const result = await this.emailService.sendEmail({ to: { email: dto.email, name: dto.userName }, subject: saved.title, html: this.formatEmailHtml(saved.title, saved.message, saved.action_url), text: saved.message, metadata: { notificationId: saved.id }, }); if (result.success) { saved.delivery_status = 'delivered'; saved.sent_at = new Date(); this.logger.log(`Email sent to ${dto.email}: ${result.messageId}`); } else { saved.delivery_status = 'failed'; this.logger.error(`Failed to send email to ${dto.email}: ${result.error}`); } await this.notificationRepository.save(saved); } catch (error) { this.logger.error(`Email delivery error: ${error.message}`); saved.delivery_status = 'failed'; await this.notificationRepository.save(saved); } } return saved; } async sendFromTemplate(dto, tenantId) { const template = await this.templateRepository.findOne({ where: { code: dto.templateCode, is_active: true }, }); if (!template) { throw new common_1.NotFoundException(`Template '${dto.templateCode}' no encontrado`); } let message = template.body; let subject = template.subject || ''; if (dto.variables) { for (const [key, value] of Object.entries(dto.variables)) { const regex = new RegExp(`{{${key}}}`, 'g'); message = message.replace(regex, String(value)); subject = subject.replace(regex, String(value)); } } return this.create({ userId: dto.userId, channel: template.channel, title: subject || template.name, message, data: { templateCode: dto.templateCode, variables: dto.variables }, }, tenantId); } async findAllForUser(userId, tenantId, options = {}) { const { page = 1, limit = 20, unreadOnly = false } = options; const whereCondition = { user_id: userId, tenant_id: tenantId, channel: 'in_app', }; if (unreadOnly) { whereCondition.is_read = false; } const [data, total] = await this.notificationRepository.findAndCount({ where: whereCondition, order: { created_at: 'DESC' }, skip: (page - 1) * limit, take: limit, }); const unread = await this.notificationRepository.count({ where: { user_id: userId, tenant_id: tenantId, channel: 'in_app', is_read: false, }, }); return { data, total, unread }; } async markAsRead(notificationId, userId, tenantId) { const notification = await this.notificationRepository.findOne({ where: { id: notificationId, user_id: userId, tenant_id: tenantId }, }); if (!notification) { throw new common_1.NotFoundException('Notificación no encontrada'); } notification.is_read = true; notification.read_at = new Date(); return this.notificationRepository.save(notification); } async markAllAsRead(userId, tenantId) { const result = await this.notificationRepository.update({ user_id: userId, tenant_id: tenantId, is_read: false, }, { is_read: true, read_at: new Date(), }); return result.affected || 0; } async delete(notificationId, userId, tenantId) { const result = await this.notificationRepository.delete({ id: notificationId, user_id: userId, tenant_id: tenantId, }); if (result.affected === 0) { throw new common_1.NotFoundException('Notificación no encontrada'); } } async getUnreadCount(userId, tenantId) { return this.notificationRepository.count({ where: { user_id: userId, tenant_id: tenantId, channel: 'in_app', is_read: false, }, }); } async findAllTemplates() { return this.templateRepository.find({ where: { is_active: true }, order: { category: 'ASC', code: 'ASC' }, }); } async findTemplateByCode(code) { const template = await this.templateRepository.findOne({ where: { code, is_active: true }, }); if (!template) { throw new common_1.NotFoundException(`Template '${code}' no encontrado`); } return template; } async getPreferences(userId, tenantId) { let preferences = await this.preferenceRepository.findOne({ where: { user_id: userId, tenant_id: tenantId }, }); if (!preferences) { preferences = this.preferenceRepository.create({ user_id: userId, tenant_id: tenantId, email_enabled: true, push_enabled: true, in_app_enabled: true, sms_enabled: false, marketing_emails: true, product_updates: true, security_alerts: true, }); await this.preferenceRepository.save(preferences); } return preferences; } async updatePreferences(userId, tenantId, dto) { let preferences = await this.getPreferences(userId, tenantId); if (dto.emailEnabled !== undefined) preferences.email_enabled = dto.emailEnabled; if (dto.pushEnabled !== undefined) preferences.push_enabled = dto.pushEnabled; if (dto.inAppEnabled !== undefined) preferences.in_app_enabled = dto.inAppEnabled; if (dto.smsEnabled !== undefined) preferences.sms_enabled = dto.smsEnabled; if (dto.marketingEmails !== undefined) preferences.marketing_emails = dto.marketingEmails; if (dto.productUpdates !== undefined) preferences.product_updates = dto.productUpdates; if (dto.securityAlerts !== undefined) preferences.security_alerts = dto.securityAlerts; if (dto.categoryPreferences !== undefined) { preferences.category_preferences = dto.categoryPreferences; } return this.preferenceRepository.save(preferences); } formatEmailHtml(title, message, actionUrl) { const actionButton = actionUrl ? `
` : ''; return `${message}
${actionButton}Este es un mensaje automático de Template SaaS
`; } async cleanupOldNotifications(daysToKeep = 30) { const cutoffDate = new Date(); cutoffDate.setDate(cutoffDate.getDate() - daysToKeep); const result = await this.notificationRepository.delete({ created_at: (0, typeorm_2.LessThan)(cutoffDate), is_read: true, }); return result.affected || 0; } }; exports.NotificationsService = NotificationsService; exports.NotificationsService = NotificationsService = NotificationsService_1 = __decorate([ (0, common_1.Injectable)(), __param(0, (0, typeorm_1.InjectRepository)(entities_1.Notification)), __param(1, (0, typeorm_1.InjectRepository)(entities_1.NotificationTemplate)), __param(2, (0, typeorm_1.InjectRepository)(entities_1.UserNotificationPreference)), __metadata("design:paramtypes", [typeorm_2.Repository, typeorm_2.Repository, typeorm_2.Repository, email_1.EmailService]) ], NotificationsService); //# sourceMappingURL=notifications.service.js.map