feat: apply permissions to menus

This commit is contained in:
Beng Eu committed 2022-05-27 18:26:57 +08:00
1 parent dad4d3d93d
commit b811e69158
6 files changed
+115 -93

No files matched your search

@@ -3,11 +3,11 @@ import Link from 'next/link'
import { observer } from 'mobx-react-lite'
import { useRouter } from 'next/router'
import { isUndefined } from 'lodash'
import { Button, IconHome, Dropdown, IconUser, IconSettings } from '@supabase/ui'
import { Button, Dropdown, IconHome, IconSettings, IconUser } from '@supabase/ui'
import { IS_PLATFORM } from 'lib/constants'
import { useStore } from 'hooks'
import { generateProductRoutes, generateOtherRoutes } from './NavigationBar.utils'
import { generateOtherRoutes, generateProductRoutes } from './NavigationBar.utils'
import NavigationIconButton from './NavigationIconButton'
interface Props {}
@@ -55,21 +55,25 @@ const NavigationBar: FC<Props> = ({}) => {
}}
/>
<div className="h-px w-full bg-scale-500"></div>
{productRoutes.map((route) => (
<NavigationIconButton
key={route.key}
route={route}
isActive={activeRoute === route.key}
/>
))}
{productRoutes
.filter(({ hidden }) => !hidden)
.map((route) => (
<NavigationIconButton
key={route.key}
route={route}
isActive={activeRoute === route.key}
/>
))}
<div className="h-px w-full bg-scale-500"></div>
{otherRoutes.map((route) => (
<NavigationIconButton
key={route.key}
route={route}
isActive={activeRoute === route.key}
/>
))}
{otherRoutes
.filter(({ hidden }) => !hidden)
.map((route) => (
<NavigationIconButton
key={route.key}
route={route}
isActive={activeRoute === route.key}
/>
))}
</ul>
<ul className="flex flex-col space-y-2">
<Dropdown
@@ -1,20 +1,21 @@
import {
IconUsers,
IconFileText,
IconArchive,
IconSettings,
IconDatabase,
IconBarChart,
IconList,
IconCode,
IconDatabase,
IconFileText,
IconList,
IconSettings,
IconUsers,
} from '@supabase/ui'
import SVG from 'react-inlinesvg'
import { IS_PLATFORM, PROJECT_STATUS } from 'lib/constants'
import { Route } from 'components/ui/ui.types'
import { useFlag } from 'hooks'
import { useFlag, usePermissions } from 'hooks'
import { ProjectBase } from 'types'
import { PermissionAction } from '@supabase/shared-types/out/constants'
export const generateProductRoutes = (ref: string, project?: ProjectBase): Route[] => {
const functionsUi = useFlag('functionsUi')
@@ -33,6 +34,7 @@ export const generateProductRoutes = (ref: string, project?: ProjectBase): Route
/>
),
link: isProjectBuilding ? buildingUrl : `/project/${ref}/editor`,
hidden: !usePermissions(PermissionAction.SQL_SELECT, 'postgres.public'),
},
{
key: 'auth',
@@ -40,16 +42,13 @@ export const generateProductRoutes = (ref: string, project?: ProjectBase): Route
icon: <IconUsers size={18} strokeWidth={2} />,
link: isProjectBuilding ? buildingUrl : `/project/${ref}/auth/users`,
},
...(IS_PLATFORM
? [
{
key: 'storage',
label: 'Storage',
icon: <IconArchive size={18} strokeWidth={2} />,
link: isProjectBuilding ? buildingUrl : `/project/${ref}/storage/buckets`,
},
]
: []),
{
key: 'storage',
label: 'Storage',
icon: <IconArchive size={18} strokeWidth={2} />,
link: isProjectBuilding ? buildingUrl : `/project/${ref}/storage/buckets`,
hidden: !IS_PLATFORM,
},
{
key: 'sql',
label: 'SQL Editor',
@@ -68,59 +67,63 @@ export const generateProductRoutes = (ref: string, project?: ProjectBase): Route
icon: <IconDatabase size={18} strokeWidth={2} />,
link: isProjectBuilding ? buildingUrl : `/project/${ref}/database/tables`,
},
...(IS_PLATFORM && functionsUi
? [
{
key: 'functions',
label: 'Functions',
icon: <IconCode size={18} strokeWidth={2} />,
link: isProjectBuilding ? buildingUrl : `/project/${ref}/functions`,
},
]
: []),
{
key: 'functions',
label: 'Functions',
icon: <IconCode size={18} strokeWidth={2} />,
link: isProjectBuilding ? buildingUrl : `/project/${ref}/functions`,
hidden: !IS_PLATFORM || !functionsUi,
},
]
}
export const generateOtherRoutes = (ref: string, project?: ProjectBase) => {
export const generateOtherRoutes = (ref: string, project?: ProjectBase): Route[] => {
const isProjectBuilding = project?.status !== PROJECT_STATUS.ACTIVE_HEALTHY
const buildingUrl = `/project/${ref}/building`
const canReadStats = usePermissions(
PermissionAction.SQL_SELECT,
'postgres.public.stats_daily_projects',
{
resource: { type: 'report' },
}
)
const canReadReport = usePermissions(
PermissionAction.SQL_SELECT,
'postgres.public.user_content',
{
resource: { type: 'report' },
}
)
return [
...(IS_PLATFORM
? [
{
key: 'logs-explorer',
label: 'Logs Explorer',
icon: <IconList size={18} strokeWidth={2} />,
link: isProjectBuilding ? buildingUrl : `/project/${ref}/logs-explorer`,
},
]
: []),
...(IS_PLATFORM
? [
{
key: 'reports',
label: 'Reports',
icon: <IconBarChart size={18} strokeWidth={2} />,
link: isProjectBuilding ? buildingUrl : `/project/${ref}/reports`,
},
]
: []),
{
key: 'logs-explorer',
label: 'Logs Explorer',
icon: <IconList size={18} strokeWidth={2} />,
link: isProjectBuilding ? buildingUrl : `/project/${ref}/logs-explorer`,
hidden: !IS_PLATFORM,
},
{
key: 'reports',
label: 'Reports',
icon: <IconBarChart size={18} strokeWidth={2} />,
link: isProjectBuilding ? buildingUrl : `/project/${ref}/reports`,
hidden: !IS_PLATFORM || !canReadReport || !canReadStats,
},
{
key: 'api',
label: 'API',
icon: <IconFileText size={18} strokeWidth={2} />,
link: isProjectBuilding ? buildingUrl : `/project/${ref}/api`,
},
...(IS_PLATFORM
? [
{
key: 'settings',
label: 'Settings',
icon: <IconSettings size={18} strokeWidth={2} />,
link: `/project/${ref}/settings/general`,
},
]
: []),
{
key: 'settings',
label: 'Settings',
icon: <IconSettings size={18} strokeWidth={2} />,
link: `/project/${ref}/settings/general`,
hidden:
!IS_PLATFORM || !usePermissions(PermissionAction.SQL_SELECT, 'postgres.public.projects'),
},
]
}
@@ -1,16 +1,26 @@
import { ProductMenuGroup } from 'components/ui/ProductMenu/ProductMenu.types'
import { PROJECT_STATUS } from 'lib/constants'
import { ProjectBase } from 'types'
import { usePermissions } from '../../../hooks'
import { PermissionAction } from '@supabase/shared-types/out/constants'
export const generateSettingsMenu = (ref: string, project?: ProjectBase): ProductMenuGroup[] => {
const isProjectBuilding = project?.status !== PROJECT_STATUS.ACTIVE_HEALTHY
const buildingUrl = `/project/${ref}/building`
const canReadInvoices = usePermissions(PermissionAction.BILLING_READ, 'stripe.invoices')
const canReadSubscriptions = usePermissions(PermissionAction.BILLING_READ, 'stripe.subscriptions')
return [
{
title: 'Project settings',
items: [
{ name: 'General', key: 'general', url: `/project/${ref}/settings/general`, items: [] },
{
name: 'General',
key: 'general',
url: `/project/${ref}/settings/general`,
items: [],
},
{
name: 'Database',
key: 'database',
@@ -29,18 +39,19 @@ export const generateSettingsMenu = (ref: string, project?: ProjectBase): Produc
url: isProjectBuilding ? buildingUrl : `/project/${ref}/auth/settings`,
items: [],
},
// TODO(thebengeu): Uncomment once all tenants migrated to multitenant storage API.
// {
// name: 'Storage',
// key: 'storage',
// url: isProjectBuilding ? buildingUrl : `/project/${ref}/storage/settings`,
// items: [],
// },
{
name: 'Storage',
key: 'storage',
url: isProjectBuilding ? buildingUrl : `/project/${ref}/storage/settings`,
items: [],
hidden: !usePermissions(PermissionAction.STORAGE_ADMIN_READ, '*'),
},
{
name: 'Billing & Usage',
key: 'billing',
url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/billing`,
items: [],
hidden: canReadInvoices && canReadSubscriptions,
},
],
},
@@ -1,7 +1,7 @@
import { FC } from 'react'
import { Badge, Menu } from '@supabase/ui'
import ProductMenuItem from './ProductMenuItem'
import { ProductMenuGroupItem, ProductMenuGroup } from './ProductMenu.types'
import { ProductMenuGroup, ProductMenuGroupItem } from './ProductMenu.types'
interface Props {
page?: string
@@ -26,17 +26,19 @@ const ProductMenu: FC<Props> = ({ page, menu }) => {
}
/>
<div>
{group.items.map((item: ProductMenuGroupItem) => (
<ProductMenuItem
key={item.key}
url={item.url}
name={item.name}
icon={item.icon}
isActive={page === item.key}
isExternal={item.isExternal}
target={item.isExternal ? '_blank' : '_self'}
/>
))}
{group.items
.filter(({ hidden }) => !hidden)
.map((item: ProductMenuGroupItem) => (
<ProductMenuItem
key={item.key}
url={item.url}
name={item.name}
icon={item.icon}
isActive={page === item.key}
isExternal={item.isExternal}
target={item.isExternal ? '_blank' : '_self'}
/>
))}
</div>
</div>
</div>
@@ -13,4 +13,5 @@ export interface ProductMenuGroupItem {
items: any[]
icon?: ReactNode
isExternal?: boolean
hidden?: boolean
}
+1
View File
@@ -5,4 +5,5 @@ export interface Route {
label: string
icon: ReactNode
link: string
hidden?: boolean
}