import { Router } from "express"; import { storage } from "../storage"; import { authenticateToken, requirePermission, type AuthenticatedRequest } from "../middleware/auth.middleware"; import { tenantIsolation } from "../middleware/tenant.middleware"; import { canManageUser } from "../utils/user-access"; const router = Router(); // GET /api/roles — список всех ролей организации router.get('/api/roles', authenticateToken, tenantIsolation, async (req: AuthenticatedRequest, res) => { try { const roles = await storage.getRoles(req.organizationId!); return res.json({ success: true, roles }); } catch (error) { console.error('Get roles error:', error); return res.status(500).json({ success: false, error: 'Внутренняя ошибка сервера' }); } }); // PATCH /api/roles/:id/module-access — флаги доступа к модулям GPS/Финансы (только admin) router.patch('/api/roles/:id/module-access', authenticateToken, tenantIsolation, async (req: AuthenticatedRequest, res) => { try { if (req.user!.appRole !== 'admin') { return res.status(403).json({ success: false, error: 'Требуются права администратора' }); } const id = parseInt(req.params.id); if (isNaN(id)) return res.status(400).json({ success: false, error: 'Некорректный ID роли' }); const { gpsAccess, financeAccess } = req.body ?? {}; const updates: { gpsAccess?: boolean; financeAccess?: boolean } = {}; if (gpsAccess !== undefined) { if (typeof gpsAccess !== 'boolean') { return res.status(400).json({ success: false, error: 'gpsAccess: boolean' }); } updates.gpsAccess = gpsAccess; } if (financeAccess !== undefined) { if (typeof financeAccess !== 'boolean') { return res.status(400).json({ success: false, error: 'financeAccess: boolean' }); } updates.financeAccess = financeAccess; } if (Object.keys(updates).length === 0) { return res.status(400).json({ success: false, error: 'Передайте gpsAccess и/или financeAccess' }); } const role = await storage.updateRole(id, req.organizationId!, updates); if (!role) return res.status(404).json({ success: false, error: 'Роль не найдена' }); return res.json({ success: true, role: { id: role.id, gpsAccess: role.gpsAccess, financeAccess: role.financeAccess }, }); } catch (error) { console.error('Update role module access error:', error); return res.status(500).json({ success: false, error: 'Ошибка при обновлении доступа к модулям' }); } }); // GET /api/roles/:id — одна роль с участниками router.get('/api/roles/:id', authenticateToken, tenantIsolation, async (req: AuthenticatedRequest, res) => { try { const id = parseInt(req.params.id); if (isNaN(id)) return res.status(400).json({ success: false, error: 'Некорректный ID' }); const role = await storage.getRoleMembers(id, req.organizationId!); return res.json({ success: true, role }); } catch (error: any) { if (error?.message === 'Роль не найдена') return res.status(404).json({ success: false, error: error.message }); console.error('Get role error:', error); return res.status(500).json({ success: false, error: 'Внутренняя ошибка сервера' }); } }); // POST /api/roles — создать роль (только admin) router.post('/api/roles', authenticateToken, requirePermission('roles.manage'), tenantIsolation, async (req: AuthenticatedRequest, res) => { try { const { name, description, parentId, managerId } = req.body; if (!name?.trim()) return res.status(400).json({ success: false, error: 'Название роли обязательно' }); const role = await storage.createRole({ organizationId: req.organizationId!, name: name.trim(), description: description ?? null, parentId: parentId ?? null, managerId: managerId ?? null, }); return res.status(201).json({ success: true, role }); } catch (error) { console.error('Create role error:', error); return res.status(500).json({ success: false, error: 'Внутренняя ошибка сервера' }); } }); // PUT /api/roles/:id — обновить роль (только admin) router.put('/api/roles/:id', authenticateToken, requirePermission('roles.manage'), tenantIsolation, async (req: AuthenticatedRequest, res) => { try { const id = parseInt(req.params.id); if (isNaN(id)) return res.status(400).json({ success: false, error: 'Некорректный ID' }); const { name, description, parentId, managerId } = req.body; const updates: { name?: string; description?: string | null; parentId?: number | null; managerId?: number | null } = {}; if (name !== undefined) updates.name = name.trim(); if (description !== undefined) updates.description = description ?? null; if (parentId !== undefined) updates.parentId = parentId ?? null; if (managerId !== undefined) updates.managerId = managerId ?? null; const role = await storage.updateRole(id, req.organizationId!, updates); if (!role) return res.status(404).json({ success: false, error: 'Роль не найдена' }); return res.json({ success: true, role }); } catch (error) { console.error('Update role error:', error); return res.status(500).json({ success: false, error: 'Внутренняя ошибка сервера' }); } }); // DELETE /api/roles/:id — удалить роль (только admin) router.delete('/api/roles/:id', authenticateToken, requirePermission('roles.manage'), tenantIsolation, async (req: AuthenticatedRequest, res) => { try { const id = parseInt(req.params.id); if (isNaN(id)) return res.status(400).json({ success: false, error: 'Некорректный ID' }); await storage.deleteRole(id, req.organizationId!); return res.json({ success: true }); } catch (error) { console.error('Delete role error:', error); return res.status(500).json({ success: false, error: 'Внутренняя ошибка сервера' }); } }); // POST /api/roles/:id/members — добавить участника в роль (admin или руководитель роли) router.post('/api/roles/:id/members', authenticateToken, tenantIsolation, async (req: AuthenticatedRequest, res) => { try { const roleId = parseInt(req.params.id); if (isNaN(roleId)) return res.status(400).json({ success: false, error: 'Некорректный ID роли' }); const { userId } = req.body; if (!userId) return res.status(400).json({ success: false, error: 'userId обязателен' }); const role = await storage.getRole(roleId, req.organizationId!); if (!role) return res.status(404).json({ success: false, error: 'Роль не найдена' }); const hasAdminPermission = req.user?.appRole === 'admin' || (req.user?.permissions?.includes('roles.manage') ?? false); const isRoleManager = role.managerId === req.user?.id; const canManage = await canManageUser(req.user, parseInt(userId), req.organizationId!); if (!hasAdminPermission && !isRoleManager && !canManage) { return res.status(403).json({ success: false, error: 'Недостаточно прав' }); } await storage.addRoleMember(roleId, parseInt(userId), req.organizationId!); return res.status(201).json({ success: true }); } catch (error: any) { if (error?.message === 'Роль не найдена') return res.status(404).json({ success: false, error: error.message }); console.error('Add role member error:', error); return res.status(500).json({ success: false, error: 'Внутренняя ошибка сервера' }); } }); // DELETE /api/roles/:id/members/:userId — удалить участника из роли (admin или руководитель роли) router.delete('/api/roles/:id/members/:userId', authenticateToken, tenantIsolation, async (req: AuthenticatedRequest, res) => { try { const roleId = parseInt(req.params.id); const userId = parseInt(req.params.userId); if (isNaN(roleId) || isNaN(userId)) return res.status(400).json({ success: false, error: 'Некорректный ID' }); const role = await storage.getRole(roleId, req.organizationId!); if (!role) return res.status(404).json({ success: false, error: 'Роль не найдена' }); const hasAdminPermission = req.user?.appRole === 'admin' || (req.user?.permissions?.includes('roles.manage') ?? false); const isRoleManager = role.managerId === req.user?.id; const canManage = await canManageUser(req.user, userId, req.organizationId!); if (!hasAdminPermission && !isRoleManager && !canManage) { return res.status(403).json({ success: false, error: 'Недостаточно прав' }); } await storage.removeRoleMember(roleId, userId, req.organizationId!); return res.json({ success: true }); } catch (error: any) { if (error?.message === 'Роль не найдена') return res.status(404).json({ success: false, error: error.message }); console.error('Remove role member error:', error); return res.status(500).json({ success: false, error: 'Внутренняя ошибка сервера' }); } }); // PUT /api/roles/:id/members/:userId/move — переместить участника в другую роль router.put('/api/roles/:id/members/:userId/move', authenticateToken, tenantIsolation, async (req: AuthenticatedRequest, res) => { try { const sourceRoleId = parseInt(req.params.id); const userId = parseInt(req.params.userId); const { targetRoleId } = req.body; if (isNaN(sourceRoleId) || isNaN(userId)) { return res.status(400).json({ success: false, error: 'Некорректный ID' }); } if (targetRoleId !== null && (typeof targetRoleId !== 'number' || isNaN(targetRoleId))) { return res.status(400).json({ success: false, error: 'Некорректный targetRoleId' }); } const hasAdminPermission = req.user?.appRole === 'admin' || (req.user?.permissions?.includes('roles.manage') ?? false); const canManage = await canManageUser(req.user, userId, req.organizationId!); if (!hasAdminPermission && !canManage) { return res.status(403).json({ success: false, error: 'Недостаточно прав' }); } await storage.moveRoleMember(sourceRoleId, targetRoleId, userId, req.organizationId!); return res.json({ success: true }); } catch (error: any) { if (error?.message?.includes('не найден')) return res.status(404).json({ success: false, error: error.message }); console.error('Move role member error:', error); return res.status(500).json({ success: false, error: 'Внутренняя ошибка сервера' }); } }); export default router;