feat(permissions) : add role-based UI guards and readonly mode for viewers
- Add usePermissions composable (isAdmin, canEdit, canView) - Password-protected profile login with modal on profiles page - Disable all form fields for ROLE_VIEWER across edit/create pages - Show navigation buttons (Modifier/Consulter) for all roles, hide delete for viewers - Add readonly prop to ModelTypeForm for category pages - Disable modal fields (sites, constructeurs) for viewers - Guard /admin routes in middleware Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,9 @@
|
||||
import { useState, useRequestHeaders, useRuntimeConfig } from '#imports'
|
||||
import { useState, useRuntimeConfig } from '#imports'
|
||||
import type { Profile } from './useProfiles'
|
||||
|
||||
const buildUrl = (path: string): string => {
|
||||
const config = useRuntimeConfig()
|
||||
const baseUrl = import.meta.server
|
||||
? ((config.apiBaseUrl as string) || (config.public.apiBaseUrl as string) || '')
|
||||
: ((config.public.apiBaseUrl as string) || '')
|
||||
const base = baseUrl.replace(/\/$/, '')
|
||||
const base = ((config.public.apiBaseUrl as string) || '').replace(/\/$/, '')
|
||||
return `${base}${path}`
|
||||
}
|
||||
|
||||
@@ -15,19 +12,12 @@ export function useProfileSession() {
|
||||
const sessionLoaded = useState<boolean>('profileSession:loaded', () => false)
|
||||
const loading = useState<boolean>('profileSession:loading', () => false)
|
||||
|
||||
const getSessionHeaders = (): Record<string, string> | undefined => {
|
||||
if (!import.meta.server) { return undefined }
|
||||
const headers = useRequestHeaders(['cookie'])
|
||||
return headers?.cookie ? { cookie: headers.cookie } : undefined
|
||||
}
|
||||
|
||||
const fetchCurrentProfile = async (): Promise<Profile | null> => {
|
||||
loading.value = true
|
||||
try {
|
||||
activeProfile.value = await $fetch<Profile>(buildUrl('/session/profile'), {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
headers: getSessionHeaders(),
|
||||
})
|
||||
} catch (error) {
|
||||
const err = error as { status?: number }
|
||||
@@ -51,12 +41,15 @@ export function useProfileSession() {
|
||||
return Promise.resolve(activeProfile.value)
|
||||
}
|
||||
|
||||
const activateProfile = async (profileId: string): Promise<void> => {
|
||||
const activateProfile = async (profileId: string, password?: string): Promise<void> => {
|
||||
const body: Record<string, string> = { profileId }
|
||||
if (password) {
|
||||
body.password = password
|
||||
}
|
||||
await $fetch(buildUrl('/session/profile'), {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
body: { profileId },
|
||||
headers: getSessionHeaders(),
|
||||
body,
|
||||
})
|
||||
await fetchCurrentProfile()
|
||||
}
|
||||
@@ -66,7 +59,6 @@ export function useProfileSession() {
|
||||
await $fetch(buildUrl('/session/profile'), {
|
||||
method: 'DELETE',
|
||||
credentials: 'include',
|
||||
headers: getSessionHeaders(),
|
||||
})
|
||||
} finally {
|
||||
activeProfile.value = null
|
||||
|
||||
Reference in New Issue
Block a user