@gzl10/nexus-plugin-notifications 0.16.1 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,23 @@
1
+ # @gzl10/nexus-plugin-notifications
2
+
3
+ Real-time in-app notifications plugin for Nexus BaaS.
4
+
5
+ ## Features
6
+
7
+ - In-app notifications via Socket.IO
8
+ - Notification types and priorities
9
+ - Read/unread tracking
10
+ - Programmatic sending via service API
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ nexus plugin add @gzl10/nexus-plugin-notifications
16
+ nexus migrate dev
17
+ ```
18
+
19
+ ## Funding
20
+
21
+ If you find this plugin useful, consider supporting its development:
22
+
23
+ [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-support-yellow?logo=buymeacoffee)](https://buymeacoffee.com/gzl10)
package/dist/index.js CHANGED
@@ -1,3 +1,7 @@
1
+ // src/index.ts
2
+ import { readFileSync } from "fs";
3
+ import { join } from "path";
4
+
1
5
  // src/notifications.entity.ts
2
6
  import { useIdField, useTextField, useTextareaField, useSelectField, useUrlField, useDatetimeField, useExpiresAtField } from "@gzl10/nexus-sdk/fields";
3
7
 
@@ -558,13 +562,14 @@ var notificationsModule = {
558
562
  };
559
563
 
560
564
  // src/index.ts
565
+ var pkg = JSON.parse(readFileSync(join(import.meta.dirname, "..", "package.json"), "utf-8"));
561
566
  var notificationsPlugin = {
562
- name: "@gzl10/nexus-plugin-notifications",
567
+ name: pkg.name,
563
568
  code: "ntf",
564
569
  label: { en: "Notifications", es: "Notificaciones" },
565
570
  icon: "mdi:bell-outline",
566
571
  category: "messaging",
567
- version: "0.14.0",
572
+ version: pkg.version,
568
573
  description: {
569
574
  en: "Real-time in-app notifications via Socket.IO",
570
575
  es: "Notificaciones in-app en tiempo real v\xEDa Socket.IO"
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/notifications.entity.ts","../src/notifications.service.ts","../src/notifications.types.ts","../src/notifications.routes.ts","../src/notifications.socket.ts","../src/notifications-module.ts","../src/index.ts"],"sourcesContent":["import type { EventEntityDefinition, ActionDefinition } from '@gzl10/nexus-sdk'\nimport { useIdField, useTextField, useTextareaField, useSelectField, useUrlField, useDatetimeField, useExpiresAtField } from '@gzl10/nexus-sdk/fields'\nimport { getNotificationService } from './notifications.service.js'\nimport {\n NOTIFICATION_TYPE_OPTIONS,\n NOTIFICATION_PRIORITY_OPTIONS,\n NOTIFICATION_TARGET_OPTIONS,\n sendNotificationInputSchema\n} from './notifications.types.js'\nimport type { SendNotificationInput } from './notifications.types.js'\n\n/**\n * Notification (event) - ephemeral notifications with TTL\n */\nexport const notificationEntity: EventEntityDefinition = {\n type: 'event',\n realtime: 'sync',\n immutable: true,\n table: 'notifications',\n routePrefix: '/',\n label: { en: 'Notification', es: 'Notificación' },\n labelPlural: { en: 'Notifications', es: 'Notificaciones' },\n labelField: 'title',\n timestamps: true,\n retention: { days: 30 },\n calendarFrom: 'expires_at',\n fields: {\n id: useIdField(),\n title: useTextField({ label: { en: 'Title', es: 'Título' }, required: true }),\n message: useTextareaField({ label: { en: 'Message', es: 'Mensaje' }, required: true }),\n type: useSelectField({\n label: { en: 'Type', es: 'Tipo' },\n options: [...NOTIFICATION_TYPE_OPTIONS],\n defaultValue: 'info',\n required: true,\n size: 20\n }),\n priority: useSelectField({\n label: { en: 'Priority', es: 'Prioridad' },\n options: [...NOTIFICATION_PRIORITY_OPTIONS],\n defaultValue: 'normal',\n required: true,\n size: 10\n }),\n target_type: useSelectField({\n label: { en: 'Target Type', es: 'Tipo de destino' },\n options: [...NOTIFICATION_TARGET_OPTIONS],\n required: true,\n size: 20\n }),\n target_value: useTextField({ label: { en: 'Target Value', es: 'Valor del destino' } }),\n link: useUrlField({ label: { en: 'Link', es: 'Enlace' } }),\n expires_at: useExpiresAtField()\n },\n actions: [\n {\n key: 'delete',\n scope: 'row',\n icon: 'mdi:delete-outline',\n label: { en: 'Delete Notification', es: 'Eliminar notificación' },\n method: 'DELETE',\n handler: async (_ctx, input: unknown) => {\n const record = (input as Record<string, unknown>)?._record as Record<string, unknown> | undefined\n const id = typeof record?.id === 'string' ? record.id : undefined\n if (!id) return { deleted: false }\n const service = getNotificationService()\n const deleted = await service.delete(id)\n return { deleted }\n },\n casl: {\n subject: 'Notification',\n permissions: {\n ADMIN: { actions: ['execute'] }\n }\n }\n } satisfies ActionDefinition\n ],\n casl: {\n subject: 'Notification',\n permissions: {\n MANAGER: { actions: ['read'] },\n EDITOR: { actions: ['read'] },\n CONTRIBUTOR: { actions: ['read'] },\n USER: [\n { actions: ['read'], conditions: { target_type: 'user', target_value: '${user.id}' } },\n { actions: ['read'], conditions: { target_type: 'all' } },\n { actions: ['read'], conditions: { target_type: 'authenticated' } },\n ],\n VIEWER: [\n { actions: ['read'], conditions: { target_type: 'user', target_value: '${user.id}' } },\n { actions: ['read'], conditions: { target_type: 'all' } },\n { actions: ['read'], conditions: { target_type: 'authenticated' } },\n ],\n SUPPORT: { actions: ['read'] }\n }\n }\n}\n\n/**\n * NotificationRead (event) - read log\n */\nexport const notificationReadEntity: EventEntityDefinition = {\n type: 'event',\n immutable: true,\n table: 'notification_reads',\n label: { en: 'Notification Read', es: 'Lectura de notificación' },\n labelPlural: { en: 'Notification Reads', es: 'Lecturas de notificaciones' },\n labelField: 'notification_id',\n retention: { days: 90 },\n fields: {\n id: useIdField(),\n notification_id: useTextField({ label: { en: 'Notification ID', es: 'ID de notificación' }, required: true, size: 26, hidden: true }),\n user_id: useTextField({ label: { en: 'User ID', es: 'ID de usuario' }, required: true, size: 26, hidden: true }),\n read_at: useDatetimeField({ label: { en: 'Read At', es: 'Leído el' }, required: true })\n },\n indexes: [\n { columns: ['notification_id', 'user_id'], unique: true }\n ],\n casl: {\n subject: 'NotificationRead',\n permissions: {\n ADMIN: { actions: ['manage'] }\n }\n }\n}\n\n/**\n * SendNotification (action) - send notification\n */\nexport const sendNotificationAction: ActionDefinition = {\n key: \"send\",\n scope: \"module\",\n label: { en: \"Send Notification\", es: \"Enviar notificación\" },\n output: {},\n inputSchema: sendNotificationInputSchema,\n handler: async (_ctx, input: unknown) => {\n // Usar getNotificationService() directamente, no ctx.services\n // porque createModuleRouters() sobrescribe ctx.services['notifications']\n // con el EventEntityService de notificationEntity\n const service = getNotificationService();\n return service.send(input as SendNotificationInput);\n },\n input: {\n title: useTextField({ label: { en: 'Title', es: 'Título' }, required: true }),\n message: useTextareaField({ label: { en: 'Message', es: 'Mensaje' }, required: true }),\n target_type: useSelectField({\n label: { en: 'Target Type', es: 'Tipo de destino' },\n options: [...NOTIFICATION_TARGET_OPTIONS],\n required: true,\n }),\n target_value: useTextField({ label: { en: 'Target Value', es: 'Valor del destino' } }),\n type: useSelectField({\n label: { en: 'Type', es: 'Tipo' },\n options: [...NOTIFICATION_TYPE_OPTIONS],\n }),\n priority: useSelectField({\n label: { en: 'Priority', es: 'Prioridad' },\n options: [...NOTIFICATION_PRIORITY_OPTIONS],\n }),\n link: useUrlField({ label: { en: 'Link', es: 'Enlace' } }),\n expires_at: useDatetimeField({ label: { en: 'Expires At', es: 'Expira el' } }),\n },\n casl: {\n subject: \"Notification\",\n permissions: {\n ADMIN: { actions: [\"execute\"] },\n },\n },\n};\n","import type { Knex } from 'knex'\nimport type { Logger } from 'pino'\nimport type { ModuleContext } from '@gzl10/nexus-sdk'\nimport type {\n SendNotificationInput,\n Notification,\n NotificationPayload\n} from './notifications.types.js'\n\n/**\n * Notifications service\n */\nexport class NotificationService {\n private db: Knex\n private logger: Logger\n private generateId: () => string\n private nowTimestamp: (db: Knex) => string\n private formatTimestamp: (db: Knex, date?: Date) => string\n private safeJsonParse: ModuleContext['core']['safeJsonParse']\n private socket: ModuleContext['core']['socket']\n private eventsNotify: ModuleContext['events']['notify']\n private t: (name: string) => string\n\n constructor(ctx: ModuleContext) {\n this.db = ctx.db.knex\n this.logger = ctx.core.logger.child({ service: 'notifications' })\n this.generateId = ctx.core.generateId\n this.nowTimestamp = ctx.db.nowTimestamp\n this.formatTimestamp = ctx.db.formatTimestamp\n this.safeJsonParse = ctx.core.safeJsonParse\n this.socket = ctx.core.socket\n this.eventsNotify = ctx.events.notify.bind(ctx.events)\n this.t = ctx.db.t\n }\n\n /**\n * Sends a notification\n */\n async send(options: SendNotificationInput): Promise<{ id: string; sent: number }> {\n const { target_type, target_value } = options\n let sent = 0\n\n const type = options.type || 'info'\n const priority = options.priority || 'normal'\n\n // 1. Persistir notificación\n const id = this.generateId()\n const now = this.nowTimestamp(this.db)\n\n await this.db(this.t('notifications')).insert({\n id,\n title: options.title,\n message: options.message,\n type,\n priority,\n target_type,\n target_value: target_value || null,\n link: options.link || null,\n expires_at: options.expires_at ? this.formatTimestamp(this.db, new Date(options.expires_at)) : null,\n created_at: now\n })\n\n // 2. Emitir por Socket.IO si está inicializado (via ctx.core.socket helpers)\n if (this.socket.isInitialized()) {\n const payload: NotificationPayload = {\n id,\n title: options.title,\n message: options.message,\n type,\n priority,\n link: options.link || undefined,\n timestamp: now\n }\n\n switch (target_type) {\n case 'all':\n this.socket.emitToAll('notification', payload)\n sent = 1 // broadcast, exact count not critical\n break\n\n case 'authenticated':\n this.socket.emitToAuthenticated('notification', payload)\n sent = 1\n break\n\n case 'role':\n if (target_value) {\n this.socket.emitToRole(target_value, 'notification', payload)\n sent = 1\n }\n break\n\n case 'users':\n if (target_value) {\n const userIds = this.safeJsonParse<string[]>(\n target_value,\n [],\n { target_value, context: 'send.users' }\n )\n for (const userId of userIds) {\n this.socket.emitToUser(userId, 'notification', payload)\n }\n sent = userIds.length\n }\n break\n\n case 'user':\n if (target_value) {\n this.socket.emitToUser(target_value, 'notification', payload)\n sent = this.socket.isUserConnected(target_value) ? 1 : 0\n }\n break\n }\n\n this.logger.debug({ id, target_type, sent }, 'Notification sent')\n }\n\n // 3. Emitir evento interno\n this.eventsNotify('notifications.sent', { id, target_type, target_value, sent })\n\n return { id, sent }\n }\n\n /**\n * Checks if a user has access to a notification based on its target\n */\n canUserAccess(notification: Notification, userId: string, roleIds?: string[]): boolean {\n switch (notification.target_type) {\n case 'all':\n case 'authenticated':\n return true\n case 'role':\n return roleIds ? roleIds.includes(notification.target_value || '') : false\n case 'users': {\n const userIds = this.safeJsonParse<string[]>(\n notification.target_value || '[]',\n [],\n { notificationId: notification.id, context: 'canUserAccess.users' }\n )\n return userIds.includes(userId)\n }\n case 'user':\n return notification.target_value === userId\n default:\n return false\n }\n }\n\n /**\n * Marks a notification as read by a user.\n * If roleIds is provided, verifies access before marking.\n * Uses onConflict to prevent duplicates atomically.\n */\n async markAsRead(notificationId: string, userId: string, roleIds?: string[]): Promise<boolean> {\n const notification = await this.findById(notificationId)\n if (!notification) return false\n\n // Access check when roleIds provided (Socket.IO path)\n if (roleIds !== undefined && !this.canUserAccess(notification, userId, roleIds)) {\n return false\n }\n\n await this.db(this.t('notification_reads'))\n .insert({\n id: this.generateId(),\n notification_id: notificationId,\n user_id: userId,\n read_at: this.nowTimestamp(this.db)\n })\n .onConflict(['notification_id', 'user_id'])\n .ignore()\n\n // Emit Socket.IO for tab sync\n if (this.socket.isInitialized()) {\n this.socket.emitToUser(userId, 'notification:read', { notificationId })\n }\n\n this.logger.debug({ notificationId, userId }, 'Notification marked as read')\n return true\n }\n\n /**\n * Marks all unread notifications as read by a user.\n * Batches DB operations for efficiency.\n */\n async markAllAsRead(userId: string, roleIds?: string[], userCreatedAt?: Date | string): Promise<number> {\n const unread = await this.getUnread(userId, roleIds, userCreatedAt)\n if (unread.length === 0) return 0\n\n const now = this.nowTimestamp(this.db)\n\n // Batch insert into notification_reads (UNIQUE constraint prevents duplicates)\n const reads = unread.map(n => ({\n id: this.generateId(),\n notification_id: n.id,\n user_id: userId,\n read_at: now\n }))\n\n // Batch insert with conflict handling — idempotent under concurrent calls\n await this.db.transaction(async (trx) => {\n for (const read of reads) {\n await trx(this.t('notification_reads'))\n .insert(read)\n .onConflict(['notification_id', 'user_id'])\n .ignore()\n }\n })\n\n // Single batch event for tab sync (not N individual events)\n if (this.socket.isInitialized()) {\n this.socket.emitToUser(userId, 'notification:read', {\n notificationIds: unread.map(n => n.id)\n })\n }\n\n this.logger.debug({ userId, count: unread.length }, 'All notifications marked as read')\n return unread.length\n }\n\n /**\n * Gets unread notifications for a user.\n * Only returns notifications created after the user's registration date.\n * Uses LEFT JOIN with notification_reads to determine read status.\n */\n async getUnread(userId: string, roleIds?: string[], userCreatedAt?: Date | string): Promise<Notification[]> {\n const now = this.nowTimestamp(this.db)\n\n const db = this.db\n const t = this.t\n const query = db(t('notifications'))\n .leftJoin(t('notification_reads'), function () {\n this.on(`${t('notifications')}.id`, '=', `${t('notification_reads')}.notification_id`)\n .andOn(`${t('notification_reads')}.user_id`, '=', db.raw('?', [userId]))\n })\n .whereNull(`${t('notification_reads')}.id`)\n .where(function () {\n this.whereNull(`${t('notifications')}.expires_at`).orWhere(`${t('notifications')}.expires_at`, '>', now)\n })\n .select(`${t('notifications')}.*`)\n .orderBy(`${t('notifications')}.created_at`, 'desc')\n\n // Filter out notifications created before the user existed\n if (userCreatedAt) {\n query.andWhere(`${t('notifications')}.created_at`, '>=', userCreatedAt)\n }\n\n const notifications = await query\n\n // Filter by target access in memory\n return notifications.filter((n: Notification) => this.canUserAccess(n, userId, roleIds))\n }\n\n /**\n * Gets a notification by ID\n */\n async findById(id: string): Promise<Notification | null> {\n const notification = await this.db(this.t('notifications')).where('id', id).first()\n return notification || null\n }\n\n /**\n * Deletes a notification\n */\n async delete(id: string): Promise<boolean> {\n const deleted = await this.db(this.t('notifications')).where('id', id).delete()\n return deleted > 0\n }\n\n /**\n * Cleans up expired notifications\n */\n async cleanupExpired(): Promise<number> {\n const deleted = await this.db(this.t('notifications'))\n .where('expires_at', '<', this.nowTimestamp(this.db))\n .whereNotNull('expires_at')\n .delete()\n\n if (deleted > 0) {\n this.logger.info({ deleted }, 'Cleaned up expired notifications')\n }\n\n return deleted\n }\n}\n\nlet serviceInstance: NotificationService | null = null\n\n/**\n * Initializes the notifications service\n */\nexport function initNotificationService(ctx: ModuleContext): NotificationService {\n serviceInstance = new NotificationService(ctx)\n return serviceInstance\n}\n\n/**\n * Gets the service instance\n */\nexport function getNotificationService(): NotificationService {\n if (!serviceInstance) {\n throw new Error('NotificationService not initialized')\n }\n return serviceInstance\n}\n","import { z } from 'zod'\n\n// ============================================================================\n// TYPE ALIASES\n// ============================================================================\n\n/**\n * Notification types\n */\nexport type NotificationType = 'info' | 'warning' | 'error' | 'success'\n\n/**\n * Notification priority\n */\nexport type NotificationPriority = 'low' | 'normal' | 'high' | 'urgent'\n\n/**\n * Target types for notifications\n */\nexport type NotificationTarget = 'all' | 'authenticated' | 'role' | 'users' | 'user'\n\n// ============================================================================\n// SHARED OPTIONS (DRY)\n// ============================================================================\n\n/**\n * Notification type options for select fields\n */\nexport const NOTIFICATION_TYPE_OPTIONS = [\n { value: 'info', label: { en: 'Info', es: 'Información' } },\n { value: 'success', label: { en: 'Success', es: 'Éxito' } },\n { value: 'warning', label: { en: 'Warning', es: 'Advertencia' } },\n { value: 'error', label: { en: 'Error', es: 'Error' } }\n] as const\n\n/**\n * Notification priority options for select fields\n */\nexport const NOTIFICATION_PRIORITY_OPTIONS = [\n { value: 'low', label: { en: 'Low', es: 'Baja' } },\n { value: 'normal', label: { en: 'Normal', es: 'Normal' } },\n { value: 'high', label: { en: 'High', es: 'Alta' } },\n { value: 'urgent', label: { en: 'Urgent', es: 'Urgente' } }\n] as const\n\n/**\n * Notification target type options for select fields\n */\nexport const NOTIFICATION_TARGET_OPTIONS = [\n { value: 'user', label: { en: 'User', es: 'Usuario' } },\n { value: 'role', label: { en: 'Role', es: 'Rol' } },\n { value: 'all', label: { en: 'All users', es: 'Todos los usuarios' } }\n] as const\n\n// ============================================================================\n// ZOD SCHEMAS\n// ============================================================================\n\n/**\n * Input schema for sending notifications\n */\n// Preprocess: convert empty strings to undefined for optional fields\nconst emptyToUndefined = z.preprocess((v) => (v === '' ? undefined : v), z.any())\n\nexport const sendNotificationInputSchema = z.object({\n title: z.string().min(1, 'Title is required'),\n message: z.string().min(1, 'Message is required'),\n type: emptyToUndefined.pipe(z.enum(['info', 'warning', 'error', 'success']).optional().default('info')),\n priority: emptyToUndefined.pipe(z.enum(['low', 'normal', 'high', 'urgent']).optional().default('normal')),\n target_type: z.enum(['all', 'authenticated', 'role', 'users', 'user']),\n target_value: z.string().optional(),\n link: z.string().url().optional().or(z.literal('')),\n expires_at: emptyToUndefined.pipe(z.string().datetime().optional())\n}).refine(\n (data) => {\n if (['role', 'user', 'users'].includes(data.target_type)) {\n return !!data.target_value\n }\n return true\n },\n { message: 'target_value is required for role, user, and users target types', path: ['target_value'] }\n)\n\nexport type SendNotificationInput = z.output<typeof sendNotificationInputSchema>\n\n\n// ============================================================================\n// ENTITY TYPES\n// ============================================================================\n\n/**\n * Notification in the DB\n */\nexport interface Notification {\n id: string\n title: string\n message: string\n type: NotificationType\n priority: NotificationPriority\n target_type: NotificationTarget\n target_value: string | null\n link: string | null\n expires_at: Date | null\n created_at: Date\n}\n\n/**\n * Payload sent over Socket.IO\n */\nexport interface NotificationPayload {\n id: string\n title: string\n message: string\n type: NotificationType\n priority: NotificationPriority\n link?: string\n timestamp: string\n}\n","import type { Request, Response, ModuleContext, Router, AuthRequest, BaseUsersService } from '@gzl10/nexus-sdk'\nimport { getNotificationService } from './notifications.service.js'\n\n/**\n * Notifications Routes\n *\n * Auto-mounted from definitions:\n * - sendNotificationAction (action) -> POST /notifications/send\n * - notificationEntity.actions[delete] (row action) -> DELETE /notifications/delete/:id\n *\n * Manual routes defined here:\n * - GET /notifications (admin list)\n * - GET /notifications/unread (user's unread)\n * - POST /notifications/:id/read (mark as read)\n * - POST /notifications/read-all (mark all as read)\n * - POST /notifications/cleanup (admin cleanup)\n */\nexport function createNotificationsRoutes(ctx: ModuleContext): Router {\n const router = ctx.createRouter()\n const { auth } = ctx.core.middleware\n const { UnauthorizedError, ForbiddenError, NotFoundError, ValidationError } = ctx.core.errors\n const usersService = ctx.services.get<BaseUsersService>('users')\n\n if (!auth) {\n throw new Error('Auth middleware not found. Ensure auth module loads before notifications.')\n }\n\n /**\n * GET /notifications/unread\n * Gets unread notifications for the authenticated user\n */\n router.get('/unread', auth, async (req: Request, res: Response) => {\n const user = (req as AuthRequest).user\n if (!user) throw new UnauthorizedError('AUTH_REQUIRED')\n\n const service = getNotificationService()\n const roleIds = await usersService.getRoleIds(user.id)\n const notifications = await service.getUnread(user.id, roleIds, user.created_at)\n\n res.json({\n items: notifications,\n total: notifications.length,\n page: 1,\n limit: notifications.length,\n totalPages: 1,\n hasNext: false\n })\n })\n\n /**\n * POST /notifications/:id/read\n * Marks a notification as read (validates user has access)\n */\n router.post('/:id/read', auth, async (req: Request, res: Response) => {\n const user = (req as AuthRequest).user\n if (!user) throw new UnauthorizedError('AUTH_REQUIRED')\n\n const id = req.params['id']\n if (!id) throw new ValidationError('VALIDATION_ERROR')\n\n const service = getNotificationService()\n\n // Verify notification exists and user has access\n const notification = await service.findById(id)\n if (!notification) throw new NotFoundError('RESOURCE_NOT_FOUND')\n\n const roleIds = await usersService.getRoleIds(user.id)\n if (!service.canUserAccess(notification, user.id, roleIds)) throw new ForbiddenError('FORBIDDEN')\n\n await service.markAsRead(id, user.id)\n res.status(204).send()\n })\n\n /**\n * POST /notifications/read-all\n * Marks all notifications as read\n */\n router.post('/read-all', auth, async (req: Request, res: Response) => {\n const user = (req as AuthRequest).user\n if (!user) throw new UnauthorizedError('AUTH_REQUIRED')\n\n const service = getNotificationService()\n const roleIds = await usersService.getRoleIds(user.id)\n const count = await service.markAllAsRead(user.id, roleIds, user.created_at)\n\n res.json({ count })\n })\n\n /**\n * GET /notifications\n * Lists all notifications (admin only)\n * Note: Manual route because event entity auto-mount uses different path\n */\n router.get('/', auth, async (req: Request, res: Response) => {\n const ability = (req as AuthRequest).ability\n if (!ability?.can('manage', 'all')) throw new ForbiddenError('FORBIDDEN')\n\n const limit = Math.min(Math.max(parseInt(req.query['limit'] as string, 10) || 50, 1), 200)\n const page = Math.max(parseInt(req.query['page'] as string, 10) || 1, 1)\n const offset = (page - 1) * limit\n\n const notifications = await ctx.db.knex(ctx.db.t('notifications'))\n .orderBy('created_at', 'desc')\n .limit(limit)\n .offset(offset)\n\n const countResult = await ctx.db.knex(ctx.db.t('notifications')).count('* as count').first<{ count: string | number }>()\n const total = parseInt(String(countResult?.count || 0))\n const totalPages = Math.ceil(total / limit)\n\n res.json({\n items: notifications,\n total,\n page,\n limit,\n totalPages,\n hasNext: page < totalPages\n })\n })\n\n /**\n * POST /notifications/cleanup\n * Cleans up expired notifications (admin only)\n */\n const cleanupRateLimit = ctx.core.middleware.rateLimit({ windowMs: 60 * 1000, max: 1, message: 'Cleanup solo puede ejecutarse 1 vez por minuto' })\n router.post('/cleanup', cleanupRateLimit, auth, async (req: Request, res: Response) => {\n const ability = (req as AuthRequest).ability\n if (!ability?.can('manage', 'all')) throw new ForbiddenError('FORBIDDEN')\n\n const service = getNotificationService()\n const deleted = await service.cleanupExpired()\n\n res.json({ deleted })\n })\n\n return router\n}\n","import type { ModuleContext } from '@gzl10/nexus-sdk'\nimport { getNotificationService } from './notifications.service.js'\n\ninterface SocketLike {\n data: { userId?: string; roleIds?: string[]; authenticated?: boolean }\n on(event: string, handler: (...args: unknown[]) => void): void\n}\n\n/**\n * Registers Socket.IO handlers for notifications\n */\nexport function registerNotificationSocketHandlers(ctx: ModuleContext): void {\n if (!ctx.core.socket.isInitialized()) {\n ctx.core.logger.debug('Socket.IO not initialized, skipping notification handlers')\n return\n }\n\n const io = ctx.core.socket.getIO() as unknown as { on(event: string, handler: (socket: SocketLike) => void): void }\n\n io.on('connection', (socket: SocketLike) => {\n const { userId, roleIds, authenticated } = socket.data\n\n if (!authenticated || !userId) {\n return\n }\n\n /**\n * notification:read - Mark notification as read (with access check)\n */\n socket.on('notification:read', async (data: { notificationId: string }, callback?: (res: { success: boolean; error?: string }) => void) => {\n try {\n if (!data?.notificationId || typeof data.notificationId !== 'string') {\n callback?.({ success: false, error: 'INVALID_INPUT' })\n return\n }\n\n const service = getNotificationService()\n // roleIds passed → service verifies access internally (single query)\n const success = await service.markAsRead(data.notificationId, userId, roleIds)\n if (!success) {\n callback?.({ success: false, error: 'NOT_FOUND' })\n return\n }\n callback?.({ success: true })\n } catch (err) {\n ctx.core.logger.error({ err, userId, notificationId: data?.notificationId }, 'Error marking notification as read')\n callback?.({ success: false, error: 'INTERNAL_ERROR' })\n }\n })\n\n /**\n * notifications:read-all - Mark all as read\n */\n socket.on('notifications:read-all', async (callback?: (res: { success: boolean; count: number; error?: string }) => void) => {\n try {\n const service = getNotificationService()\n const user = await ctx.db.knex('users').where('id', userId).select('created_at').first()\n\n if (!user) {\n callback?.({ success: false, count: 0, error: 'USER_NOT_FOUND' })\n return\n }\n\n const count = await service.markAllAsRead(userId, roleIds ?? [], user.created_at)\n callback?.({ success: true, count })\n } catch (err) {\n ctx.core.logger.error({ err, userId }, 'Error marking all notifications as read')\n callback?.({ success: false, count: 0, error: 'INTERNAL_ERROR' })\n }\n })\n })\n\n ctx.core.logger.debug('Notification socket handlers registered')\n}\n","/**\n * @module notifications\n * @description Real-time notifications with Socket.IO delivery and persistence\n */\n\nimport type { ModuleManifest } from '@gzl10/nexus-sdk'\nimport {\n notificationEntity,\n notificationReadEntity,\n sendNotificationAction\n} from './notifications.entity.js'\nimport { createNotificationsRoutes } from './notifications.routes.js'\nimport { initNotificationService, getNotificationService, NotificationService } from './notifications.service.js'\nimport { registerNotificationSocketHandlers } from './notifications.socket.js'\n\nexport {\n NotificationService,\n getNotificationService,\n initNotificationService\n}\n\n// Re-export all types from the centralized types file\nexport type {\n NotificationType,\n NotificationPriority,\n NotificationTarget,\n SendNotificationInput,\n Notification,\n NotificationPayload\n} from './notifications.types.js'\n\nexport {\n NOTIFICATION_TYPE_OPTIONS,\n NOTIFICATION_PRIORITY_OPTIONS,\n NOTIFICATION_TARGET_OPTIONS,\n sendNotificationInputSchema\n} from './notifications.types.js'\n\n/**\n * Real-time notifications module\n */\nexport const notificationsModule: ModuleManifest = {\n name: 'notifications',\n label: { en: 'Notifications', es: 'Notificaciones' },\n icon: 'mdi:bell-outline',\n description: { en: 'Real-time notifications via Socket.IO', es: 'Notificaciones en tiempo real vía Socket.IO' },\n category: 'messaging',\n definitions: [\n notificationEntity,\n notificationReadEntity\n ],\n\n actions: [sendNotificationAction],\n\n routePrefix: '/notifications',\n routes: createNotificationsRoutes,\n\n init: (ctx) => {\n const service = initNotificationService(ctx)\n ctx.services.register('notifications', service)\n\n // Registrar handlers Socket.IO cuando esté listo\n ctx.core.socket.onReady(() => registerNotificationSocketHandlers(ctx))\n\n ctx.core.logger.debug('Notifications module initialized')\n }\n}\n","/**\n * @module @gzl10/nexus-plugin-notifications\n * @description Real-time in-app notifications via Socket.IO\n */\n\nimport type { PluginManifest } from '@gzl10/nexus-sdk'\nimport { notificationsModule } from './notifications-module.js'\n\nexport { NotificationService, getNotificationService, initNotificationService } from './notifications.service.js'\n\nexport type {\n NotificationType,\n NotificationPriority,\n NotificationTarget,\n SendNotificationInput,\n Notification,\n NotificationPayload\n} from './notifications.types.js'\n\nexport {\n NOTIFICATION_TYPE_OPTIONS,\n NOTIFICATION_PRIORITY_OPTIONS,\n NOTIFICATION_TARGET_OPTIONS,\n sendNotificationInputSchema\n} from './notifications.types.js'\n\nexport { notificationEntity, notificationReadEntity } from './notifications.entity.js'\n\nexport const notificationsPlugin: PluginManifest = {\n name: '@gzl10/nexus-plugin-notifications',\n code: 'ntf',\n label: { en: 'Notifications', es: 'Notificaciones' },\n icon: 'mdi:bell-outline',\n category: 'messaging',\n version: '0.14.0',\n description: {\n en: 'Real-time in-app notifications via Socket.IO',\n es: 'Notificaciones in-app en tiempo real vía Socket.IO'\n },\n modules: [notificationsModule]\n}\n\nexport default notificationsPlugin\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/notifications.entity.ts","../src/notifications.service.ts","../src/notifications.types.ts","../src/notifications.routes.ts","../src/notifications.socket.ts","../src/notifications-module.ts"],"sourcesContent":["/**\n * @module @gzl10/nexus-plugin-notifications\n * @description Real-time in-app notifications via Socket.IO\n */\n\nimport { readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport type { PluginManifest } from '@gzl10/nexus-sdk'\nimport { notificationsModule } from './notifications-module.js'\n\nconst pkg = JSON.parse(readFileSync(join(import.meta.dirname, '..', 'package.json'), 'utf-8')) as { name: string; version: string }\n\nexport { NotificationService, getNotificationService, initNotificationService } from './notifications.service.js'\n\nexport type {\n NotificationType,\n NotificationPriority,\n NotificationTarget,\n SendNotificationInput,\n Notification,\n NotificationPayload\n} from './notifications.types.js'\n\nexport {\n NOTIFICATION_TYPE_OPTIONS,\n NOTIFICATION_PRIORITY_OPTIONS,\n NOTIFICATION_TARGET_OPTIONS,\n sendNotificationInputSchema\n} from './notifications.types.js'\n\nexport { notificationEntity, notificationReadEntity } from './notifications.entity.js'\n\nexport const notificationsPlugin: PluginManifest = {\n name: pkg.name,\n code: 'ntf',\n label: { en: 'Notifications', es: 'Notificaciones' },\n icon: 'mdi:bell-outline',\n category: 'messaging',\n version: pkg.version,\n description: {\n en: 'Real-time in-app notifications via Socket.IO',\n es: 'Notificaciones in-app en tiempo real vía Socket.IO'\n },\n modules: [notificationsModule]\n}\n\nexport default notificationsPlugin\n","import type { EventEntityDefinition, ActionDefinition } from '@gzl10/nexus-sdk'\nimport { useIdField, useTextField, useTextareaField, useSelectField, useUrlField, useDatetimeField, useExpiresAtField } from '@gzl10/nexus-sdk/fields'\nimport { getNotificationService } from './notifications.service.js'\nimport {\n NOTIFICATION_TYPE_OPTIONS,\n NOTIFICATION_PRIORITY_OPTIONS,\n NOTIFICATION_TARGET_OPTIONS,\n sendNotificationInputSchema\n} from './notifications.types.js'\nimport type { SendNotificationInput } from './notifications.types.js'\n\n/**\n * Notification (event) - ephemeral notifications with TTL\n */\nexport const notificationEntity: EventEntityDefinition = {\n type: 'event',\n realtime: 'sync',\n immutable: true,\n table: 'notifications',\n routePrefix: '/',\n label: { en: 'Notification', es: 'Notificación' },\n labelPlural: { en: 'Notifications', es: 'Notificaciones' },\n labelField: 'title',\n timestamps: true,\n retention: { days: 30 },\n calendarFrom: 'expires_at',\n fields: {\n id: useIdField(),\n title: useTextField({ label: { en: 'Title', es: 'Título' }, required: true }),\n message: useTextareaField({ label: { en: 'Message', es: 'Mensaje' }, required: true }),\n type: useSelectField({\n label: { en: 'Type', es: 'Tipo' },\n options: [...NOTIFICATION_TYPE_OPTIONS],\n defaultValue: 'info',\n required: true,\n size: 20\n }),\n priority: useSelectField({\n label: { en: 'Priority', es: 'Prioridad' },\n options: [...NOTIFICATION_PRIORITY_OPTIONS],\n defaultValue: 'normal',\n required: true,\n size: 10\n }),\n target_type: useSelectField({\n label: { en: 'Target Type', es: 'Tipo de destino' },\n options: [...NOTIFICATION_TARGET_OPTIONS],\n required: true,\n size: 20\n }),\n target_value: useTextField({ label: { en: 'Target Value', es: 'Valor del destino' } }),\n link: useUrlField({ label: { en: 'Link', es: 'Enlace' } }),\n expires_at: useExpiresAtField()\n },\n actions: [\n {\n key: 'delete',\n scope: 'row',\n icon: 'mdi:delete-outline',\n label: { en: 'Delete Notification', es: 'Eliminar notificación' },\n method: 'DELETE',\n handler: async (_ctx, input: unknown) => {\n const record = (input as Record<string, unknown>)?._record as Record<string, unknown> | undefined\n const id = typeof record?.id === 'string' ? record.id : undefined\n if (!id) return { deleted: false }\n const service = getNotificationService()\n const deleted = await service.delete(id)\n return { deleted }\n },\n casl: {\n subject: 'Notification',\n permissions: {\n ADMIN: { actions: ['execute'] }\n }\n }\n } satisfies ActionDefinition\n ],\n casl: {\n subject: 'Notification',\n permissions: {\n MANAGER: { actions: ['read'] },\n EDITOR: { actions: ['read'] },\n CONTRIBUTOR: { actions: ['read'] },\n USER: [\n { actions: ['read'], conditions: { target_type: 'user', target_value: '${user.id}' } },\n { actions: ['read'], conditions: { target_type: 'all' } },\n { actions: ['read'], conditions: { target_type: 'authenticated' } },\n ],\n VIEWER: [\n { actions: ['read'], conditions: { target_type: 'user', target_value: '${user.id}' } },\n { actions: ['read'], conditions: { target_type: 'all' } },\n { actions: ['read'], conditions: { target_type: 'authenticated' } },\n ],\n SUPPORT: { actions: ['read'] }\n }\n }\n}\n\n/**\n * NotificationRead (event) - read log\n */\nexport const notificationReadEntity: EventEntityDefinition = {\n type: 'event',\n immutable: true,\n table: 'notification_reads',\n label: { en: 'Notification Read', es: 'Lectura de notificación' },\n labelPlural: { en: 'Notification Reads', es: 'Lecturas de notificaciones' },\n labelField: 'notification_id',\n retention: { days: 90 },\n fields: {\n id: useIdField(),\n notification_id: useTextField({ label: { en: 'Notification ID', es: 'ID de notificación' }, required: true, size: 26, hidden: true }),\n user_id: useTextField({ label: { en: 'User ID', es: 'ID de usuario' }, required: true, size: 26, hidden: true }),\n read_at: useDatetimeField({ label: { en: 'Read At', es: 'Leído el' }, required: true })\n },\n indexes: [\n { columns: ['notification_id', 'user_id'], unique: true }\n ],\n casl: {\n subject: 'NotificationRead',\n permissions: {\n ADMIN: { actions: ['manage'] }\n }\n }\n}\n\n/**\n * SendNotification (action) - send notification\n */\nexport const sendNotificationAction: ActionDefinition = {\n key: \"send\",\n scope: \"module\",\n label: { en: \"Send Notification\", es: \"Enviar notificación\" },\n output: {},\n inputSchema: sendNotificationInputSchema,\n handler: async (_ctx, input: unknown) => {\n // Usar getNotificationService() directamente, no ctx.services\n // porque createModuleRouters() sobrescribe ctx.services['notifications']\n // con el EventEntityService de notificationEntity\n const service = getNotificationService();\n return service.send(input as SendNotificationInput);\n },\n input: {\n title: useTextField({ label: { en: 'Title', es: 'Título' }, required: true }),\n message: useTextareaField({ label: { en: 'Message', es: 'Mensaje' }, required: true }),\n target_type: useSelectField({\n label: { en: 'Target Type', es: 'Tipo de destino' },\n options: [...NOTIFICATION_TARGET_OPTIONS],\n required: true,\n }),\n target_value: useTextField({ label: { en: 'Target Value', es: 'Valor del destino' } }),\n type: useSelectField({\n label: { en: 'Type', es: 'Tipo' },\n options: [...NOTIFICATION_TYPE_OPTIONS],\n }),\n priority: useSelectField({\n label: { en: 'Priority', es: 'Prioridad' },\n options: [...NOTIFICATION_PRIORITY_OPTIONS],\n }),\n link: useUrlField({ label: { en: 'Link', es: 'Enlace' } }),\n expires_at: useDatetimeField({ label: { en: 'Expires At', es: 'Expira el' } }),\n },\n casl: {\n subject: \"Notification\",\n permissions: {\n ADMIN: { actions: [\"execute\"] },\n },\n },\n};\n","import type { Knex } from 'knex'\nimport type { Logger } from 'pino'\nimport type { ModuleContext } from '@gzl10/nexus-sdk'\nimport type {\n SendNotificationInput,\n Notification,\n NotificationPayload\n} from './notifications.types.js'\n\n/**\n * Notifications service\n */\nexport class NotificationService {\n private db: Knex\n private logger: Logger\n private generateId: () => string\n private nowTimestamp: (db: Knex) => string\n private formatTimestamp: (db: Knex, date?: Date) => string\n private safeJsonParse: ModuleContext['core']['safeJsonParse']\n private socket: ModuleContext['core']['socket']\n private eventsNotify: ModuleContext['events']['notify']\n private t: (name: string) => string\n\n constructor(ctx: ModuleContext) {\n this.db = ctx.db.knex\n this.logger = ctx.core.logger.child({ service: 'notifications' })\n this.generateId = ctx.core.generateId\n this.nowTimestamp = ctx.db.nowTimestamp\n this.formatTimestamp = ctx.db.formatTimestamp\n this.safeJsonParse = ctx.core.safeJsonParse\n this.socket = ctx.core.socket\n this.eventsNotify = ctx.events.notify.bind(ctx.events)\n this.t = ctx.db.t\n }\n\n /**\n * Sends a notification\n */\n async send(options: SendNotificationInput): Promise<{ id: string; sent: number }> {\n const { target_type, target_value } = options\n let sent = 0\n\n const type = options.type || 'info'\n const priority = options.priority || 'normal'\n\n // 1. Persistir notificación\n const id = this.generateId()\n const now = this.nowTimestamp(this.db)\n\n await this.db(this.t('notifications')).insert({\n id,\n title: options.title,\n message: options.message,\n type,\n priority,\n target_type,\n target_value: target_value || null,\n link: options.link || null,\n expires_at: options.expires_at ? this.formatTimestamp(this.db, new Date(options.expires_at)) : null,\n created_at: now\n })\n\n // 2. Emitir por Socket.IO si está inicializado (via ctx.core.socket helpers)\n if (this.socket.isInitialized()) {\n const payload: NotificationPayload = {\n id,\n title: options.title,\n message: options.message,\n type,\n priority,\n link: options.link || undefined,\n timestamp: now\n }\n\n switch (target_type) {\n case 'all':\n this.socket.emitToAll('notification', payload)\n sent = 1 // broadcast, exact count not critical\n break\n\n case 'authenticated':\n this.socket.emitToAuthenticated('notification', payload)\n sent = 1\n break\n\n case 'role':\n if (target_value) {\n this.socket.emitToRole(target_value, 'notification', payload)\n sent = 1\n }\n break\n\n case 'users':\n if (target_value) {\n const userIds = this.safeJsonParse<string[]>(\n target_value,\n [],\n { target_value, context: 'send.users' }\n )\n for (const userId of userIds) {\n this.socket.emitToUser(userId, 'notification', payload)\n }\n sent = userIds.length\n }\n break\n\n case 'user':\n if (target_value) {\n this.socket.emitToUser(target_value, 'notification', payload)\n sent = this.socket.isUserConnected(target_value) ? 1 : 0\n }\n break\n }\n\n this.logger.debug({ id, target_type, sent }, 'Notification sent')\n }\n\n // 3. Emitir evento interno\n this.eventsNotify('notifications.sent', { id, target_type, target_value, sent })\n\n return { id, sent }\n }\n\n /**\n * Checks if a user has access to a notification based on its target\n */\n canUserAccess(notification: Notification, userId: string, roleIds?: string[]): boolean {\n switch (notification.target_type) {\n case 'all':\n case 'authenticated':\n return true\n case 'role':\n return roleIds ? roleIds.includes(notification.target_value || '') : false\n case 'users': {\n const userIds = this.safeJsonParse<string[]>(\n notification.target_value || '[]',\n [],\n { notificationId: notification.id, context: 'canUserAccess.users' }\n )\n return userIds.includes(userId)\n }\n case 'user':\n return notification.target_value === userId\n default:\n return false\n }\n }\n\n /**\n * Marks a notification as read by a user.\n * If roleIds is provided, verifies access before marking.\n * Uses onConflict to prevent duplicates atomically.\n */\n async markAsRead(notificationId: string, userId: string, roleIds?: string[]): Promise<boolean> {\n const notification = await this.findById(notificationId)\n if (!notification) return false\n\n // Access check when roleIds provided (Socket.IO path)\n if (roleIds !== undefined && !this.canUserAccess(notification, userId, roleIds)) {\n return false\n }\n\n await this.db(this.t('notification_reads'))\n .insert({\n id: this.generateId(),\n notification_id: notificationId,\n user_id: userId,\n read_at: this.nowTimestamp(this.db)\n })\n .onConflict(['notification_id', 'user_id'])\n .ignore()\n\n // Emit Socket.IO for tab sync\n if (this.socket.isInitialized()) {\n this.socket.emitToUser(userId, 'notification:read', { notificationId })\n }\n\n this.logger.debug({ notificationId, userId }, 'Notification marked as read')\n return true\n }\n\n /**\n * Marks all unread notifications as read by a user.\n * Batches DB operations for efficiency.\n */\n async markAllAsRead(userId: string, roleIds?: string[], userCreatedAt?: Date | string): Promise<number> {\n const unread = await this.getUnread(userId, roleIds, userCreatedAt)\n if (unread.length === 0) return 0\n\n const now = this.nowTimestamp(this.db)\n\n // Batch insert into notification_reads (UNIQUE constraint prevents duplicates)\n const reads = unread.map(n => ({\n id: this.generateId(),\n notification_id: n.id,\n user_id: userId,\n read_at: now\n }))\n\n // Batch insert with conflict handling — idempotent under concurrent calls\n await this.db.transaction(async (trx) => {\n for (const read of reads) {\n await trx(this.t('notification_reads'))\n .insert(read)\n .onConflict(['notification_id', 'user_id'])\n .ignore()\n }\n })\n\n // Single batch event for tab sync (not N individual events)\n if (this.socket.isInitialized()) {\n this.socket.emitToUser(userId, 'notification:read', {\n notificationIds: unread.map(n => n.id)\n })\n }\n\n this.logger.debug({ userId, count: unread.length }, 'All notifications marked as read')\n return unread.length\n }\n\n /**\n * Gets unread notifications for a user.\n * Only returns notifications created after the user's registration date.\n * Uses LEFT JOIN with notification_reads to determine read status.\n */\n async getUnread(userId: string, roleIds?: string[], userCreatedAt?: Date | string): Promise<Notification[]> {\n const now = this.nowTimestamp(this.db)\n\n const db = this.db\n const t = this.t\n const query = db(t('notifications'))\n .leftJoin(t('notification_reads'), function () {\n this.on(`${t('notifications')}.id`, '=', `${t('notification_reads')}.notification_id`)\n .andOn(`${t('notification_reads')}.user_id`, '=', db.raw('?', [userId]))\n })\n .whereNull(`${t('notification_reads')}.id`)\n .where(function () {\n this.whereNull(`${t('notifications')}.expires_at`).orWhere(`${t('notifications')}.expires_at`, '>', now)\n })\n .select(`${t('notifications')}.*`)\n .orderBy(`${t('notifications')}.created_at`, 'desc')\n\n // Filter out notifications created before the user existed\n if (userCreatedAt) {\n query.andWhere(`${t('notifications')}.created_at`, '>=', userCreatedAt)\n }\n\n const notifications = await query\n\n // Filter by target access in memory\n return notifications.filter((n: Notification) => this.canUserAccess(n, userId, roleIds))\n }\n\n /**\n * Gets a notification by ID\n */\n async findById(id: string): Promise<Notification | null> {\n const notification = await this.db(this.t('notifications')).where('id', id).first()\n return notification || null\n }\n\n /**\n * Deletes a notification\n */\n async delete(id: string): Promise<boolean> {\n const deleted = await this.db(this.t('notifications')).where('id', id).delete()\n return deleted > 0\n }\n\n /**\n * Cleans up expired notifications\n */\n async cleanupExpired(): Promise<number> {\n const deleted = await this.db(this.t('notifications'))\n .where('expires_at', '<', this.nowTimestamp(this.db))\n .whereNotNull('expires_at')\n .delete()\n\n if (deleted > 0) {\n this.logger.info({ deleted }, 'Cleaned up expired notifications')\n }\n\n return deleted\n }\n}\n\nlet serviceInstance: NotificationService | null = null\n\n/**\n * Initializes the notifications service\n */\nexport function initNotificationService(ctx: ModuleContext): NotificationService {\n serviceInstance = new NotificationService(ctx)\n return serviceInstance\n}\n\n/**\n * Gets the service instance\n */\nexport function getNotificationService(): NotificationService {\n if (!serviceInstance) {\n throw new Error('NotificationService not initialized')\n }\n return serviceInstance\n}\n","import { z } from 'zod'\n\n// ============================================================================\n// TYPE ALIASES\n// ============================================================================\n\n/**\n * Notification types\n */\nexport type NotificationType = 'info' | 'warning' | 'error' | 'success'\n\n/**\n * Notification priority\n */\nexport type NotificationPriority = 'low' | 'normal' | 'high' | 'urgent'\n\n/**\n * Target types for notifications\n */\nexport type NotificationTarget = 'all' | 'authenticated' | 'role' | 'users' | 'user'\n\n// ============================================================================\n// SHARED OPTIONS (DRY)\n// ============================================================================\n\n/**\n * Notification type options for select fields\n */\nexport const NOTIFICATION_TYPE_OPTIONS = [\n { value: 'info', label: { en: 'Info', es: 'Información' } },\n { value: 'success', label: { en: 'Success', es: 'Éxito' } },\n { value: 'warning', label: { en: 'Warning', es: 'Advertencia' } },\n { value: 'error', label: { en: 'Error', es: 'Error' } }\n] as const\n\n/**\n * Notification priority options for select fields\n */\nexport const NOTIFICATION_PRIORITY_OPTIONS = [\n { value: 'low', label: { en: 'Low', es: 'Baja' } },\n { value: 'normal', label: { en: 'Normal', es: 'Normal' } },\n { value: 'high', label: { en: 'High', es: 'Alta' } },\n { value: 'urgent', label: { en: 'Urgent', es: 'Urgente' } }\n] as const\n\n/**\n * Notification target type options for select fields\n */\nexport const NOTIFICATION_TARGET_OPTIONS = [\n { value: 'user', label: { en: 'User', es: 'Usuario' } },\n { value: 'role', label: { en: 'Role', es: 'Rol' } },\n { value: 'all', label: { en: 'All users', es: 'Todos los usuarios' } }\n] as const\n\n// ============================================================================\n// ZOD SCHEMAS\n// ============================================================================\n\n/**\n * Input schema for sending notifications\n */\n// Preprocess: convert empty strings to undefined for optional fields\nconst emptyToUndefined = z.preprocess((v) => (v === '' ? undefined : v), z.any())\n\nexport const sendNotificationInputSchema = z.object({\n title: z.string().min(1, 'Title is required'),\n message: z.string().min(1, 'Message is required'),\n type: emptyToUndefined.pipe(z.enum(['info', 'warning', 'error', 'success']).optional().default('info')),\n priority: emptyToUndefined.pipe(z.enum(['low', 'normal', 'high', 'urgent']).optional().default('normal')),\n target_type: z.enum(['all', 'authenticated', 'role', 'users', 'user']),\n target_value: z.string().optional(),\n link: z.string().url().optional().or(z.literal('')),\n expires_at: emptyToUndefined.pipe(z.string().datetime().optional())\n}).refine(\n (data) => {\n if (['role', 'user', 'users'].includes(data.target_type)) {\n return !!data.target_value\n }\n return true\n },\n { message: 'target_value is required for role, user, and users target types', path: ['target_value'] }\n)\n\nexport type SendNotificationInput = z.output<typeof sendNotificationInputSchema>\n\n\n// ============================================================================\n// ENTITY TYPES\n// ============================================================================\n\n/**\n * Notification in the DB\n */\nexport interface Notification {\n id: string\n title: string\n message: string\n type: NotificationType\n priority: NotificationPriority\n target_type: NotificationTarget\n target_value: string | null\n link: string | null\n expires_at: Date | null\n created_at: Date\n}\n\n/**\n * Payload sent over Socket.IO\n */\nexport interface NotificationPayload {\n id: string\n title: string\n message: string\n type: NotificationType\n priority: NotificationPriority\n link?: string\n timestamp: string\n}\n","import type { Request, Response, ModuleContext, Router, AuthRequest, BaseUsersService } from '@gzl10/nexus-sdk'\nimport { getNotificationService } from './notifications.service.js'\n\n/**\n * Notifications Routes\n *\n * Auto-mounted from definitions:\n * - sendNotificationAction (action) -> POST /notifications/send\n * - notificationEntity.actions[delete] (row action) -> DELETE /notifications/delete/:id\n *\n * Manual routes defined here:\n * - GET /notifications (admin list)\n * - GET /notifications/unread (user's unread)\n * - POST /notifications/:id/read (mark as read)\n * - POST /notifications/read-all (mark all as read)\n * - POST /notifications/cleanup (admin cleanup)\n */\nexport function createNotificationsRoutes(ctx: ModuleContext): Router {\n const router = ctx.createRouter()\n const { auth } = ctx.core.middleware\n const { UnauthorizedError, ForbiddenError, NotFoundError, ValidationError } = ctx.core.errors\n const usersService = ctx.services.get<BaseUsersService>('users')\n\n if (!auth) {\n throw new Error('Auth middleware not found. Ensure auth module loads before notifications.')\n }\n\n /**\n * GET /notifications/unread\n * Gets unread notifications for the authenticated user\n */\n router.get('/unread', auth, async (req: Request, res: Response) => {\n const user = (req as AuthRequest).user\n if (!user) throw new UnauthorizedError('AUTH_REQUIRED')\n\n const service = getNotificationService()\n const roleIds = await usersService.getRoleIds(user.id)\n const notifications = await service.getUnread(user.id, roleIds, user.created_at)\n\n res.json({\n items: notifications,\n total: notifications.length,\n page: 1,\n limit: notifications.length,\n totalPages: 1,\n hasNext: false\n })\n })\n\n /**\n * POST /notifications/:id/read\n * Marks a notification as read (validates user has access)\n */\n router.post('/:id/read', auth, async (req: Request, res: Response) => {\n const user = (req as AuthRequest).user\n if (!user) throw new UnauthorizedError('AUTH_REQUIRED')\n\n const id = req.params['id']\n if (!id) throw new ValidationError('VALIDATION_ERROR')\n\n const service = getNotificationService()\n\n // Verify notification exists and user has access\n const notification = await service.findById(id)\n if (!notification) throw new NotFoundError('RESOURCE_NOT_FOUND')\n\n const roleIds = await usersService.getRoleIds(user.id)\n if (!service.canUserAccess(notification, user.id, roleIds)) throw new ForbiddenError('FORBIDDEN')\n\n await service.markAsRead(id, user.id)\n res.status(204).send()\n })\n\n /**\n * POST /notifications/read-all\n * Marks all notifications as read\n */\n router.post('/read-all', auth, async (req: Request, res: Response) => {\n const user = (req as AuthRequest).user\n if (!user) throw new UnauthorizedError('AUTH_REQUIRED')\n\n const service = getNotificationService()\n const roleIds = await usersService.getRoleIds(user.id)\n const count = await service.markAllAsRead(user.id, roleIds, user.created_at)\n\n res.json({ count })\n })\n\n /**\n * GET /notifications\n * Lists all notifications (admin only)\n * Note: Manual route because event entity auto-mount uses different path\n */\n router.get('/', auth, async (req: Request, res: Response) => {\n const ability = (req as AuthRequest).ability\n if (!ability?.can('manage', 'all')) throw new ForbiddenError('FORBIDDEN')\n\n const limit = Math.min(Math.max(parseInt(req.query['limit'] as string, 10) || 50, 1), 200)\n const page = Math.max(parseInt(req.query['page'] as string, 10) || 1, 1)\n const offset = (page - 1) * limit\n\n const notifications = await ctx.db.knex(ctx.db.t('notifications'))\n .orderBy('created_at', 'desc')\n .limit(limit)\n .offset(offset)\n\n const countResult = await ctx.db.knex(ctx.db.t('notifications')).count('* as count').first<{ count: string | number }>()\n const total = parseInt(String(countResult?.count || 0))\n const totalPages = Math.ceil(total / limit)\n\n res.json({\n items: notifications,\n total,\n page,\n limit,\n totalPages,\n hasNext: page < totalPages\n })\n })\n\n /**\n * POST /notifications/cleanup\n * Cleans up expired notifications (admin only)\n */\n const cleanupRateLimit = ctx.core.middleware.rateLimit({ windowMs: 60 * 1000, max: 1, message: 'Cleanup solo puede ejecutarse 1 vez por minuto' })\n router.post('/cleanup', cleanupRateLimit, auth, async (req: Request, res: Response) => {\n const ability = (req as AuthRequest).ability\n if (!ability?.can('manage', 'all')) throw new ForbiddenError('FORBIDDEN')\n\n const service = getNotificationService()\n const deleted = await service.cleanupExpired()\n\n res.json({ deleted })\n })\n\n return router\n}\n","import type { ModuleContext } from '@gzl10/nexus-sdk'\nimport { getNotificationService } from './notifications.service.js'\n\ninterface SocketLike {\n data: { userId?: string; roleIds?: string[]; authenticated?: boolean }\n on(event: string, handler: (...args: unknown[]) => void): void\n}\n\n/**\n * Registers Socket.IO handlers for notifications\n */\nexport function registerNotificationSocketHandlers(ctx: ModuleContext): void {\n if (!ctx.core.socket.isInitialized()) {\n ctx.core.logger.debug('Socket.IO not initialized, skipping notification handlers')\n return\n }\n\n const io = ctx.core.socket.getIO() as unknown as { on(event: string, handler: (socket: SocketLike) => void): void }\n\n io.on('connection', (socket: SocketLike) => {\n const { userId, roleIds, authenticated } = socket.data\n\n if (!authenticated || !userId) {\n return\n }\n\n /**\n * notification:read - Mark notification as read (with access check)\n */\n socket.on('notification:read', async (data: { notificationId: string }, callback?: (res: { success: boolean; error?: string }) => void) => {\n try {\n if (!data?.notificationId || typeof data.notificationId !== 'string') {\n callback?.({ success: false, error: 'INVALID_INPUT' })\n return\n }\n\n const service = getNotificationService()\n // roleIds passed → service verifies access internally (single query)\n const success = await service.markAsRead(data.notificationId, userId, roleIds)\n if (!success) {\n callback?.({ success: false, error: 'NOT_FOUND' })\n return\n }\n callback?.({ success: true })\n } catch (err) {\n ctx.core.logger.error({ err, userId, notificationId: data?.notificationId }, 'Error marking notification as read')\n callback?.({ success: false, error: 'INTERNAL_ERROR' })\n }\n })\n\n /**\n * notifications:read-all - Mark all as read\n */\n socket.on('notifications:read-all', async (callback?: (res: { success: boolean; count: number; error?: string }) => void) => {\n try {\n const service = getNotificationService()\n const user = await ctx.db.knex('users').where('id', userId).select('created_at').first()\n\n if (!user) {\n callback?.({ success: false, count: 0, error: 'USER_NOT_FOUND' })\n return\n }\n\n const count = await service.markAllAsRead(userId, roleIds ?? [], user.created_at)\n callback?.({ success: true, count })\n } catch (err) {\n ctx.core.logger.error({ err, userId }, 'Error marking all notifications as read')\n callback?.({ success: false, count: 0, error: 'INTERNAL_ERROR' })\n }\n })\n })\n\n ctx.core.logger.debug('Notification socket handlers registered')\n}\n","/**\n * @module notifications\n * @description Real-time notifications with Socket.IO delivery and persistence\n */\n\nimport type { ModuleManifest } from '@gzl10/nexus-sdk'\nimport {\n notificationEntity,\n notificationReadEntity,\n sendNotificationAction\n} from './notifications.entity.js'\nimport { createNotificationsRoutes } from './notifications.routes.js'\nimport { initNotificationService, getNotificationService, NotificationService } from './notifications.service.js'\nimport { registerNotificationSocketHandlers } from './notifications.socket.js'\n\nexport {\n NotificationService,\n getNotificationService,\n initNotificationService\n}\n\n// Re-export all types from the centralized types file\nexport type {\n NotificationType,\n NotificationPriority,\n NotificationTarget,\n SendNotificationInput,\n Notification,\n NotificationPayload\n} from './notifications.types.js'\n\nexport {\n NOTIFICATION_TYPE_OPTIONS,\n NOTIFICATION_PRIORITY_OPTIONS,\n NOTIFICATION_TARGET_OPTIONS,\n sendNotificationInputSchema\n} from './notifications.types.js'\n\n/**\n * Real-time notifications module\n */\nexport const notificationsModule: ModuleManifest = {\n name: 'notifications',\n label: { en: 'Notifications', es: 'Notificaciones' },\n icon: 'mdi:bell-outline',\n description: { en: 'Real-time notifications via Socket.IO', es: 'Notificaciones en tiempo real vía Socket.IO' },\n category: 'messaging',\n definitions: [\n notificationEntity,\n notificationReadEntity\n ],\n\n actions: [sendNotificationAction],\n\n routePrefix: '/notifications',\n routes: createNotificationsRoutes,\n\n init: (ctx) => {\n const service = initNotificationService(ctx)\n ctx.services.register('notifications', service)\n\n // Registrar handlers Socket.IO cuando esté listo\n ctx.core.socket.onReady(() => registerNotificationSocketHandlers(ctx))\n\n ctx.core.logger.debug('Notifications module initialized')\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gzl10/nexus-plugin-notifications",
3
- "version": "0.16.1",
3
+ "version": "0.16.2",
4
4
  "type": "module",
5
5
  "description": "Nexus Plugin: Real-time in-app notifications via Socket.IO",
6
6
  "license": "MIT",
@@ -37,8 +37,8 @@
37
37
  }
38
38
  },
39
39
  "devDependencies": {
40
- "@gzl10/nexus-sdk": "0.16.1",
41
- "@gzl10/nexus-client": "0.16.1"
40
+ "@gzl10/nexus-client": "0.16.2",
41
+ "@gzl10/nexus-sdk": "0.16.2"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "tsup",