173 lines
4.6 KiB
TypeScript
173 lines
4.6 KiB
TypeScript
import { computed, ref } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import Swal from 'sweetalert2'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
import { getApiErrorMessage } from '@/core/utils/getApiErrorMessage'
|
|
import { resolvePostLoginRoute } from '@/modules/auth'
|
|
import { leaveImpersonation, takeImpersonation } from '../services/impersonate.service'
|
|
import type { UserListItem } from '../types/user.types'
|
|
|
|
const IMPERSONATE_PERMISSION = 'menyamar pengguna'
|
|
const DEVELOPER_ROLE = 'DEVELOPER'
|
|
|
|
function userCanImpersonate(): boolean {
|
|
const authStore = useAuthStore()
|
|
const roles = authStore.user?.roles ?? []
|
|
|
|
if (roles.some((role) => role.name === DEVELOPER_ROLE)) {
|
|
return true
|
|
}
|
|
|
|
const activeRole = authStore.activeRole
|
|
const roleWithPermissions = roles.find((role) => role.id === activeRole?.id)
|
|
|
|
return (
|
|
roleWithPermissions?.permissions?.some(
|
|
(permission) => permission.name === IMPERSONATE_PERMISSION,
|
|
) ?? false
|
|
)
|
|
}
|
|
|
|
function isImpersonateTargetVisible(target: UserListItem, currentUserId?: string): boolean {
|
|
if (!currentUserId || target.id === currentUserId) {
|
|
return false
|
|
}
|
|
|
|
return !target.roles?.some((role) => role.name === DEVELOPER_ROLE)
|
|
}
|
|
|
|
function targetCanBeImpersonated(target: UserListItem, currentUserId?: string): boolean {
|
|
if (!isImpersonateTargetVisible(target, currentUserId)) {
|
|
return false
|
|
}
|
|
|
|
return target.status === 'active'
|
|
}
|
|
|
|
export function useImpersonate() {
|
|
const authStore = useAuthStore()
|
|
const router = useRouter()
|
|
const loading = ref(false)
|
|
|
|
const impersonating = computed(() => authStore.isImpersonating)
|
|
const canImpersonate = computed(() => userCanImpersonate())
|
|
|
|
function showImpersonateButton(target: UserListItem): boolean {
|
|
return canImpersonate.value && isImpersonateTargetVisible(target, authStore.user?.id)
|
|
}
|
|
|
|
function canImpersonateUser(target: UserListItem): boolean {
|
|
return canImpersonate.value && targetCanBeImpersonated(target, authStore.user?.id)
|
|
}
|
|
|
|
function impersonateButtonTitle(target: UserListItem): string {
|
|
if (impersonating.value) {
|
|
return 'Anda sedang menyamar pengguna'
|
|
}
|
|
|
|
if (target.status !== 'active') {
|
|
return 'Hanya pengguna aktif boleh disamar'
|
|
}
|
|
|
|
return 'Menyamar sebagai pengguna'
|
|
}
|
|
|
|
async function refreshImpersonationStatus() {
|
|
await authStore.refreshImpersonationStatus()
|
|
}
|
|
|
|
async function impersonateUser(target: UserListItem) {
|
|
if (!canImpersonateUser(target) || loading.value || impersonating.value) {
|
|
return
|
|
}
|
|
|
|
const result = await Swal.fire({
|
|
title: 'Menyamar pengguna?',
|
|
text: `Anda akan log masuk sebagai "${target.name}".`,
|
|
icon: 'warning',
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Ya, menyamar',
|
|
cancelButtonText: 'Batal',
|
|
reverseButtons: true,
|
|
})
|
|
|
|
if (!result.isConfirmed) {
|
|
return
|
|
}
|
|
|
|
loading.value = true
|
|
|
|
try {
|
|
const response = await takeImpersonation(target.id)
|
|
authStore.applySession(response)
|
|
authStore.isImpersonating = true
|
|
|
|
await Swal.fire({
|
|
toast: true,
|
|
position: 'top-end',
|
|
icon: 'success',
|
|
title: response.message,
|
|
showConfirmButton: false,
|
|
showCloseButton: true,
|
|
timer: 500,
|
|
})
|
|
|
|
await router.push(resolvePostLoginRoute(response.redirect_path))
|
|
} catch (error) {
|
|
await Swal.fire({
|
|
icon: 'error',
|
|
title: 'Gagal menyamar',
|
|
text: getApiErrorMessage(error, 'Tidak dapat menyamar sebagai pengguna ini.'),
|
|
})
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
async function stopImpersonation() {
|
|
if (!impersonating.value || loading.value) {
|
|
return
|
|
}
|
|
|
|
loading.value = true
|
|
|
|
try {
|
|
const response = await leaveImpersonation()
|
|
authStore.applySession(response)
|
|
authStore.isImpersonating = false
|
|
|
|
await Swal.fire({
|
|
toast: true,
|
|
position: 'top-end',
|
|
icon: 'success',
|
|
title: response.message,
|
|
showConfirmButton: false,
|
|
showCloseButton: true,
|
|
timer: 500,
|
|
})
|
|
|
|
await router.push(resolvePostLoginRoute(response.redirect_path))
|
|
} catch (error) {
|
|
await Swal.fire({
|
|
icon: 'error',
|
|
title: 'Gagal tamatkan penyamaran',
|
|
text: getApiErrorMessage(error, 'Tidak dapat kembali ke akaun asal.'),
|
|
})
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
return {
|
|
canImpersonate,
|
|
showImpersonateButton,
|
|
canImpersonateUser,
|
|
impersonateButtonTitle,
|
|
impersonating,
|
|
loading,
|
|
refreshImpersonationStatus,
|
|
impersonateUser,
|
|
stopImpersonation,
|
|
}
|
|
}
|