diff --git a/backend/routes/admin.js b/backend/routes/admin.js index cabf91b..afb86a1 100644 --- a/backend/routes/admin.js +++ b/backend/routes/admin.js @@ -12,8 +12,13 @@ router.post('/admin/set-admin-role', async (req, res) => { if (!requesterId) return res.status(401).json({ error: 'Authentication required' }); + // Fetch user to get uid for isAdmin check + const requester = await User.findByPk(requesterId, { attributes: ['uid'] }); + if (!requester) + return res.status(401).json({ error: 'Authentication required' }); + // Allow if requester is already admin OR if there are no roles yet (bootstrap) - const requesterIsAdmin = await isAdmin(requesterId); + const requesterIsAdmin = await isAdmin(requester.uid); const existingRolesCount = await Role.count(); if (!requesterIsAdmin && existingRolesCount > 0) { return res.status(403).json({ error: 'Forbidden' }); @@ -55,7 +60,12 @@ async function requireAdmin(req, res, next) { const requesterId = req.currentUser?.id || req.session?.userId; if (!requesterId) return res.status(401).json({ error: 'Authentication required' }); - const admin = await isAdmin(requesterId); + + // Fetch user to get uid for isAdmin check + const user = await User.findByPk(requesterId, { attributes: ['uid'] }); + if (!user) return res.status(401).json({ error: 'Authentication required' }); + + const admin = await isAdmin(user.uid); if (!admin) return res.status(403).json({ error: 'Forbidden' }); next(); } catch (err) { diff --git a/backend/routes/auth.js b/backend/routes/auth.js index 32cb2b3..fe750f6 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -15,16 +15,10 @@ router.get('/current_user', async (req, res) => { try { if (req.session && req.session.userId) { const user = await User.findByPk(req.session.userId, { - attributes: [ - 'uid', - 'email', - 'language', - 'appearance', - 'timezone', - ], + attributes: ['uid', 'email', 'language', 'appearance', 'timezone'], }); if (user) { - const admin = await isAdmin(user.id); + const admin = await isAdmin(user.uid); return res.json({ user: { uid: user.uid, @@ -76,7 +70,7 @@ router.post('/login', async (req, res) => { }); }); - const admin = await isAdmin(user.id); + const admin = await isAdmin(user.uid); res.json({ user: { uid: user.uid, diff --git a/backend/services/rolesService.js b/backend/services/rolesService.js index 804cd8d..d68a181 100644 --- a/backend/services/rolesService.js +++ b/backend/services/rolesService.js @@ -1,8 +1,17 @@ -const { Role } = require('../models'); +const { Role, User } = require('../models'); -async function isAdmin(userId) { - if (!userId) return false; - const role = await Role.findOne({ where: { user_id: userId } }); +async function isAdmin(userUid) { + if (!userUid) return false; + + // Find user by uid to get numeric id for role lookup + const user = await User.findOne({ + where: { uid: userUid }, + attributes: ['id'], + }); + + if (!user) return false; + + const role = await Role.findOne({ where: { user_id: user.id } }); return !!(role && role.is_admin); } diff --git a/frontend/App.tsx b/frontend/App.tsx index cc00474..8c4f034 100644 --- a/frontend/App.tsx +++ b/frontend/App.tsx @@ -249,7 +249,7 @@ const App: React.FC = () => { diff --git a/frontend/components/Admin/AdminUsersPage.tsx b/frontend/components/Admin/AdminUsersPage.tsx index cab0e82..675b67d 100644 --- a/frontend/components/Admin/AdminUsersPage.tsx +++ b/frontend/components/Admin/AdminUsersPage.tsx @@ -174,20 +174,22 @@ const AddUserModal: React.FC<{ {error && ( -
{error}
+
+ {error} +
)} -
+
{error && ( -
+
{error}
)} -
+
- + - - - - - + {loading && ( @@ -358,24 +361,27 @@ const AdminUsersPage: React.FC = () => { users.map((u) => ( - - - + -
+ + {t('admin.email', 'Email')} + {t('admin.created', 'Created')} + {t('admin.role', 'Role')}
{t( 'admin.loadingUsers', @@ -347,7 +350,7 @@ const AdminUsersPage: React.FC = () => {
{t('admin.noUsers', 'No users')}
+ toggleSelect(u.id)} + className="w-4 h-4 rounded border-gray-300 dark:border-gray-600 text-blue-600 focus:ring-blue-500 dark:focus:ring-blue-400 focus:ring-2 bg-white dark:bg-gray-700" /> {u.email} + + {u.email} + {new Date( u.created_at ).toLocaleString()} + {u.role === 'admin' ? t('admin.admin', 'admin') diff --git a/frontend/components/Navbar.tsx b/frontend/components/Navbar.tsx index 86efa51..baf6ec5 100644 --- a/frontend/components/Navbar.tsx +++ b/frontend/components/Navbar.tsx @@ -196,7 +196,7 @@ const Navbar: React.FC = ({ 'Profile Settings' )} - {(window as any).__CURRENT_USER__?.is_admin && ( + {currentUser?.is_admin === true && ( = ({ }, ]; - // Append admin link if current user is admin (read from global window state via current_user fetch in App) - try { - const raw = (window as any).__CURRENT_USER__; - if (raw?.is_admin) { - navLinks.push({ - path: '/admin/users', - title: t('sidebar.adminUsers', 'User Management'), - icon: , - } as any); - } - } catch { - // ignore missing window state - } - const isActive = (path: string, query?: string) => { // Handle special case for paths without query parameters if (path === '/inbox' || path === '/today' || path === '/upcoming') {