import { Router } from "express"; import { storage } from "../storage"; import { authenticateToken, type AuthenticatedRequest } from "../middleware/auth.middleware"; import { tenantIsolation } from "../middleware/tenant.middleware"; import { validateRequest } from "../middleware/validation.middleware"; import { createSubscriptionSchema, updateSubscriptionSchema } from "@shared/schema"; import { notificationService } from "../services/notification.service"; import { canManageUser } from "../utils/user-access"; export function registerChatNotificationRoutes(router: Router): void { // Get user notifications router.get('/api/notifications', authenticateToken, tenantIsolation, async (req: AuthenticatedRequest, res) => { try { const { unread } = req.query; const isReadFilter = unread === 'true' ? false : undefined; const notifications = await storage.getUserNotifications( req.user!.id, req.organizationId!, isReadFilter ); res.json({ success: true, notifications }); } catch (error) { console.error('Get notifications error:', error); res.status(500).json({ success: false, error: 'Ошибка при загрузке уведомлений' }); } } ); // Счётчик непрочитанных уведомлений (бейдж в навигации) router.get('/api/notifications/unread-count', authenticateToken, tenantIsolation, async (req: AuthenticatedRequest, res) => { try { const count = await storage.getUnreadNotificationCount(req.user!.id, req.organizationId!); res.json({ count }); } catch (error) { console.error('Get unread notification count error:', error); res.status(500).json({ success: false, count: 0 }); } } ); // Mark notification as read router.put('/api/notifications/:id/read', authenticateToken, tenantIsolation, async (req: AuthenticatedRequest, res) => { try { const notificationId = parseInt(req.params.id); if (isNaN(notificationId)) { return res.status(400).json({ success: false, error: 'Некорректный ID уведомления' }); } const updatedNotification = await storage.markNotificationAsRead( notificationId, req.user!.id, req.organizationId! ); res.json({ success: true, message: 'Уведомление отмечено как прочитанное', notification: updatedNotification }); } catch (error) { console.error('Mark notification as read error:', error); res.status(500).json({ success: false, error: 'Ошибка при обновлении уведомления' }); } } ); // Mark all notifications as read router.put('/api/notifications/read-all', authenticateToken, tenantIsolation, async (req: AuthenticatedRequest, res) => { try { await storage.markAllNotificationsAsRead(req.user!.id, req.organizationId!); res.json({ success: true, message: 'Все уведомления отмечены как прочитанные' }); } catch (error) { console.error('Mark all notifications as read error:', error); res.status(500).json({ success: false, error: 'Ошибка при обновлении уведомлений' }); } } ); // Delete notification router.delete('/api/notifications/:id', authenticateToken, tenantIsolation, async (req: AuthenticatedRequest, res) => { try { const notificationId = parseInt(req.params.id); if (isNaN(notificationId)) { return res.status(400).json({ success: false, error: 'Некорректный ID уведомления' }); } await storage.deleteNotification(notificationId, req.user!.id, req.organizationId!); res.json({ success: true, message: 'Уведомление удалено' }); } catch (error) { console.error('Delete notification error:', error); res.status(500).json({ success: false, error: 'Ошибка при удалении уведомления' }); } } ); // Get all event types for subscription router.get('/api/subscriptions/event-types', authenticateToken, tenantIsolation, async (req: AuthenticatedRequest, res) => { try { const eventTypes = await notificationService.getEventTypes(); res.json({ success: true, eventTypes }); } catch (error) { console.error('Get event types error:', error); res.status(500).json({ success: false, error: 'Ошибка при загрузке типов событий' }); } } ); // Get user subscriptions // ?userId=N — подписки другого пользователя (только при canManageUser: админ/руководитель) router.get('/api/subscriptions', authenticateToken, tenantIsolation, async (req: AuthenticatedRequest, res) => { try { let targetUserId = req.user!.id; if (req.query.userId !== undefined) { const queryUserId = parseInt(String(req.query.userId)); if (isNaN(queryUserId)) { return res.status(400).json({ success: false, error: 'Некорректный ID пользователя' }); } if (queryUserId !== req.user!.id) { if (!(await canManageUser(req.user, queryUserId, req.organizationId!))) { return res.status(403).json({ success: false, error: 'Нет прав на просмотр подписок этого пользователя' }); } targetUserId = queryUserId; } } const subscriptions = await notificationService.getUserSubscriptions( targetUserId, req.organizationId! ); res.json({ success: true, subscriptions }); } catch (error) { console.error('Get subscriptions error:', error); res.status(500).json({ success: false, error: 'Ошибка при загрузке подписок' }); } } ); // Create subscription // body.userId — создать подписку другому пользователю (только при canManageUser) router.post('/api/subscriptions', authenticateToken, tenantIsolation, validateRequest(createSubscriptionSchema), async (req: AuthenticatedRequest, res) => { try { let targetUserId = req.user!.id; if (req.body.userId !== undefined && req.body.userId !== req.user!.id) { if (!(await canManageUser(req.user, req.body.userId, req.organizationId!))) { return res.status(403).json({ success: false, error: 'Нет прав на управление подписками этого пользователя' }); } targetUserId = req.body.userId; } const subscription = await notificationService.createSubscription({ userId: targetUserId, organizationId: req.organizationId!, eventTypeCode: req.body.eventTypeCode, targetType: req.body.targetType, targetId: req.body.targetId, channels: req.body.channels, }); res.status(201).json({ success: true, message: 'Подписка создана', subscription }); } catch (error) { console.error('Create subscription error:', error); res.status(500).json({ success: false, error: 'Ошибка при создании подписки' }); } } ); // Update subscription router.patch('/api/subscriptions/:id', authenticateToken, tenantIsolation, validateRequest(updateSubscriptionSchema), async (req: AuthenticatedRequest, res) => { try { const subscriptionId = parseInt(req.params.id); if (isNaN(subscriptionId)) { return res.status(400).json({ success: false, error: 'Некорректный ID подписки' }); } const existing = await notificationService.getSubscriptionById(subscriptionId, req.organizationId!); if (!existing) { return res.status(404).json({ success: false, error: 'Подписка не найдена' }); } // Владелец подписки или canManageUser над владельцем (админ/руководитель) if (existing.userId !== req.user!.id && !(await canManageUser(req.user, existing.userId, req.organizationId!))) { return res.status(403).json({ success: false, error: 'Нет прав на изменение этой подписки' }); } const subscription = await notificationService.updateSubscription( subscriptionId, existing.userId, req.organizationId!, req.body ); if (!subscription) { return res.status(404).json({ success: false, error: 'Подписка не найдена' }); } res.json({ success: true, message: 'Подписка обновлена', subscription }); } catch (error) { console.error('Update subscription error:', error); res.status(500).json({ success: false, error: 'Ошибка при обновлении подписки' }); } } ); // Delete subscription router.delete('/api/subscriptions/:id', authenticateToken, tenantIsolation, async (req: AuthenticatedRequest, res) => { try { const subscriptionId = parseInt(req.params.id); if (isNaN(subscriptionId)) { return res.status(400).json({ success: false, error: 'Некорректный ID подписки' }); } const existing = await notificationService.getSubscriptionById(subscriptionId, req.organizationId!); if (!existing) { return res.status(404).json({ success: false, error: 'Подписка не найдена' }); } // Владелец подписки или canManageUser над владельцем (админ/руководитель) if (existing.userId !== req.user!.id && !(await canManageUser(req.user, existing.userId, req.organizationId!))) { return res.status(403).json({ success: false, error: 'Нет прав на удаление этой подписки' }); } await notificationService.deleteSubscription(subscriptionId, existing.userId, req.organizationId!); res.json({ success: true, message: 'Подписка удалена' }); } catch (error) { console.error('Delete subscription error:', error); res.status(500).json({ success: false, error: 'Ошибка при удалении подписки' }); } } ); }