Files
iistwin/server/routes/chat.notifications.routes.ts

239 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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: 'Ошибка при удалении подписки' });
}
}
);
}