Permissions Module (Spatie)#
Overview#
Centralizes user permissions in a global Pinia store.
Auto-loads permissions when a user is authenticated.
Provides helpers for component-level checks.
Optional route-level enforcement via page meta and middleware.
Files#
Store: store/global/permission.ts
Global loader middleware: middleware/permissions.global.ts
Page guard middleware: middleware/require-permissions.ts
Backend Contract#
Endpoint: VITE_PERMISSIONS_ENDPOINT or /api/v1/permissions by default.
{ permissions: string[] }
Each item is a Spatie permission name (e.g., manage users, edit workflows).Store API#
// store/global/permission.ts
const permissionStore = usePermissionStore()
// State
permissionStore.permissions // string[]
permissionStore.isLoaded // boolean
permissionStore.isLoading // boolean
permissionStore.lastFetchedAt // Date | null
// Actions
await permissionStore.boot() // idempotent boot; fetches if user is authenticated
await permissionStore.fetchPermissions()
permissionStore.clear() // clears permissions (e.g., on logout)
// Helpers
permissionStore.hasPermission('edit workflows')
permissionStore.hasAny(['admin', 'owner'])
permissionStore.hasAll(['manage users', 'view users'])
The store watches useSanctumUser(): it clears on logout, and lazy-boots on login.
Global Loading#
middleware/permissions.global.ts runs on each navigation:If the user is authenticated, it calls permissionStore.boot() once per session.
This ensures components can rely on permissions being available.
Page-Level Enforcement (Optional)#
Use middleware/require-permissions.ts and page meta to guard routes.
// Inside a page .vue <script setup>
definePageMeta({
layout: 'dashboard',
middleware: ['sanctum:auth', 'require-permissions'],
permissions: {
// Pass if user has any of these
any: ['view dashboard', 'admin'],
// Or require all of these instead
// all: ['manage users', 'view users'],
// Optional redirect target when unauthorized (route name or absolute path)
redirectTo: 'chat-index',
},
})
If permissions meta is absent: no route-level checks are performed (page loads normally).
If present: permissions are loaded (if needed), evaluated, and the user is redirected when unauthorized.
Component-Level Checks#
// In any component
const permissionStore = usePermissionStore()
const canEdit = computed(() => permissionStore.hasPermission('edit workflows'))
You can gate actions, buttons, or UI sections using hasPermission, hasAny, or hasAll.Configuration#
Override endpoint via .env:
Troubleshooting#
"Cannot find name 'usePermissionStore'": ensure you imported it, e.g. import { usePermissionStore } from '~/store/global/permission'.
Permissions not loading: verify the user is authenticated and the backend endpoint returns the expected payload.
Route not guarding access: ensure the page includes both 'require-permissions' in middleware and a permissions meta config.
Modified at 2025-09-02 01:17:20