import { Router } from "express"; import { storage } from "../storage"; import { authenticateToken, type AuthenticatedRequest } from "../middleware/auth.middleware"; import { tenantIsolation } from "../middleware/tenant.middleware"; export function registerTaskCrudExtraRoutes(router: ReturnType): void { router.put('/api/tasks/:id/notifications/read', authenticateToken, tenantIsolation, async (req: AuthenticatedRequest, res) => { try { const taskId = parseInt(req.params.id); const userId = req.user!.id; const organizationId = req.organizationId!; if (isNaN(taskId)) { return res.status(400).json({ success: false, error: 'Неверный ID задачи' }); } console.log(`[tasks/:id/notifications/read] Marking read for taskId=${taskId}, userId=${userId}, orgId=${organizationId}`); await storage.markTaskNotificationsAsRead(taskId, userId, organizationId); res.json({ success: true }); } catch (err) { console.error('[tasks/:id/notifications/read]', err); res.status(500).json({ success: false, error: 'Ошибка сервера' }); } }); router.get('/api/tasks/:id/audit-log', authenticateToken, tenantIsolation, async (req: AuthenticatedRequest, res) => { try { const taskId = parseInt(req.params.id); if (isNaN(taskId)) { return res.status(400).json({ success: false, error: 'Неверный ID задачи' }); } const task = await storage.getTask(taskId, req.organizationId!); if (!task) { return res.status(404).json({ success: false, error: 'Задача не найдена' }); } const log = await storage.getTaskAuditLog(taskId, req.organizationId!); res.json({ success: true, log }); } catch (error) { console.error('Get task audit log error:', error); res.status(500).json({ success: false, error: 'Ошибка при получении истории задачи' }); } }); router.get('/api/tasks/:id/subtasks', async (req: AuthenticatedRequest, res) => { try { const taskId = parseInt(req.params.id); if (isNaN(taskId)) { return res.status(400).json({ success: false, error: 'Неверный ID задачи' }); } const subtasks = await storage.getSubtasks(taskId, req.organizationId!); res.json({ success: true, subtasks }); } catch (error) { console.error('Get subtasks error:', error); res.status(500).json({ success: false, error: 'Ошибка при получении подзадач' }); } }); router.get('/api/tasks/:id/tree', async (req: AuthenticatedRequest, res) => { try { const taskId = parseInt(req.params.id); if (isNaN(taskId)) { return res.status(400).json({ success: false, error: 'Неверный ID задачи' }); } const taskTree = await storage.getTaskTree(taskId, req.organizationId!); if (!taskTree) { return res.status(404).json({ success: false, error: 'Задача не найдена' }); } res.json({ success: true, task: taskTree }); } catch (error) { console.error('Get task tree error:', error); res.status(500).json({ success: false, error: 'Ошибка при получении дерева задач' }); } }); /** * @swagger * /api/tasks/{id}/related: * get: * tags: [Tasks] * summary: Получить дерево связанных задач * description: > * Возвращает дерево задач, связанных с указанной через таблицу task_relations. * Связи создаются либо через поля типа «задача» в форме, либо вручную через чат * (кнопка «Привязать»). Каждый узел рекурсивно содержит своих родителей/детей * в поле `nodes` (глубина до 10 уровней). Поле `fieldName` указывает имя поля * связи или «Связь из чата» если привязка выполнена вручную. * parameters: * - in: path * name: id * required: true * schema: * type: integer * description: ID задачи * responses: * 200: * description: Дерево связанных задач * content: * application/json: * schema: * type: object * properties: * success: * type: boolean * example: true * parents: * type: array * description: Задачи-родители (данная задача является дочерней) * items: * $ref: '#/components/schemas/RelatedTreeNode' * children: * type: array * description: Задачи-дети (данная задача является родительской) * items: * $ref: '#/components/schemas/RelatedTreeNode' * 400: * description: Неверный ID задачи * content: * application/json: * schema: * $ref: '#/components/schemas/Error' */ router.get('/api/tasks/:id/related', async (req: AuthenticatedRequest, res) => { try { const taskId = parseInt(req.params.id); if (isNaN(taskId)) { return res.status(400).json({ success: false, error: 'Неверный ID задачи' }); } const relatedTasks = await storage.getRelatedTasks(taskId, req.organizationId!); res.json({ success: true, ...relatedTasks }); } catch (error) { console.error('Get related tasks error:', error); res.status(500).json({ success: false, error: 'Ошибка при получении связанных задач' }); } }); }