feat: block inactive users from login and fix activity log on dev operators

- Block inactive users on email/password login (403)
- Block inactive users on Google OAuth (redirect to account_disabled)
- Auto-logout inactive users on session check (deleteMany sessions)
- Delete sessions when user is deactivated via PATCH /api/operators/:id
- Add account_disabled error message on login page
- Show inactive indicator on users table with reactivate button
- Add createSystemLog calls to /api/admin/users role and activate endpoints

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-29 11:58:31 +08:00
parent 73aa9729b8
commit 06794524fd
3 changed files with 91 additions and 17 deletions

View File

@@ -203,6 +203,10 @@ export function createApp() {
})
}
if (!user.active) {
return new Response(null, { status: 302, headers: { Location: '/login?error=account_disabled' } })
}
if (env.SUPER_ADMIN_EMAILS.includes(user.email) && user.role !== 'DEVELOPER') {
user = await prisma.user.update({ where: { id: user.id }, data: { role: 'DEVELOPER' } })
}
@@ -233,6 +237,10 @@ export function createApp() {
set.status = 401
return { error: 'Email atau password salah' }
}
if (!user.active) {
set.status = 403
return { error: 'Akun Anda telah dinonaktifkan. Hubungi admin untuk informasi lebih lanjut.' }
}
// Auto-promote super admin from env
if (env.SUPER_ADMIN_EMAILS.includes(user.email) && user.role !== 'DEVELOPER') {
user = await prisma.user.update({ where: { id: user.id }, data: { role: 'DEVELOPER' } })
@@ -281,13 +289,18 @@ export function createApp() {
if (!token) { set.status = 401; return { user: null } }
const session = await prisma.session.findUnique({
where: { token },
include: { user: { select: { id: true, name: true, email: true, role: true, image: true } } },
include: { user: { select: { id: true, name: true, email: true, role: true, image: true, active: true } } },
})
if (!session || session.expiresAt < new Date()) {
if (session) await prisma.session.delete({ where: { id: session.id } })
set.status = 401
return { user: null }
}
if (!session.user.active) {
await prisma.session.deleteMany({ where: { userId: session.user.id } })
set.status = 401
return { user: null }
}
return { user: session.user }
}, {
detail: {
@@ -641,6 +654,10 @@ export function createApp() {
},
})
if (body.active === false) {
await prisma.session.deleteMany({ where: { userId: id } })
}
if (userId) {
await createSystemLog(userId, 'UPDATE', `Updated user: ${user.name} (${user.email})`)
}
@@ -1054,6 +1071,7 @@ export function createApp() {
select: { id: true, name: true, email: true, role: true, active: true, createdAt: true },
})
await appLog('info', `Role changed: ${user.email} ${target?.role}${role}`)
await createSystemLog(auth.userId, 'UPDATE', `Role changed: ${user.name} (${user.email}) ${target?.role}${role}`)
return { user }
})
@@ -1069,6 +1087,7 @@ export function createApp() {
})
if (!active) await prisma.session.deleteMany({ where: { userId: params.id } })
await appLog('info', `User ${active ? 'activated' : 'deactivated'}: ${user.email}`)
await createSystemLog(auth.userId, active ? 'UPDATE' : 'DELETE', `User ${active ? 'activated' : 'deactivated'}: ${user.name} (${user.email})`)
return { user }
})