UNPKG

@tmlmobilidade/interfaces

Version:

This package provides SDK-style connectors for interacting with databases (e.g., stops, plans, rides, alerts) and external providers (e.g., authentication, storage). It simplifies data access and integration across projects.

109 lines (108 loc) 4.24 kB
/* * */ import { MongoCollectionClass } from '../../common/mongo-collection.js'; import { roles } from '../auth/roles.js'; import { users } from '../auth/users.js'; import { getModuleConfig } from '@tmlmobilidade/consts'; import { CreateNotificationSchema, UpdateNotificationSchema } from '@tmlmobilidade/types'; import { asyncSingletonProxy, mergeObjects } from '@tmlmobilidade/utils'; /* * */ class NotificationsClass extends MongoCollectionClass { static _instance; createSchema = CreateNotificationSchema; updateSchema = UpdateNotificationSchema; constructor() { super(); } static async getInstance() { if (!NotificationsClass._instance) { const instance = new NotificationsClass(); await instance.connect(); NotificationsClass._instance = instance; } return NotificationsClass._instance; } async sendNotification(scope, topic, user, id, title, description) { // Fetch roles and users that have access to this topic const rolesWithTopic = await roles.findMany({ 'permissions.action': topic }); const roleIdsWithTopic = rolesWithTopic.map(r => r._id); const usersWithTopic = await users.findMany({ $or: [ { 'permissions.action': topic }, { role_ids: { $in: roleIdsWithTopic } }, ], }); if (usersWithTopic.length === 0) return; // Base notification template const baseNotification = { created_by: user?._id, is_read: false, payload: { body: description, href: `${getModuleConfig(scope, 'frontend_url')}/${scope}/${id}`, icon: scope, title, }, priority: 'normal', scope, topic, updated_by: user?._id, }; // Iterate over eligible users (excluding creator) for (const recipient of usersWithTopic.filter(u => u._id !== baseNotification.created_by)) { const permissions = this.collectUserPermissions(recipient, rolesWithTopic); const canReceiveEmail = this.getNotificationPermission(permissions, topic); const newNotification = { ...baseNotification, user_id: recipient._id }; const result = await notifications.insertOne(newNotification); // // Send email if permission allows // if (canReceiveEmail) { // await sendGenericNotificationEmail({ // data: { // body: result.payload.body, // notificationId: result._id, // notificationUrl: result.payload.href ?? '', // title: result.payload.title, // }, // to: recipient.email, // }); // } } } getCollectionIndexes() { return [ { background: true, key: { name: 1 }, unique: true }, ]; } getCollectionName() { return 'notifications'; } getEnvName() { return 'DATABASE_URI'; } /** * Collects all effective permissions of a user (direct + via roles), * merging duplicates by (scope:action). */ collectUserPermissions(user, rolesWithTopic) { const rolePermissions = rolesWithTopic .filter(role => user.role_ids?.includes(role._id)) .flatMap(role => role.permissions ?? []); const allPermissions = [...rolePermissions, ...(user.permissions ?? [])]; const map = new Map(); for (const permission of allPermissions) { const key = `${permission.scope}:${permission.action}`; const existing = map.get(key); map.set(key, existing ? mergeObjects(existing, permission) : permission); } return map; } /** * Determines whether a user can receive email notifications for a topic. */ getNotificationPermission(permissions, topic) { const permission = permissions.get(`notifications:${topic}`); return permission['resource']?.send_mail ?? false; } } /* * */ export const notifications = asyncSingletonProxy(NotificationsClass);