diff --git a/backend/src/middleware/auth.js b/backend/src/middleware/auth.js
index 2b75427..4f22243 100644
--- a/backend/src/middleware/auth.js
+++ b/backend/src/middleware/auth.js
@@ -15,10 +15,18 @@ module.exports = (pool, query) => {
try {
// Verify API key
- const result = await query(
- 'SELECT id, email, first_name, last_name, is_admin FROM users WHERE api_key = $1',
- [apiKey]
- );
+
+ const result = await query(`
+ SELECT
+ u.*,
+ CASE WHEN s.user_id IS NOT NULL THEN TRUE ELSE FALSE END AS is_super_admin
+ FROM
+ users u
+ LEFT JOIN
+ superadmins s ON u.id = s.user_id
+ WHERE
+ u.api_key = $1
+ `, [apiKey]);
if (result.rows.length === 0) {
return res.status(401).json({
diff --git a/backend/src/routes/userAdmin.js b/backend/src/routes/userAdmin.js
index 3735d5b..c6e211f 100644
--- a/backend/src/routes/userAdmin.js
+++ b/backend/src/routes/userAdmin.js
@@ -29,17 +29,22 @@ module.exports = (pool, query, authMiddleware) => {
const result = await query(`
SELECT
- id,
- email,
- first_name,
- last_name,
- is_admin,
- is_disabled,
- internal_notes,
- created_at,
- last_login
- FROM users
- ORDER BY last_login DESC NULLS LAST
+ u.id,
+ u.email,
+ u.first_name,
+ u.last_name,
+ u.is_admin,
+ u.is_disabled,
+ u.internal_notes,
+ u.created_at,
+ u.last_login,
+ CASE WHEN s.user_id IS NOT NULL THEN TRUE ELSE FALSE END AS is_super_admin
+ FROM
+ users u
+ LEFT JOIN
+ superadmins s ON u.id = s.user_id
+ ORDER BY
+ u.last_login DESC NULLS LAST
`);
res.json(result.rows);
@@ -63,17 +68,22 @@ module.exports = (pool, query, authMiddleware) => {
const result = await query(`
SELECT
- id,
- email,
- first_name,
- last_name,
- is_admin,
- is_disabled,
- internal_notes,
- created_at,
- last_login
- FROM users
- WHERE id = $1
+ u.id,
+ u.email,
+ u.first_name,
+ u.last_name,
+ u.is_admin,
+ u.is_disabled,
+ u.internal_notes,
+ u.created_at,
+ u.last_login,
+ CASE WHEN s.user_id IS NOT NULL THEN TRUE ELSE FALSE END AS is_super_admin
+ FROM
+ users u
+ LEFT JOIN
+ superadmins s ON u.id = s.user_id
+ WHERE
+ u.id = $1
`, [id]);
if (result.rows.length === 0) {
@@ -93,18 +103,35 @@ module.exports = (pool, query, authMiddleware) => {
router.patch('/:id', async (req, res, next) => {
try {
const { id } = req.params;
- const { is_disabled, internal_notes, is_admin} = req.body;
-
+ const { is_disabled, internal_notes, is_admin } = req.body;
+ // Verify admin status from middleware
if (!req.user.is_admin) {
return res.status(403).json({
error: true,
message: 'Admin access required'
});
+ }
+
+ if (is_admin !== undefined && typeof is_admin !== 'boolean') {
+ return res.status(400).json({
+ error: true,
+ message: 'is_admin must be a boolean'
+ });
}
- // Check if user exists
- const userCheck = await query('SELECT * FROM users WHERE id = $1', [id]);
+ // 4. Check if user exists and get their details (including superadmin status)
+ const userCheck = await query(`
+ SELECT
+ u.*,
+ CASE WHEN s.user_id IS NOT NULL THEN TRUE ELSE FALSE END AS is_super_admin
+ FROM
+ users u
+ LEFT JOIN
+ superadmins s ON u.id = s.user_id
+ WHERE
+ u.id = $1
+ `, [id]);
if (userCheck.rows.length === 0) {
return res.status(404).json({
@@ -113,19 +140,39 @@ module.exports = (pool, query, authMiddleware) => {
});
}
- // Update only allowed fields
+ const targetUser = userCheck.rows[0];
+
+ // - If target user is a superadmin, only allow self-editing
+ if (targetUser.is_super_admin && req.user.id !== targetUser.id) {
+ return res.status(403).json({
+ error: true,
+ message: 'Superadmin accounts can only be edited by themselves'
+ });
+ }
+
const result = await query(`
UPDATE users
SET
is_disabled = $1,
internal_notes = $2,
- is_admin = $3
+ is_admin = CASE
+ WHEN EXISTS (SELECT 1 FROM superadmins WHERE user_id = $4) THEN TRUE
+ ELSE $3
+ END
WHERE id = $4
- RETURNING id, email, first_name, last_name, is_admin, is_disabled, internal_notes
+ RETURNING
+ id,
+ email,
+ first_name,
+ last_name,
+ is_admin,
+ is_disabled,
+ internal_notes,
+ (SELECT CASE WHEN COUNT(*) > 0 THEN TRUE ELSE FALSE END FROM superadmins WHERE user_id = $4) AS is_super_admin
`, [
- is_disabled !== undefined ? is_disabled : userCheck.rows[0].is_disabled,
- internal_notes !== undefined ? internal_notes : userCheck.rows[0].internal_notes,
- is_admin !== undefined ? is_admin : userCheck.rows[0].is_admin,
+ is_disabled !== undefined ? is_disabled : targetUser.is_disabled,
+ internal_notes !== undefined ? internal_notes : targetUser.internal_notes,
+ is_admin !== undefined ? is_admin : targetUser.is_admin,
id
]);
@@ -134,10 +181,10 @@ module.exports = (pool, query, authMiddleware) => {
user: result.rows[0]
});
} catch (error) {
+ // Pass to error handler middleware
next(error);
}
});
-
// Send email to user
router.post('/send-email', async (req, res, next) => {
try {
diff --git a/frontend/src/pages/Admin/CustomersPage.jsx b/frontend/src/pages/Admin/CustomersPage.jsx
index 88f4a38..67b666a 100644
--- a/frontend/src/pages/Admin/CustomersPage.jsx
+++ b/frontend/src/pages/Admin/CustomersPage.jsx
@@ -227,6 +227,14 @@ const AdminCustomersPage = () => {
+ )}
+ {user.is_super_admin && (
+