diff --git a/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx index 0cf04315a69..e94da27828e 100644 --- a/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx @@ -50,7 +50,11 @@ const NavigationMenuHome = () => { > {link?.icon && } {link.label} - {link.community && Community} + {link.community && ( + + Community + + )} diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 0835f6b7a86..5060991b108 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -170,6 +170,8 @@ services: PGRST_DB_ANON_ROLE: anon PGRST_JWT_SECRET: ${JWT_SECRET} PGRST_DB_USE_LEGACY_GUCS: "false" + PGRST_APP_SETTINGS_JWT_SECRET: ${JWT_SECRET} + PGRST_APP_SETTINGS_JWT_EXP: ${JWT_EXPIRY} realtime: container_name: realtime-dev.supabase-realtime diff --git a/packages/ui/src/lib/theme/defaultTheme.ts b/packages/ui/src/lib/theme/defaultTheme.ts index 52376c4be5c..832713fae83 100644 --- a/packages/ui/src/lib/theme/defaultTheme.ts +++ b/packages/ui/src/lib/theme/defaultTheme.ts @@ -216,8 +216,7 @@ export default { }, dot: '-ml-0.5 mr-1.5 h-2 w-2 rounded-full', color: { - brand: 'bg-brand bg-opacity-100 text-background border border-brand-400', - alt: 'bg-brand-500 text-brand-600 border border-brand-400', + brand: 'bg-brand-500 text-brand-600 border border-brand-400', scale: 'bg-scale-200 text-scale-1100 border border-scale-700', tomato: `bg-tomato-200 text-tomato-1100 border border-tomato-700`, red: `bg-red-200 text-red-1100 border border-red-700`, diff --git a/studio/components/interfaces/App/RouteValidationWrapper.tsx b/studio/components/interfaces/App/RouteValidationWrapper.tsx index 3b82a9b1679..9689db2fdee 100644 --- a/studio/components/interfaces/App/RouteValidationWrapper.tsx +++ b/studio/components/interfaces/App/RouteValidationWrapper.tsx @@ -2,11 +2,10 @@ import { observer } from 'mobx-react-lite' import { useRouter } from 'next/router' import { PropsWithChildren, useEffect } from 'react' -import { useParams } from 'common' +import { useIsLoggedIn, useParams } from 'common' import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { useProjectsQuery } from 'data/projects/projects-query' -import { useFlag, useStore } from 'hooks' -import useLatest from 'hooks/misc/useLatest' +import { useFlag, useStore, useLatest } from 'hooks' import { DEFAULT_HOME, IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants' // Ideally these could all be within a _middleware when we use Next 12 @@ -16,6 +15,8 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { const { ref, slug } = useParams() const navLayoutV2 = useFlag('navigationLayoutV2') + const isLoggedIn = useIsLoggedIn() + /** * Array of urls/routes that should be ignored */ @@ -37,12 +38,14 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { return excemptUrls.includes(router?.pathname) } - const { data: organizations, isSuccess: orgsInitialized } = useOrganizationsQuery() + 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()) return + if (isExceptUrl() || !isLoggedIn) return if (orgsInitialized && slug) { // Check validity of organization that user is trying to access @@ -57,12 +60,14 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => { } }, [orgsInitialized]) - const { data: projects, isSuccess: projectsInitialized } = useProjectsQuery() + const { data: projects, isSuccess: projectsInitialized } = useProjectsQuery({ + enabled: isLoggedIn, + }) const projectsRef = useLatest(projects) useEffect(() => { // check if current route is excempted from route validation check - if (isExceptUrl()) return + if (isExceptUrl() || !isLoggedIn) return if (projectsInitialized && ref) { // Check validity of project that the user is trying to access diff --git a/studio/components/interfaces/Support/SupportForm.tsx b/studio/components/interfaces/Support/SupportForm.tsx index bf2bddef769..614b49cd147 100644 --- a/studio/components/interfaces/Support/SupportForm.tsx +++ b/studio/components/interfaces/Support/SupportForm.tsx @@ -35,6 +35,7 @@ import { Project } from 'types' import DisabledStateForFreeTier from './DisabledStateForFreeTier' import { CATEGORY_OPTIONS, SERVICE_OPTIONS, SEVERITY_OPTIONS } from './Support.constants' import { formatMessage, uploadAttachments } from './SupportForm.utils' +import { useRouter } from 'next/router' const MAX_ATTACHMENTS = 5 const INCLUDE_DISCUSSIONS = ['Problem', 'Database_unresponsive'] @@ -45,6 +46,7 @@ export interface SupportFormProps { const SupportForm = ({ setSentCategory }: SupportFormProps) => { const { ui } = useStore() + const { isReady } = useRouter() const { ref, subject, category, message } = useParams() const uploadButtonRef = useRef() @@ -270,6 +272,21 @@ const SupportForm = ({ setSentCategory }: SupportFormProps) => { } }, [isSuccessProjects, isSuccessOrganizations]) + // Populate fields when router is ready, required when navigating to + // support form on a refresh browser session + // eslint-disable-next-line react-hooks/rules-of-hooks + useEffect(() => { + if (isReady) { + const updatedValues = { + projectRef: ref, + subject, + category: selectedCategoryFromUrl?.value, + message, + } + resetForm({ values: updatedValues, initialValues: updatedValues }) + } + }, [isReady]) + return (
diff --git a/studio/components/ui/PageTelemetry.tsx b/studio/components/ui/PageTelemetry.tsx index ddba717617f..24fd0e4bea5 100644 --- a/studio/components/ui/PageTelemetry.tsx +++ b/studio/components/ui/PageTelemetry.tsx @@ -1,4 +1,4 @@ -import { useParams, useTelemetryProps } from 'common' +import { useIsLoggedIn, useParams, useTelemetryProps } from 'common' import { observer } from 'mobx-react-lite' import { useRouter } from 'next/router' import { PropsWithChildren, useEffect } from 'react' @@ -13,6 +13,8 @@ const PageTelemetry = ({ children }: PropsWithChildren<{}>) => { const telemetryProps = useTelemetryProps() const selectedOrganization = useSelectedOrganization() + const isLoggedIn = useIsLoggedIn() + useEffect(() => { function handleRouteChange(url: string) { handlePageTelemetry(url) @@ -59,14 +61,16 @@ const PageTelemetry = ({ children }: PropsWithChildren<{}>) => { }, }) - post(`${API_URL}/telemetry/pageview`, { - ...(ref && { projectRef: ref }), - ...(selectedOrganization && { orgSlug: selectedOrganization.slug }), - referrer: referrer, - title: document.title, - path: router.route, - location: router.asPath, - }) + if (isLoggedIn) { + post(`${API_URL}/telemetry/pageview`, { + ...(ref && { projectRef: ref }), + ...(selectedOrganization && { orgSlug: selectedOrganization.slug }), + referrer: referrer, + title: document.title, + path: router.route, + location: router.asPath, + }) + } } } diff --git a/studio/data/permissions/permissions-query.ts b/studio/data/permissions/permissions-query.ts index b96a57c6698..d98a9a2a15b 100644 --- a/studio/data/permissions/permissions-query.ts +++ b/studio/data/permissions/permissions-query.ts @@ -1,7 +1,8 @@ import { useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query' +import { useCallback } from 'react' + import { get } from 'lib/common/fetch' import { API_URL, IS_PLATFORM } from 'lib/constants' -import { useCallback } from 'react' import { Permission, ResponseError } from 'types' import { permissionKeys } from './keys' diff --git a/studio/data/profile/profile-create-mutation.ts b/studio/data/profile/profile-create-mutation.ts index a6a3f358a4c..37ff0d23424 100644 --- a/studio/data/profile/profile-create-mutation.ts +++ b/studio/data/profile/profile-create-mutation.ts @@ -1,6 +1,8 @@ import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' import { toast } from 'react-hot-toast' +import { organizationKeys } from 'data/organizations/keys' +import { permissionKeys } from 'data/permissions/keys' import { post } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { ResponseError } from 'types' @@ -29,7 +31,11 @@ export const useProfileCreateMutation = ({ return useMutation(() => createProfile(), { async onSuccess(data, variables, context) { - await queryClient.invalidateQueries(profileKeys.profile()) + await Promise.all([ + queryClient.invalidateQueries(profileKeys.profile()), + queryClient.invalidateQueries(organizationKeys.list()), + queryClient.invalidateQueries(permissionKeys.list()), + ]) await onSuccess?.(data, variables, context) }, async onError(data, variables, context) { diff --git a/studio/data/profile/profile-query.ts b/studio/data/profile/profile-query.ts index a11e5050e16..53ab747ba7f 100644 --- a/studio/data/profile/profile-query.ts +++ b/studio/data/profile/profile-query.ts @@ -1,10 +1,10 @@ -import { useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query' +import { useQuery, UseQueryOptions } from '@tanstack/react-query' + import { get } from 'lib/common/fetch' import { API_URL } from 'lib/constants' -import { useCallback } from 'react' +import { ResponseError } from 'types' import { profileKeys } from './keys' import { Profile } from './types' -import { ResponseError } from 'types' export type ProfileResponse = Profile @@ -30,14 +30,7 @@ export const useProfileQuery = ({ { staleTime: 1000 * 60 * 30, // default good for 30 mins ...options, + enabled, } ) } - -export const useProfilePrefetch = () => { - const client = useQueryClient() - - return useCallback(() => { - client.prefetchQuery(profileKeys.profile(), ({ signal }) => getProfile(signal)) - }, []) -} diff --git a/studio/hooks/index.ts b/studio/hooks/index.ts index c8c4d6b4db6..1f606782393 100644 --- a/studio/hooks/index.ts +++ b/studio/hooks/index.ts @@ -6,4 +6,5 @@ export * from './misc/useSelectedOrganization' export * from './misc/useSelectedProject' export * from './misc/useStore' export * from './misc/withAuth' +export * from './misc/useLatest' export * from './ui' diff --git a/studio/hooks/misc/useCheckPermissions.ts b/studio/hooks/misc/useCheckPermissions.ts index f1228eda934..963f739a630 100644 --- a/studio/hooks/misc/useCheckPermissions.ts +++ b/studio/hooks/misc/useCheckPermissions.ts @@ -1,3 +1,4 @@ +import { useIsLoggedIn } from 'common' import jsonLogic from 'json-logic-js' import { usePermissionsQuery } from 'data/permissions/permissions-query' @@ -67,11 +68,15 @@ export function useCheckPermissions( organizationId?: number, permissions?: Permission[] ) { + const isLoggedIn = useIsLoggedIn() + const { permissions: allPermissions, organizationId: orgId } = useGetPermissions( permissions, - organizationId + organizationId, + isLoggedIn ) + if (!isLoggedIn) return false if (!IS_PLATFORM) return true return doPermissionsCheck(allPermissions, action, resource, data, orgId) diff --git a/studio/hooks/misc/useLatest.ts b/studio/hooks/misc/useLatest.ts index 68577e65399..dd29313d4e8 100644 --- a/studio/hooks/misc/useLatest.ts +++ b/studio/hooks/misc/useLatest.ts @@ -1,6 +1,6 @@ import { useRef } from 'react' -const useLatest = (value: T): { readonly current: T } => { +export const useLatest = (value: T): { readonly current: T } => { const ref = useRef(value) ref.current = value return ref diff --git a/studio/hooks/misc/useSelectedOrganization.ts b/studio/hooks/misc/useSelectedOrganization.ts index bc6be1b3710..ef7a1d6599c 100644 --- a/studio/hooks/misc/useSelectedOrganization.ts +++ b/studio/hooks/misc/useSelectedOrganization.ts @@ -1,13 +1,15 @@ -import { useParams } from 'common' +import { useIsLoggedIn, useParams } from 'common' import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { useMemo } from 'react' -import { useSelectedProject } from './useSelectedProject' import { LOCAL_STORAGE_KEYS } from 'lib/constants' +import { useSelectedProject } from './useSelectedProject' export function useSelectedOrganization({ enabled = true } = {}) { + const isLoggedIn = useIsLoggedIn() + const { slug } = useParams() - const { data } = useOrganizationsQuery({ enabled }) + const { data } = useOrganizationsQuery({ enabled: isLoggedIn && enabled }) const selectedProject = useSelectedProject() const localStorageSlug = useMemo(() => { diff --git a/studio/lib/auth.tsx b/studio/lib/auth.tsx index a0e3e21ed7a..12fd3097c06 100644 --- a/studio/lib/auth.tsx +++ b/studio/lib/auth.tsx @@ -16,7 +16,6 @@ import { clearLocalStorage, resetSignInClicks } from './local-storage' export const AuthContext = AuthContextInternal export const AuthProvider = ({ children }: PropsWithChildren<{}>) => { - const queryClient = useQueryClient() const { ui } = useStore() const router = useRouter() const telemetryProps = useTelemetryProps() diff --git a/studio/lib/profile.tsx b/studio/lib/profile.tsx index d8feec8dcbc..41203d29913 100644 --- a/studio/lib/profile.tsx +++ b/studio/lib/profile.tsx @@ -1,9 +1,7 @@ -import { useQueryClient } from '@tanstack/react-query' import { useRouter } from 'next/router' import { PropsWithChildren, createContext, useContext, useMemo } from 'react' -import { useTelemetryProps } from 'common' -import { invalidateOrganizationsQuery } from 'data/organizations/organizations-query' +import { useIsLoggedIn, useTelemetryProps } from 'common' import { usePermissionsQuery } from 'data/permissions/permissions-query' import { useProfileCreateMutation } from 'data/profile/profile-create-mutation' import { useProfileQuery } from 'data/profile/profile-query' @@ -29,11 +27,12 @@ export const ProfileContext = createContext({ }) export const ProfileProvider = ({ children }: PropsWithChildren<{}>) => { - const queryClient = useQueryClient() const { ui } = useStore() const router = useRouter() const telemetryProps = useTelemetryProps() + const isLoggedIn = useIsLoggedIn() + const { mutate: createProfile, isLoading: isCreatingProfile } = useProfileCreateMutation({ async onSuccess() { Telemetry.sendEvent( @@ -41,8 +40,6 @@ export const ProfileProvider = ({ children }: PropsWithChildren<{}>) => { telemetryProps, router ) - - await invalidateOrganizationsQuery(queryClient) }, onError() { ui.setNotification({ @@ -60,6 +57,7 @@ export const ProfileProvider = ({ children }: PropsWithChildren<{}>) => { isError, isSuccess, } = useProfileQuery({ + enabled: isLoggedIn, onSuccess(profile) { Telemetry.sendIdentify(profile, telemetryProps) }, @@ -71,7 +69,7 @@ export const ProfileProvider = ({ children }: PropsWithChildren<{}>) => { }, }) - const { isInitialLoading: isLoadingPermissions } = usePermissionsQuery() + const { isInitialLoading: isLoadingPermissions } = usePermissionsQuery({ enabled: isLoggedIn }) const value = useMemo(() => { const isLoading = isLoadingProfile || isCreatingProfile || isLoadingPermissions