From 696cf4d38c6aedbf9dd3557e5d832d071310d600 Mon Sep 17 00:00:00 2001 From: HotelSync Date: Mon, 20 Apr 2026 20:17:09 +0300 Subject: [PATCH] fix: refresh token re-reads role from DB; remove staff stats cards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /api/auth/refresh now fetches current role from DB instead of using stale Redis payload — role changes now take effect on next page refresh without requiring re-login. Also deactivates token if user.active=false. - Removed role-count stat cards from staff page (user request) Co-Authored-By: Claude Sonnet 4.6 --- backend/src/routes/auth.ts | 29 +++++++++++++++++++++++++---- src/pages/UsersPage.tsx | 27 +++------------------------ 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/backend/src/routes/auth.ts b/backend/src/routes/auth.ts index f8cf671..aea7e8b 100644 --- a/backend/src/routes/auth.ts +++ b/backend/src/routes/auth.ts @@ -291,11 +291,32 @@ const auth: FastifyPluginAsync = async (fastify) => { const stored = await redis.get(`refresh:${refreshToken}`) if (!stored) return reply.code(401).send({ error: 'Invalid or expired refresh token' }) - const payload = JSON.parse(stored) as JwtPayload - const accessToken = fastify.jwt.sign(payload, { - expiresIn: config.jwt.accessExpiry, - }) + const cached = JSON.parse(stored) as JwtPayload + // Re-read role and active status from DB so role changes take effect immediately + const { rows } = await db.query( + `SELECT u.role, u.active, h.slug AS hotel_slug + FROM users u + LEFT JOIN hotels h ON h.id = u.hotel_id + WHERE u.id = $1`, + [cached.sub], + ) + if (!rows[0] || !rows[0].active) { + await redis.del(`refresh:${refreshToken}`) + reply.clearCookie('refresh_token', { path: '/api/auth' }) + return reply.code(401).send({ error: 'Account inactive or not found' }) + } + + const payload: JwtPayload = { + ...cached, + role: rows[0].role, + hotelSlug: rows[0].hotel_slug ?? cached.hotelSlug, + } + // Update Redis with fresh payload + const ttl = await redis.ttl(`refresh:${refreshToken}`) + if (ttl > 0) await redis.set(`refresh:${refreshToken}`, JSON.stringify(payload), 'EX', ttl) + + const accessToken = fastify.jwt.sign(payload, { expiresIn: config.jwt.accessExpiry }) return { access_token: accessToken } }) diff --git a/src/pages/UsersPage.tsx b/src/pages/UsersPage.tsx index a962dc8..337dae9 100644 --- a/src/pages/UsersPage.tsx +++ b/src/pages/UsersPage.tsx @@ -1025,10 +1025,8 @@ export function UsersPage() { } } - const stats = { - total: users.length, - active: users.filter(u => u.isActive).length, - } + const totalUsers = users.length + const activeUsers = users.filter(u => u.isActive).length return (
@@ -1036,7 +1034,7 @@ export function UsersPage() {

Сотрудники

-

{stats.total} пользователей · {stats.active} активных

+

{totalUsers} пользователей · {activeUsers} активных

{tab === 'staff' && (