refactor: remove redundant role field, keep only is_admin

- Remove role column from users schema (migration 0003)
- Update auth and admin services to use is_admin only
- Remove role from JWT token payloads
- Update admin CLI to use is_admin field
- Update frontend admin page to use isAdmin boolean
- Fix security: remove console.log dumping credentials in settings

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-01-21 11:41:41 +01:00
parent fc44fef91a
commit 8f8abfc651
11 changed files with 789 additions and 62 deletions

View File

@@ -122,7 +122,6 @@ export async function getUserStats() {
id: users.id,
email: users.email,
callsign: users.callsign,
role: users.role,
isAdmin: users.isAdmin,
qsoCount: sql`CAST(COUNT(${qsos.id}) AS INTEGER)`,
lotwConfirmed: sql`CAST(SUM(CASE WHEN ${qsos.lotwQslRstatus} = 'Y' THEN 1 ELSE 0 END) AS INTEGER)`,
@@ -250,19 +249,18 @@ export async function getImpersonationStatus(adminId, { limit = 10 } = {}) {
}
/**
* Update user role (admin operation)
* Update user admin status (admin operation)
* @param {number} adminId - Admin user ID making the change
* @param {number} targetUserId - User ID to update
* @param {string} newRole - New role ('user' or 'admin')
* @param {boolean} newIsAdmin - New admin flag
* @returns {Promise<void>}
* @throws {Error} If not admin or would remove last admin
*/
export async function changeUserRole(adminId, targetUserId, newRole, newIsAdmin) {
export async function changeUserRole(adminId, targetUserId, newIsAdmin) {
// Verify the requester is an admin
const requesterIsAdmin = await isAdmin(adminId);
if (!requesterIsAdmin) {
throw new Error('Only admins can change user roles');
throw new Error('Only admins can change user admin status');
}
// Get target user
@@ -283,11 +281,10 @@ export async function changeUserRole(adminId, targetUserId, newRole, newIsAdmin)
}
}
// Update role
// Update admin status
await db
.update(users)
.set({
role: newRole,
isAdmin: newIsAdmin ? 1 : 0,
updatedAt: new Date(),
})
@@ -295,8 +292,6 @@ export async function changeUserRole(adminId, targetUserId, newRole, newIsAdmin)
// Log action
await logAdminAction(adminId, 'role_change', targetUserId, {
oldRole: targetUser.role,
newRole: newRole,
oldIsAdmin: targetUser.isAdmin,
newIsAdmin: newIsAdmin,
});

View File

@@ -168,7 +168,6 @@ export async function getAdminUsers() {
id: users.id,
email: users.email,
callsign: users.callsign,
role: users.role,
isAdmin: users.isAdmin,
createdAt: users.createdAt,
})
@@ -179,17 +178,15 @@ export async function getAdminUsers() {
}
/**
* Update user role
* Update user admin status
* @param {number} userId - User ID
* @param {string} role - New role ('user' or 'admin')
* @param {boolean} isAdmin - Admin flag
* @returns {Promise<void>}
*/
export async function updateUserRole(userId, role, isAdmin) {
export async function updateUserRole(userId, isAdmin) {
await db
.update(users)
.set({
role,
isAdmin: isAdmin ? 1 : 0,
updatedAt: new Date(),
})
@@ -206,7 +203,6 @@ export async function getAllUsers() {
id: users.id,
email: users.email,
callsign: users.callsign,
role: users.role,
isAdmin: users.isAdmin,
createdAt: users.createdAt,
updatedAt: users.updatedAt,
@@ -228,7 +224,6 @@ export async function getUserByIdFull(userId) {
id: users.id,
email: users.email,
callsign: users.callsign,
role: users.role,
isAdmin: users.isAdmin,
lotwUsername: users.lotwUsername,
dclApiKey: users.dclApiKey,