diff --git a/apps/studio/components/interfaces/App/AppBannerWrapperContext.tsx b/apps/studio/components/interfaces/App/AppBannerWrapperContext.tsx index 221a8f9c376..c49235f6eea 100644 --- a/apps/studio/components/interfaces/App/AppBannerWrapperContext.tsx +++ b/apps/studio/components/interfaces/App/AppBannerWrapperContext.tsx @@ -3,38 +3,75 @@ import { PropsWithChildren, createContext, useContext, useEffect, useState } fro import { LOCAL_STORAGE_KEYS } from 'lib/constants' -const NOTICE_BANNER_KEY = LOCAL_STORAGE_KEYS.PGBOUNCER_DEPRECATION_WARNING +const PGBOUNCER_BANNER_KEY = LOCAL_STORAGE_KEYS.PGBOUNCER_IPV6_DEPRECATION_WARNING +const VERCEL_BANNER_KEY = LOCAL_STORAGE_KEYS.VERCEL_IPV6_DEPRECATION_WARNING +const DEFAULT_NOTICE_BANNER_KEY = LOCAL_STORAGE_KEYS.PGBOUNCER_DEPRECATION_WARNING // [Joshen] Update this as and when we need to use the NoticeBanner type AppBannerContextType = { - acknowledged: boolean - onUpdateAcknowledged: (value: boolean) => void + ipv6BannerAcknowledged: boolean + pgbouncerBannerAcknowledged: string[] + vercelBannerAcknowledged: string[] + onUpdateAcknowledged: (key: 'ipv6' | 'pgbouncer' | 'vercel', value: boolean | string) => void } const AppBannerContext = createContext({ - acknowledged: false, + ipv6BannerAcknowledged: false, + pgbouncerBannerAcknowledged: [], + vercelBannerAcknowledged: [], onUpdateAcknowledged: noop, }) export const useAppBannerContext = () => useContext(AppBannerContext) export const AppBannerContextProvider = ({ children }: PropsWithChildren<{}>) => { - const [acknowledged, setAcknowledged] = useState(false) + const [ipv6BannerAcknowledged, setIpv6BannerAcknowledged] = useState(false) + + // [Joshen] These will take a list of refs instead since they are project specific + // Comma separated strings, no spaces + const [pgbouncerBannerAcknowledged, setPgbouncerBannerAcknowledged] = useState([]) + const [vercelBannerAcknowledged, setVercelBannerAcknowledged] = useState([]) useEffect(() => { if (typeof window !== 'undefined') { - setAcknowledged(localStorage.getItem(NOTICE_BANNER_KEY) === 'true') + setIpv6BannerAcknowledged(localStorage.getItem(DEFAULT_NOTICE_BANNER_KEY) === 'true') + + const acknowledgedPbBouncerRefs = localStorage.getItem(PGBOUNCER_BANNER_KEY)?.split(',') ?? [] + setPgbouncerBannerAcknowledged(acknowledgedPbBouncerRefs) + + const acknowledgedVercelRefs = localStorage.getItem(VERCEL_BANNER_KEY)?.split(',') ?? [] + setVercelBannerAcknowledged(acknowledgedVercelRefs) } }, []) const value = { - acknowledged, - onUpdateAcknowledged: (value: boolean) => { - if (typeof window !== 'undefined') { - window.localStorage.setItem(NOTICE_BANNER_KEY, value.toString()) + ipv6BannerAcknowledged, + pgbouncerBannerAcknowledged, + vercelBannerAcknowledged, + onUpdateAcknowledged: (key: 'ipv6' | 'pgbouncer' | 'vercel', value: boolean | string) => { + if (key === 'ipv6' && typeof value === 'boolean') { + if (typeof window !== 'undefined') { + window.localStorage.setItem(DEFAULT_NOTICE_BANNER_KEY, value.toString()) + } + setIpv6BannerAcknowledged(value) + } + + if (key === 'pgbouncer' && typeof value === 'string' && value.length > 0) { + const updatedRefs = pgbouncerBannerAcknowledged.concat([value]) + if (typeof window !== 'undefined') { + window.localStorage.setItem(PGBOUNCER_BANNER_KEY, updatedRefs.join(',')) + } + setPgbouncerBannerAcknowledged(updatedRefs) + } + + if (key === 'vercel' && typeof value === 'string' && value.length > 0) { + const updatedRefs = pgbouncerBannerAcknowledged.concat([value]) + if (typeof window !== 'undefined') { + window.localStorage.setItem(VERCEL_BANNER_KEY, updatedRefs.join(',')) + } + setVercelBannerAcknowledged(updatedRefs) } - setAcknowledged(value) }, } @@ -42,6 +79,7 @@ export const AppBannerContextProvider = ({ children }: PropsWithChildren<{}>) => } export const useIsNoticeBannerShown = () => { - const { acknowledged } = useAppBannerContext() - return acknowledged + const { ipv6BannerAcknowledged, pgbouncerBannerAcknowledged, vercelBannerAcknowledged } = + useAppBannerContext() + return ipv6BannerAcknowledged && pgbouncerBannerAcknowledged && vercelBannerAcknowledged } diff --git a/apps/studio/components/layouts/AppLayout/NoticeBanner.tsx b/apps/studio/components/layouts/AppLayout/NoticeBanner.tsx index 72e6ffd1208..679a75318a5 100644 --- a/apps/studio/components/layouts/AppLayout/NoticeBanner.tsx +++ b/apps/studio/components/layouts/AppLayout/NoticeBanner.tsx @@ -1,6 +1,10 @@ +import { useSupabaseClient } from '@supabase/auth-helpers-react' +import { useQuery } from '@tanstack/react-query' +import { useRouter } from 'next/router' +import { useParams } from 'common' + import { useAppBannerContext } from 'components/interfaces/App/AppBannerWrapperContext' import { useProfile } from 'lib/profile' -import { useRouter } from 'next/router' import { Button, IconExternalLink } from 'ui' // [Joshen] For this notice specifically, just FYI @@ -9,12 +13,74 @@ import { Button, IconExternalLink } from 'ui' export const NoticeBanner = () => { const router = useRouter() - const { isLoading } = useProfile() + const { isLoading: isLoadingProfile } = useProfile() const appBannerContext = useAppBannerContext() - const { acknowledged, onUpdateAcknowledged } = appBannerContext + const { + ipv6BannerAcknowledged, + pgbouncerBannerAcknowledged, + vercelBannerAcknowledged, + onUpdateAcknowledged, + } = appBannerContext - if (isLoading || router.pathname.includes('sign-in') || acknowledged) return null + const supabase = useSupabaseClient() + const { ref: projectRef } = useParams() + + // [Alaister]: using inline queries here since this is temporary + const { data, isLoading: isLoadingIpv6Enabled } = useQuery( + ['projects', projectRef, 'pgbouncer-enabled'], + async ({ signal }) => { + let query = supabase.rpc('ipv6_active_status', { project_ref: projectRef }).returns< + { + pgbouncer_active: boolean + vercel_active: boolean + }[] + >() + + if (signal) { + query = query.abortSignal(signal) + } + + const result = await query + + if (result.data === null) { + return { + pgbouncer_active: false, + vercel_active: false, + } + } + + return result.data[0] + }, + { enabled: Boolean(projectRef) } + ) + + const pgbouncerEnabled = data?.pgbouncer_active ?? false + const vercelWithoutSupavisorEnabled = data?.vercel_active ?? false + + // [Joshen] Pgbouncer list and vercel list are mutually exclusive + const pgbouncerProjectAcknowledged = pgbouncerBannerAcknowledged.includes(projectRef ?? '') + const vercelProjectAcknowledged = vercelBannerAcknowledged.includes(projectRef ?? '') + const allAcknowledged = + (!pgbouncerEnabled && !vercelWithoutSupavisorEnabled && ipv6BannerAcknowledged) || + (ipv6BannerAcknowledged && pgbouncerEnabled && pgbouncerProjectAcknowledged) || + (ipv6BannerAcknowledged && vercelWithoutSupavisorEnabled && vercelProjectAcknowledged) + + if ( + isLoadingProfile || + isLoadingIpv6Enabled || + router.pathname.includes('sign-in') || + allAcknowledged + ) { + return null + } + + const currentlyViewing = + pgbouncerEnabled && !pgbouncerProjectAcknowledged + ? ('pgbouncer' as const) + : vercelWithoutSupavisorEnabled && !vercelProjectAcknowledged + ? ('vercel' as const) + : ('ipv6' as const) return (
{ style={{ height: '44px' }} >

- Prepare for the PgBouncer and IPv4 deprecations on 26th January 2024 + {currentlyViewing === 'pgbouncer' && + 'Our logs on 26th Jan show that you have accessed PgBouncer. Please migrate now. You can ignore this warning if you have already migrated.'} + {currentlyViewing === 'vercel' && + "To prepare for the IPv4 migration, please redeploy your Vercel application to detect the updated environment variables if it hasn't been deployed since 27th January."} + {currentlyViewing === 'ipv6' && + 'We are migrating our infrastructure from IPv4 to IPv6. Please migrate now. You can ignore this warning if you have already migrated.'}

-
diff --git a/apps/studio/lib/constants/index.ts b/apps/studio/lib/constants/index.ts index 25151b3f80a..952ace51da5 100644 --- a/apps/studio/lib/constants/index.ts +++ b/apps/studio/lib/constants/index.ts @@ -39,6 +39,8 @@ export const LOCAL_STORAGE_KEYS = { GRAPHIQL_RLS_BYPASS_WARNING: 'graphiql-rls-bypass-warning-dismissed', CLS_DIFF_WARNING: 'cls-diff-warning-dismissed', CLS_SELECT_STAR_WARNING: 'cls-select-star-warning-dismissed', + PGBOUNCER_IPV6_DEPRECATION_WARNING: 'pgbouncer-ipv6-deprecation-warning-dismissed', + VERCEL_IPV6_DEPRECATION_WARNING: 'vercel-ipv6-deprecation-warning-dismissed', PGBOUNCER_DEPRECATION_WARNING: 'pgbouncer-deprecation-warning-dismissed', } diff --git a/supabase/migrations/20240129101115_add_ipv6_active_status_rpc.sql b/supabase/migrations/20240129101115_add_ipv6_active_status_rpc.sql new file mode 100644 index 00000000000..241a06ebaa7 --- /dev/null +++ b/supabase/migrations/20240129101115_add_ipv6_active_status_rpc.sql @@ -0,0 +1,21 @@ +create +or replace function ipv6_active_status (project_ref text) returns table (pgbouncer_active boolean, vercel_active boolean) as $$ +declare + pgbouncer_active boolean; + vercel_active boolean; +begin + select exists ( + select 1 + from active_pgbouncer_projects ap + where ap.project_ref = $1 + ) into pgbouncer_active; + + select exists ( + select 1 + from vercel_project_connections_without_supavisor vp + where vp.project_ref = $1 + ) into vercel_active; + + return query select pgbouncer_active, vercel_active; +end; +$$ language plpgsql security definer; \ No newline at end of file