| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- import { LOCAL_STORAGE_KEYS, useIsLoggedIn, useIsMFAEnabled, useParams } from 'common'
- import { useRouter } from 'next/router'
- import { PropsWithChildren, useEffect } from 'react'
- import { toast } from 'sonner'
- import { useOrganizationsQuery } from '@/data/organizations/organizations-query'
- import { useProjectDetailQuery } from '@/data/projects/project-detail-query'
- import { useDashboardHistory } from '@/hooks/misc/useDashboardHistory'
- import useLatest from '@/hooks/misc/useLatest'
- import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
- import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
- import { IS_PLATFORM } from '@/lib/constants'
- // Ideally these could all be within a _middleware when we use Next 12
- export const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => {
- const router = useRouter()
- const { ref, slug, id } = useParams()
- const { data: organization } = useSelectedOrganizationQuery()
- const isLoggedIn = useIsLoggedIn()
- const isUserMFAEnabled = useIsMFAEnabled()
- const { setLastVisitedSnippet, setLastVisitedTable } = useDashboardHistory()
- const [lastVisitedOrganization, setLastVisitedOrganization] = useLocalStorageQuery(
- LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
- ''
- )
- const DEFAULT_HOME = IS_PLATFORM
- ? !!lastVisitedOrganization
- ? `/org/${lastVisitedOrganization}`
- : '/organizations'
- : '/project/default'
- /**
- * Array of urls/routes that should be ignored
- */
- const excemptUrls: string[] = [
- // project creation route, allows the page to self determine it's own route, it will redirect to the first org
- // or prompt the user to create an organaization
- // this is used by database.dev, usually as /new/new-project
- '/new/[slug]',
- '/join',
- ]
- /**
- * Map through all the urls that are excluded
- * from route validation check
- *
- * @returns a boolean
- */
- function isExceptUrl() {
- return excemptUrls.includes(router?.pathname)
- }
- const { isError: isErrorProject, error: projectError } = useProjectDetailQuery({ ref })
- const { data: organizations, isSuccess: orgsInitialized } = useOrganizationsQuery({
- enabled: isLoggedIn,
- })
- const organizationsRef = useLatest(organizations)
- useEffect(() => {
- // check if current route is excempted from route validation check
- if (isExceptUrl() || !isLoggedIn) return
- if (orgsInitialized && slug) {
- // Check validity of organization that user is trying to access
- const organizations = organizationsRef.current ?? []
- const isValidOrg = organizations.some((org) => org.slug === slug)
- if (!isValidOrg) {
- toast.error('You do not have access to this organization')
- router.push(`${DEFAULT_HOME}?error=org_not_found&org=${slug}`)
- return
- }
- }
- }, [orgsInitialized])
- useEffect(() => {
- // check if current route is excempted from route validation check
- if (isExceptUrl() || !isLoggedIn) return
- // A successful request to project details will validate access to both project and branches
- if (!!ref && isErrorProject) {
- // 404 means the project no longer exists (e.g. was deleted), not an access error
- if (projectError?.code !== 404) {
- toast.error('You do not have access to this project')
- }
- router.push(DEFAULT_HOME)
- return
- }
- }, [isErrorProject])
- useEffect(() => {
- if (ref !== undefined && id !== undefined) {
- if (router.pathname.endsWith('/sql/[id]') && id !== 'new') {
- setLastVisitedSnippet(id)
- } else if (router.pathname.endsWith('/editor/[id]')) {
- setLastVisitedTable(id)
- }
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [ref, id])
- useEffect(() => {
- if (organization) {
- setLastVisitedOrganization(organization.slug)
- if (
- organization.organization_requires_mfa &&
- !isUserMFAEnabled &&
- router.pathname !== '/org/[slug]'
- ) {
- router.push(`/org/${organization.slug}`)
- }
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [organization])
- return <>{children}</>
- }
|