perf(ux): request-cached permission checks (dedupe RPC fan-out)
New React cache()-wrapped hasPermission(uid,sid,perm): identical checks resolve once per server render, so the (app) layout's nav-permission resolution and a page's checks for the same perm (door.manage, forms.manage) no longer double-round-trip user_has_permission. Pure dedupe -- semantics unchanged, permission logic stays in SQL/RLS. Adopted in the layout + members page.
This commit is contained in:
@@ -2,6 +2,7 @@ import { createClient } from '@/lib/supabase/server'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { AppSidebar } from '@/components/app-sidebar'
|
||||
import { CommandPalette } from '@/components/command-palette'
|
||||
import { hasPermission } from '@/lib/permissions-cache'
|
||||
import { ConfirmProvider } from '@/components/ui/confirm'
|
||||
import { getRoleLabelMap } from '@/lib/role-labels'
|
||||
|
||||
@@ -69,11 +70,9 @@ export default async function AppLayout({ children }: { children: React.ReactNod
|
||||
const navPerms: Record<string, boolean> = {}
|
||||
if (!isAdminRole) {
|
||||
const results = await Promise.all(
|
||||
NAV_PERM_CODES.map(perm =>
|
||||
supabase.rpc('user_has_permission', { uid: user.id, sid: member.space_id, perm }),
|
||||
),
|
||||
NAV_PERM_CODES.map(perm => hasPermission(user.id, member.space_id, perm)),
|
||||
)
|
||||
NAV_PERM_CODES.forEach((perm, i) => { navPerms[perm] = !!results[i].data })
|
||||
NAV_PERM_CODES.forEach((perm, i) => { navPerms[perm] = results[i] })
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
import { PageHeader, PageTitle } from '@/components/ui/page-title'
|
||||
import { hasPermission } from '@/lib/permissions-cache'
|
||||
import { MembersClient } from './members-client'
|
||||
import { InvitesPanel } from '../customize/panels/invites-panel'
|
||||
|
||||
@@ -90,21 +91,13 @@ export default async function MembersPage() {
|
||||
// certifications.grant (the Instructor capability) is independent of
|
||||
// admin/board, so check it explicitly to decide whether to show the
|
||||
// per-member certifications panel.
|
||||
const { data: canGrantCerts } = await supabase.rpc('user_has_permission', {
|
||||
uid: user.id,
|
||||
sid: self.space_id,
|
||||
perm: 'certifications.grant',
|
||||
})
|
||||
const { data: canManageCards } = await supabase.rpc('user_has_permission', {
|
||||
uid: user.id,
|
||||
sid: self.space_id,
|
||||
perm: 'door.manage',
|
||||
})
|
||||
const { data: canViewForms } = await supabase.rpc('user_has_permission', {
|
||||
uid: user.id,
|
||||
sid: self.space_id,
|
||||
perm: 'forms.manage',
|
||||
})
|
||||
// Request-cached: door.manage / forms.manage here dedupe with the layout's
|
||||
// nav-permission resolution for the same render.
|
||||
const [canGrantCerts, canManageCards, canViewForms] = await Promise.all([
|
||||
hasPermission(user.id, self.space_id, 'certifications.grant'),
|
||||
hasPermission(user.id, self.space_id, 'door.manage'),
|
||||
hasPermission(user.id, self.space_id, 'forms.manage'),
|
||||
])
|
||||
|
||||
return (
|
||||
<MembersClient
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { cache } from 'react'
|
||||
import { createClient } from '@/lib/supabase/server'
|
||||
|
||||
// Request-scoped memoized permission check. The same (uid, sid, perm) is
|
||||
// resolved at most once per server render, so the layout and the page it
|
||||
// renders no longer double-round-trip the `user_has_permission` RPC for
|
||||
// overlapping permissions (e.g. door.manage / forms.manage). Semantics are
|
||||
// identical to calling the RPC directly -- this only dedupes, it does NOT
|
||||
// reimplement permission logic (that stays in the SQL function + RLS).
|
||||
export const hasPermission = cache(
|
||||
async (uid: string, sid: string, perm: string): Promise<boolean> => {
|
||||
const supabase = await createClient()
|
||||
const { data } = await supabase.rpc('user_has_permission', { uid, sid, perm })
|
||||
return !!data
|
||||
},
|
||||
)
|
||||
Reference in New Issue
Block a user