mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 13:21:46 +00:00
7d3b37a2a3
Adds a full permissions management feature allowing admins to control who can perform actions across the app (trip CRUD, files, places, budget, packing, reservations, collab, members, share links). - New server/src/services/permissions.ts: 16 configurable actions, in-memory cache, checkPermission() helper, backwards-compatible defaults matching upstream behaviour - GET/PUT /admin/permissions endpoints; permissions loaded into app-config response so clients have them on startup - checkPermission() applied to all mutating route handlers across 10 server route files; getTripOwnerId() helper eliminates repeated inline DB queries; trips.ts and files.ts now reuse canAccessTrip() result to avoid redundant DB round-trips - New client/src/store/permissionsStore.ts: Zustand store + useCanDo() hook; TripOwnerContext type accepts both Trip and DashboardTrip shapes without casting at call sites - New client/src/components/Admin/PermissionsPanel.tsx: categorised UI with per-action dropdowns, customised badge, save/reset - AdminPage, DashboardPage, FileManager, PlacesSidebar, TripMembersModal gated via useCanDo(); no prop drilling - 46 perm.* translation keys added to all 12 language files
53 lines
1.8 KiB
TypeScript
53 lines
1.8 KiB
TypeScript
import { create } from 'zustand'
|
|
import { useAuthStore } from './authStore'
|
|
|
|
export type PermissionLevel = 'admin' | 'trip_owner' | 'trip_member' | 'everybody'
|
|
|
|
/** Minimal trip shape used by permission checks — accepts both Trip and DashboardTrip */
|
|
type TripOwnerContext = { user_id?: unknown; owner_id?: unknown; is_owner?: unknown }
|
|
|
|
interface PermissionsState {
|
|
permissions: Record<string, PermissionLevel>
|
|
setPermissions: (perms: Record<string, PermissionLevel>) => void
|
|
}
|
|
|
|
export const usePermissionsStore = create<PermissionsState>((set) => ({
|
|
permissions: {},
|
|
setPermissions: (perms) => set({ permissions: perms }),
|
|
}))
|
|
|
|
/**
|
|
* Hook that returns a permission checker bound to the current user.
|
|
* Usage: const can = useCanDo(); can('trip_create') or can('file_upload', trip)
|
|
*/
|
|
export function useCanDo() {
|
|
const perms = usePermissionsStore((s: PermissionsState) => s.permissions)
|
|
const user = useAuthStore((s) => s.user)
|
|
|
|
return function can(
|
|
actionKey: string,
|
|
trip?: TripOwnerContext | null,
|
|
): boolean {
|
|
if (!user) return false
|
|
if (user.role === 'admin') return true
|
|
|
|
const level = perms[actionKey]
|
|
if (!level) return true // not configured = allow
|
|
|
|
// Support both Trip (owner_id) and DashboardTrip/server response (user_id)
|
|
const tripOwnerId = (trip?.user_id as number | undefined) ?? (trip?.owner_id as number | undefined) ?? null
|
|
const isOwnerFlag = trip?.is_owner === true || trip?.is_owner === 1
|
|
const isOwner = isOwnerFlag || (tripOwnerId !== null && tripOwnerId === user.id)
|
|
const isMember = !isOwner && trip != null
|
|
|
|
switch (level) {
|
|
case 'admin': return false
|
|
case 'trip_owner': return isOwner
|
|
case 'trip_member': return isOwner || isMember
|
|
case 'everybody': return true
|
|
default: return false
|
|
}
|
|
}
|
|
}
|
|
|