From aec38b69caa8fe8dde8d82990b442034aed16443 Mon Sep 17 00:00:00 2001 From: phamhieu Date: Mon, 18 Apr 2022 16:02:18 +0700 Subject: [PATCH 01/12] chore: clean up ProjectBuildingState --- .../{to-be-cleaned => layouts/ProjectLayout}/BuildingState.tsx | 0 studio/components/layouts/ProjectLayout/TestConnection.tsx | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename studio/components/{to-be-cleaned => layouts/ProjectLayout}/BuildingState.tsx (100%) diff --git a/studio/components/to-be-cleaned/BuildingState.tsx b/studio/components/layouts/ProjectLayout/BuildingState.tsx similarity index 100% rename from studio/components/to-be-cleaned/BuildingState.tsx rename to studio/components/layouts/ProjectLayout/BuildingState.tsx diff --git a/studio/components/layouts/ProjectLayout/TestConnection.tsx b/studio/components/layouts/ProjectLayout/TestConnection.tsx index 867d41ed7c6..7543fd5388e 100644 --- a/studio/components/layouts/ProjectLayout/TestConnection.tsx +++ b/studio/components/layouts/ProjectLayout/TestConnection.tsx @@ -2,8 +2,8 @@ import { useRouter } from 'next/router' import { observer } from 'mobx-react-lite' import { FC, ReactNode } from 'react' import { Project } from 'types' -import BuildingState from 'components/to-be-cleaned/BuildingState' import { PROJECT_STATUS } from 'lib/constants' +import BuildingState from './BuildingState' interface Props { project: Project From c915520784a6ce3f1561ebd1d6eaa5026539deb4 Mon Sep 17 00:00:00 2001 From: phamhieu Date: Mon, 18 Apr 2022 16:32:27 +0700 Subject: [PATCH 02/12] chore: clean up withAuth --- studio/hooks/misc/withAuth.tsx | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/studio/hooks/misc/withAuth.tsx b/studio/hooks/misc/withAuth.tsx index 1ebbe3ef816..11d4b57e5ee 100644 --- a/studio/hooks/misc/withAuth.tsx +++ b/studio/hooks/misc/withAuth.tsx @@ -1,10 +1,8 @@ import { useProfile, useStore } from 'hooks' -import { ComponentType, useEffect, useState } from 'react' -import { flatten, isUndefined } from 'lodash' +import { ComponentType, useEffect } from 'react' import { NextRouter, useRouter } from 'next/router' import { IS_PLATFORM } from 'lib/constants' import Connecting from 'components/ui/Loading' -import { Project } from 'types' const PLATFORM_ONLY_PAGES = ['storage', 'reports', 'settings'] @@ -18,7 +16,6 @@ export function withAuth( return (props: any) => { const router = useRouter() const rootStore = useStore() - const [isConnecting, setConnecting] = useState(true) const { ref, slug } = router.query const { app, ui } = rootStore @@ -35,6 +32,7 @@ export function withAuth( const isRedirecting = isAccessingBlockedPage || checkRedirectTo(isLoading, router, profile, redirectTo, redirectIfFound) + const isConnecting = !isLoading && !isRedirecting && router.isReady ? false : true useEffect(() => { // this should run before redirecting @@ -66,12 +64,6 @@ export function withAuth( } }, [isLoading, router.isReady, ref, slug]) - useEffect(() => { - if (!isLoading && !isRedirecting && router.isReady) { - setConnecting(false) - } - }, [isLoading, isRedirecting, router.isReady]) - if (isConnecting) return return @@ -79,7 +71,7 @@ export function withAuth( } function defaultRedirectTo(ref: string | string[] | undefined) { - return IS_PLATFORM ? '/' : !isUndefined(ref) ? `/project/${ref}` : '/' + return IS_PLATFORM ? '/' : ref !== undefined ? `/project/${ref}` : '/' } function checkRedirectTo( From edc536f9cbea9d23d094091e26110971ff09be74 Mon Sep 17 00:00:00 2001 From: phamhieu Date: Mon, 18 Apr 2022 17:06:31 +0700 Subject: [PATCH 03/12] fix: update store after project creation --- studio/pages/new/[slug].tsx | 5 ++--- studio/stores/app/AppStore.ts | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/studio/pages/new/[slug].tsx b/studio/pages/new/[slug].tsx index 3fe60a07230..b8d9107377c 100644 --- a/studio/pages/new/[slug].tsx +++ b/studio/pages/new/[slug].tsx @@ -188,9 +188,8 @@ export const Wizard = observer(() => { message: `Failed to create new project: ${response.error.message}`, }) } else { - // Use redirect to reload store data properly - // after creating a new project - window.location.replace(`/project/${response.ref}/building`) + app.onProjectCreated(response) + router.push(`/project/${response.ref}/building`) } } diff --git a/studio/stores/app/AppStore.ts b/studio/stores/app/AppStore.ts index 63ff300c127..4e0118c4225 100644 --- a/studio/stores/app/AppStore.ts +++ b/studio/stores/app/AppStore.ts @@ -12,6 +12,7 @@ export interface IAppStore { projects: IProjectStore organizations: OrganizationStore database: IDatabaseStore + onProjectCreated: (project: any) => void onProjectUpdated: (project: any) => void onProjectDeleted: (project: any) => void onProjectConnectionStringUpdated: (projectId: number, value: string) => void @@ -39,6 +40,25 @@ export default class AppStore implements IAppStore { this.database = new DatabaseStore(rootStore, `${this.baseUrl}/database`, headers) } + onProjectCreated(project: any) { + if (project && project.id) { + const temp: Project = { + id: project.id, + ref: project.ref, + name: project.name, + status: project.status, + organization_id: project.organization_id, + cloud_provider: project.cloud_provider, + region: project.region, + inserted_at: project.inserted_at, + subscription_id: project.subscription_id, + kpsVersion: undefined, + connectionString: undefined, + } + this.projects.data[project.id] = temp + } + } + onProjectUpdated(project: any) { if (project && project.id) { const clone = cloneDeep(this.projects.data[project.id]) From 1254c60b52c7bd64cf5afe69b10734662a80adf9 Mon Sep 17 00:00:00 2001 From: phamhieu Date: Mon, 18 Apr 2022 17:15:43 +0700 Subject: [PATCH 04/12] Revert "chore: clean up withAuth" This reverts commit c915520784a6ce3f1561ebd1d6eaa5026539deb4. --- studio/hooks/misc/withAuth.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/studio/hooks/misc/withAuth.tsx b/studio/hooks/misc/withAuth.tsx index 11d4b57e5ee..1ebbe3ef816 100644 --- a/studio/hooks/misc/withAuth.tsx +++ b/studio/hooks/misc/withAuth.tsx @@ -1,8 +1,10 @@ import { useProfile, useStore } from 'hooks' -import { ComponentType, useEffect } from 'react' +import { ComponentType, useEffect, useState } from 'react' +import { flatten, isUndefined } from 'lodash' import { NextRouter, useRouter } from 'next/router' import { IS_PLATFORM } from 'lib/constants' import Connecting from 'components/ui/Loading' +import { Project } from 'types' const PLATFORM_ONLY_PAGES = ['storage', 'reports', 'settings'] @@ -16,6 +18,7 @@ export function withAuth( return (props: any) => { const router = useRouter() const rootStore = useStore() + const [isConnecting, setConnecting] = useState(true) const { ref, slug } = router.query const { app, ui } = rootStore @@ -32,7 +35,6 @@ export function withAuth( const isRedirecting = isAccessingBlockedPage || checkRedirectTo(isLoading, router, profile, redirectTo, redirectIfFound) - const isConnecting = !isLoading && !isRedirecting && router.isReady ? false : true useEffect(() => { // this should run before redirecting @@ -64,6 +66,12 @@ export function withAuth( } }, [isLoading, router.isReady, ref, slug]) + useEffect(() => { + if (!isLoading && !isRedirecting && router.isReady) { + setConnecting(false) + } + }, [isLoading, isRedirecting, router.isReady]) + if (isConnecting) return return @@ -71,7 +79,7 @@ export function withAuth( } function defaultRedirectTo(ref: string | string[] | undefined) { - return IS_PLATFORM ? '/' : ref !== undefined ? `/project/${ref}` : '/' + return IS_PLATFORM ? '/' : !isUndefined(ref) ? `/project/${ref}` : '/' } function checkRedirectTo( From a1e3a2b7b16d42291cc3c9f4a032f78b79c90603 Mon Sep 17 00:00:00 2001 From: phamhieu Date: Mon, 18 Apr 2022 17:16:55 +0700 Subject: [PATCH 05/12] chore: clean up --- studio/hooks/misc/withAuth.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/studio/hooks/misc/withAuth.tsx b/studio/hooks/misc/withAuth.tsx index 1ebbe3ef816..2c6e1b84f74 100644 --- a/studio/hooks/misc/withAuth.tsx +++ b/studio/hooks/misc/withAuth.tsx @@ -1,10 +1,8 @@ import { useProfile, useStore } from 'hooks' import { ComponentType, useEffect, useState } from 'react' -import { flatten, isUndefined } from 'lodash' import { NextRouter, useRouter } from 'next/router' import { IS_PLATFORM } from 'lib/constants' import Connecting from 'components/ui/Loading' -import { Project } from 'types' const PLATFORM_ONLY_PAGES = ['storage', 'reports', 'settings'] @@ -79,7 +77,7 @@ export function withAuth( } function defaultRedirectTo(ref: string | string[] | undefined) { - return IS_PLATFORM ? '/' : !isUndefined(ref) ? `/project/${ref}` : '/' + return IS_PLATFORM ? '/' : ref !== undefined ? `/project/${ref}` : '/' } function checkRedirectTo( From 5622d0e74b46bda75ec086d7c3fe4c94da033dd2 Mon Sep 17 00:00:00 2001 From: phamhieu Date: Tue, 19 Apr 2022 11:07:04 +0700 Subject: [PATCH 06/12] chore: clean up --- studio/components/layouts/ProjectLayout/ProjectLayout.tsx | 5 ++--- studio/pages/project/[ref]/index.tsx | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx index be75878a99f..38de7719bf5 100644 --- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx @@ -76,15 +76,14 @@ interface ContentWrapperProps { const ContentWrapper: FC = observer(({ isLoading, children }) => { const { ui } = useStore() const router = useRouter() - const project = ui.selectedProject const requiresDbConnection: boolean = router.pathname !== '/project/[ref]/settings/general' return ( <> - {isLoading || project === undefined ? ( + {isLoading || ui.selectedProject === undefined ? ( ) : requiresDbConnection ? ( - +
{children}
) : ( diff --git a/studio/pages/project/[ref]/index.tsx b/studio/pages/project/[ref]/index.tsx index 36d861d5660..4cb7affddc4 100644 --- a/studio/pages/project/[ref]/index.tsx +++ b/studio/pages/project/[ref]/index.tsx @@ -5,7 +5,7 @@ import { useStore, withAuth } from 'hooks' import { ExampleProject, ClientLibrary } from 'components/interfaces/Home' import { CLIENT_LIBRARIES, EXAMPLE_PROJECTS } from 'components/interfaces/Home/Home.constants' import { IS_PLATFORM } from 'lib/constants' -import BaseLayout from 'components/layouts' +import ProjectLayout from 'components/layouts' import ProjectUsageSection from 'components/interfaces/Home/ProjectUsageSection' const Home: NextPage = () => { @@ -18,7 +18,7 @@ const Home: NextPage = () => { : 'Welcome to your project' return ( - +

{projectName}

@@ -45,7 +45,7 @@ const Home: NextPage = () => {
-
+ ) } From 1d47fc5e769fc44c4dac1467a5dd9cdee816ea0e Mon Sep 17 00:00:00 2001 From: phamhieu Date: Tue, 19 Apr 2022 11:40:59 +0700 Subject: [PATCH 07/12] refactor: DisplayConfigSettings and DisplayApiSettings --- .../Home/NewProjectPanel/NewProjectPanel.tsx | 7 +- .../layouts/ProjectLayout/BuildingState.tsx | 5 +- .../to-be-cleaned/DisplayProjectSettings.tsx | 230 ------------------ studio/components/to-be-cleaned/Panel.tsx | 2 +- .../ui/ProjectSettings/DisplayApiSettings.tsx | 108 ++++++++ .../ProjectSettings/DisplayConfigSettings.tsx | 92 +++++++ .../ProjectSettings/SettingsLoadingState.tsx | 24 ++ .../SettingsLoadingState.types.tsx | 4 + .../ui/ProjectSettings/index.constants.tsx | 1 + .../components/ui/ProjectSettings/index.tsx | 4 + studio/pages/project/[ref]/settings/api.tsx | 2 +- 11 files changed, 237 insertions(+), 242 deletions(-) delete mode 100644 studio/components/to-be-cleaned/DisplayProjectSettings.tsx create mode 100644 studio/components/ui/ProjectSettings/DisplayApiSettings.tsx create mode 100644 studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx create mode 100644 studio/components/ui/ProjectSettings/SettingsLoadingState.tsx create mode 100644 studio/components/ui/ProjectSettings/SettingsLoadingState.types.tsx create mode 100644 studio/components/ui/ProjectSettings/index.constants.tsx create mode 100644 studio/components/ui/ProjectSettings/index.tsx diff --git a/studio/components/interfaces/Home/NewProjectPanel/NewProjectPanel.tsx b/studio/components/interfaces/Home/NewProjectPanel/NewProjectPanel.tsx index fba67f8d2b9..2bae2c671a6 100644 --- a/studio/components/interfaces/Home/NewProjectPanel/NewProjectPanel.tsx +++ b/studio/components/interfaces/Home/NewProjectPanel/NewProjectPanel.tsx @@ -1,11 +1,6 @@ +import { DisplayApiSettings, DisplayConfigSettings } from 'components/ui/ProjectSettings' import { FC } from 'react' -import { Typography } from '@supabase/ui' - import GetStartedPanel from './GetStartedPanel' -import { - DisplayApiSettings, - DisplayConfigSettings, -} from 'components/to-be-cleaned/DisplayProjectSettings' interface Props {} diff --git a/studio/components/layouts/ProjectLayout/BuildingState.tsx b/studio/components/layouts/ProjectLayout/BuildingState.tsx index e23caf7c2af..19fe8106372 100644 --- a/studio/components/layouts/ProjectLayout/BuildingState.tsx +++ b/studio/components/layouts/ProjectLayout/BuildingState.tsx @@ -2,10 +2,6 @@ import Link from 'next/link' import { FC, useEffect, useRef } from 'react' import { observer } from 'mobx-react-lite' import { Badge, IconArrowRight, IconLoader, Button } from '@supabase/ui' -import { - DisplayApiSettings, - DisplayConfigSettings, -} from 'components/to-be-cleaned/DisplayProjectSettings' import ExampleProject from 'components/interfaces/Home/ExampleProject' import ClientLibrary from 'components/interfaces/Home/ClientLibrary' import { CLIENT_LIBRARIES, EXAMPLE_PROJECTS } from 'components/interfaces/Home/Home.constants' @@ -14,6 +10,7 @@ import { API_URL, PROJECT_STATUS } from 'lib/constants' import { useStore } from 'hooks' import { get } from 'lib/common/fetch' import { Project } from 'types' +import { DisplayApiSettings, DisplayConfigSettings } from 'components/ui/ProjectSettings' type ProjectBuildingState = { project: Project } const ProjectBuildingState: FC = ({ project }) => { diff --git a/studio/components/to-be-cleaned/DisplayProjectSettings.tsx b/studio/components/to-be-cleaned/DisplayProjectSettings.tsx deleted file mode 100644 index a120dc92352..00000000000 --- a/studio/components/to-be-cleaned/DisplayProjectSettings.tsx +++ /dev/null @@ -1,230 +0,0 @@ -import useSWR from 'swr' -import { FC } from 'react' -import { useRouter } from 'next/router' -import { get } from 'lib/common/fetch' -import { Badge, IconAlertCircle, Input, Loading, Typography } from '@supabase/ui' -import { JwtSecretUpdateStatus, ProjectEvents } from '@supabase/shared-types/out/events' - -import { useJwtSecretUpdateStatus } from 'hooks' -import { API_URL } from 'lib/constants' -import Panel from './Panel' - -const { Text, Title } = Typography -const API_SERVICE_ID = 1 - -export const DisplayApiSettings = () => { - const router = useRouter() - const { ref } = router.query - - const { data, error }: any = useSWR(`${API_URL}/props/project/${ref}/settings`, get) - const { - isError: isJwtSecretUpdateStatusError, - isLoading: isJwtSecretUpdateStatusLoading, - jwtSecretUpdateStatus, - }: any = useJwtSecretUpdateStatus(ref) - - const isNotUpdatingJwtSecret = - jwtSecretUpdateStatus === undefined || jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updated - - if (!data || isJwtSecretUpdateStatusLoading) - return ( - - - {error || isJwtSecretUpdateStatusError ? ( -
- - - - Failed to fetch API keys -
- ) : ( -
- {/* @ts-ignore */} - -
- )} -
-
- ) - - // Get the API service - const apiService = (data?.services ?? []).find((x: any) => x.app.id == API_SERVICE_ID) - const apiKeys = apiService?.service_api_keys ?? [] - - return ( - - {!data || isJwtSecretUpdateStatusLoading ? ( - // @ts-ignore - - ) : ( - apiKeys.map((x: any, i: number) => ( - = 1 && - 'border-t border-panel-border-interior-light dark:border-panel-border-interior-dark' - } - > - - {x.tags?.split(',').map((x: any, i: number) => ( - - {x} - - ))} - {x.tags === 'service_role' && ( - <> - {'secret'} - - )} - {x.tags === 'anon' && {'public'}} - - } - readOnly - copy={isNotUpdatingJwtSecret} - className="input-mono" - disabled - reveal={x.tags !== 'anon' && isNotUpdatingJwtSecret} - value={ - jwtSecretUpdateStatus === JwtSecretUpdateStatus.Failed - ? 'JWT secret update failed, new API key may have issues' - : jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updating - ? 'Updating JWT secret...' - : x.api_key - } - onChange={() => {}} - descriptionText={ - x.tags === 'service_role' - ? 'This key has the ability to bypass Row Level Security. Never share it publicly.' - : 'This key is safe to use in a browser if you have enabled Row Level Security for your tables and configured policies.' - } - /> - - )) - )} - - ) -} - -export const DisplayConfigSettings = () => { - const router = useRouter() - const { ref } = router.query - - const { data, error }: any = useSWR(`${API_URL}/props/project/${ref}/settings`, get) - const { - isError: isJwtSecretUpdateStatusError, - isLoading: isJwtSecretUpdateStatusLoading, - jwtSecretUpdateStatus, - }: any = useJwtSecretUpdateStatus(ref) - - const isNotUpdatingJwtSecret = - jwtSecretUpdateStatus === undefined || jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updated - - if (!data || isJwtSecretUpdateStatusLoading) - return ( - - - {error || isJwtSecretUpdateStatusError ? ( -
- - - - - Failed to fetch project configuration - -
- ) : ( -
- {/* @ts-ignore */} - -
- )} -
-
- ) - - // Get the API service - const jwtSecret = data?.project.jwt_secret ?? '' - const apiService = (data?.services ?? []).find((x: any) => x.app.id == API_SERVICE_ID) - const apiConfig = apiService.app_config - - return ( -
- - Project Configuration - - } - > - - - - - - - -
- ) -} - -const ApiContentWrapper: FC = ({ children }) => { - return ( - -
Project API keys
-

- Your API is secured behind an API gateway which requires an API Key for every request. -
- You can use the keys below to use Supabase client libraries. -

- - } - > - {children} -
- ) -} - -const ConfigContentWrapper: FC = ({ children }) => { - return ( - -
Project Configuration
- - } - > - {children} -
- ) -} diff --git a/studio/components/to-be-cleaned/Panel.tsx b/studio/components/to-be-cleaned/Panel.tsx index 59ffd62aa81..9ee51559ef7 100644 --- a/studio/components/to-be-cleaned/Panel.tsx +++ b/studio/components/to-be-cleaned/Panel.tsx @@ -22,7 +22,7 @@ function Panel(props: any) { className={` border border-panel-border-light dark:border-panel-border-dark shadow-sm overflow-hidden - rounded rounded-md ${props.noMargin ? '' : 'mb-8'} ${props.className}`} + rounded-md ${props.noMargin ? '' : 'mb-8'} ${props.className}`} > {props.title && (
diff --git a/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx b/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx new file mode 100644 index 00000000000..1ff735751ed --- /dev/null +++ b/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx @@ -0,0 +1,108 @@ +import { FC } from 'react' +import { useRouter } from 'next/router' +import { Input } from '@supabase/ui' +import { JwtSecretUpdateStatus } from '@supabase/shared-types/out/events' +import { useJwtSecretUpdateStatus, useProjectSettings } from 'hooks' +import { SettingsLoadingState } from './SettingsLoadingState' +import { DEFAULT_API_SERVICE_ID } from './index.constants' +import Panel from 'components/to-be-cleaned/Panel' + +const DisplayApiSettings = () => { + const router = useRouter() + const { ref } = router.query + const { + services, + isLoading: isProjectSettingsLoading, + isError: isProjectSettingsError, + } = useProjectSettings(ref as string | undefined) + const { + isError: isJwtSecretUpdateStatusError, + isLoading: isJwtSecretUpdateStatusLoading, + jwtSecretUpdateStatus, + }: any = useJwtSecretUpdateStatus(ref) + + const isNotUpdatingJwtSecret = + jwtSecretUpdateStatus === undefined || jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updated + // Get the API service + const apiService = (services ?? []).find((x: any) => x.app.id == DEFAULT_API_SERVICE_ID) + const apiKeys = apiService?.service_api_keys ?? [] + + return ( + + {!isProjectSettingsLoading || isJwtSecretUpdateStatusLoading ? ( + + ) : ( + apiKeys.map((x: any, i: number) => ( + = 1 && + 'border-t border-panel-border-interior-light dark:border-panel-border-interior-dark' + } + > + + {x.tags?.split(',').map((x: any, i: number) => ( + + {x} + + ))} + {x.tags === 'service_role' && ( + <> + {'secret'} + + )} + {x.tags === 'anon' && {'public'}} + + } + readOnly + copy={isNotUpdatingJwtSecret} + className="input-mono" + disabled + reveal={x.tags !== 'anon' && isNotUpdatingJwtSecret} + value={ + jwtSecretUpdateStatus === JwtSecretUpdateStatus.Failed + ? 'JWT secret update failed, new API key may have issues' + : jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updating + ? 'Updating JWT secret...' + : x.api_key + } + onChange={() => {}} + descriptionText={ + x.tags === 'service_role' + ? 'This key has the ability to bypass Row Level Security. Never share it publicly.' + : 'This key is safe to use in a browser if you have enabled Row Level Security for your tables and configured policies.' + } + /> + + )) + )} + + ) +} +export default DisplayApiSettings + +const ApiContentWrapper: FC = ({ children }) => { + return ( + +
Project API keys
+

+ Your API is secured behind an API gateway which requires an API Key for every request. +
+ You can use the keys below to use Supabase client libraries. +

+
+ } + > + {children} + + ) +} diff --git a/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx b/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx new file mode 100644 index 00000000000..8ef447a0b79 --- /dev/null +++ b/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx @@ -0,0 +1,92 @@ +import { FC } from 'react' +import { useRouter } from 'next/router' +import { Input } from '@supabase/ui' +import { JwtSecretUpdateStatus } from '@supabase/shared-types/out/events' +import { useJwtSecretUpdateStatus, useProjectSettings } from 'hooks' +import { SettingsLoadingState } from './SettingsLoadingState' +import { DEFAULT_API_SERVICE_ID } from './index.constants' +import Panel from 'components/to-be-cleaned/Panel' + +const DisplayConfigSettings = () => { + const router = useRouter() + const { ref } = router.query + const { + project, + services, + isLoading: isProjectSettingsLoading, + isError: isProjectSettingsError, + } = useProjectSettings(ref as string | undefined) + const { + isError: isJwtSecretUpdateStatusError, + isLoading: isJwtSecretUpdateStatusLoading, + jwtSecretUpdateStatus, + }: any = useJwtSecretUpdateStatus(ref) + + const isNotUpdatingJwtSecret = + jwtSecretUpdateStatus === undefined || jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updated + // Get the API service + const jwtSecret = project?.jwt_secret ?? '' + const apiService = (services ?? []).find((x: any) => x.app.id == DEFAULT_API_SERVICE_ID) + const apiConfig = apiService?.app_config + + return ( + + {isProjectSettingsLoading || isJwtSecretUpdateStatusLoading ? ( + + ) : ( + <> + + + + + + + + )} + + ) +} + +export default DisplayConfigSettings + +const ConfigContentWrapper: FC = ({ children }) => { + return ( + +
Project Configuration
+ + } + > + {children} +
+ ) +} diff --git a/studio/components/ui/ProjectSettings/SettingsLoadingState.tsx b/studio/components/ui/ProjectSettings/SettingsLoadingState.tsx new file mode 100644 index 00000000000..1c8dd232659 --- /dev/null +++ b/studio/components/ui/ProjectSettings/SettingsLoadingState.tsx @@ -0,0 +1,24 @@ +import { FC } from 'react' +import { IconAlertCircle, Loading, Typography } from '@supabase/ui' +import { SettingsLoadingStateProps } from './SettingsLoadingState.types' +import Panel from 'components/to-be-cleaned/Panel' + +export const SettingsLoadingState: FC = ({ isError, errorMessage }) => { + return ( + + {isError ? ( +
+ + + + {errorMessage} +
+ ) : ( +
+ {/* @ts-ignore */} + +
+ )} +
+ ) +} diff --git a/studio/components/ui/ProjectSettings/SettingsLoadingState.types.tsx b/studio/components/ui/ProjectSettings/SettingsLoadingState.types.tsx new file mode 100644 index 00000000000..b3c9248594d --- /dev/null +++ b/studio/components/ui/ProjectSettings/SettingsLoadingState.types.tsx @@ -0,0 +1,4 @@ +export interface SettingsLoadingStateProps { + isError: boolean + errorMessage: string +} diff --git a/studio/components/ui/ProjectSettings/index.constants.tsx b/studio/components/ui/ProjectSettings/index.constants.tsx new file mode 100644 index 00000000000..fdd902e2733 --- /dev/null +++ b/studio/components/ui/ProjectSettings/index.constants.tsx @@ -0,0 +1 @@ +export const DEFAULT_API_SERVICE_ID = 1 diff --git a/studio/components/ui/ProjectSettings/index.tsx b/studio/components/ui/ProjectSettings/index.tsx new file mode 100644 index 00000000000..37e2dcc5b8b --- /dev/null +++ b/studio/components/ui/ProjectSettings/index.tsx @@ -0,0 +1,4 @@ +import DisplayApiSettings from './DisplayApiSettings' +import DisplayConfigSettings from './DisplayConfigSettings' + +export { DisplayApiSettings, DisplayConfigSettings } diff --git a/studio/pages/project/[ref]/settings/api.tsx b/studio/pages/project/[ref]/settings/api.tsx index dfe96adce81..4e83547e1ef 100644 --- a/studio/pages/project/[ref]/settings/api.tsx +++ b/studio/pages/project/[ref]/settings/api.tsx @@ -45,7 +45,7 @@ import ConfirmModal from 'components/ui/Dialogs/ConfirmDialog' import Panel from 'components/to-be-cleaned/Panel' import MultiSelectUI from 'components/to-be-cleaned/MultiSelect' import SchemaFormPanel from 'components/to-be-cleaned/forms/SchemaFormPanel' -import { DisplayApiSettings } from 'components/to-be-cleaned/DisplayProjectSettings' +import { DisplayApiSettings } from 'components/ui/ProjectSettings' const JWT_SECRET_UPDATE_ERROR_MESSAGES = { [JwtSecretUpdateError.APIServicesConfigurationUpdateFailed]: From 7c1136307aef0397ff746a0fdbd592855c1ca579 Mon Sep 17 00:00:00 2001 From: phamhieu Date: Tue, 19 Apr 2022 11:44:59 +0700 Subject: [PATCH 08/12] fix: apiConfig undefined --- studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx b/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx index 8ef447a0b79..aea68e910dc 100644 --- a/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx +++ b/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx @@ -27,7 +27,7 @@ const DisplayConfigSettings = () => { // Get the API service const jwtSecret = project?.jwt_secret ?? '' const apiService = (services ?? []).find((x: any) => x.app.id == DEFAULT_API_SERVICE_ID) - const apiConfig = apiService?.app_config + const apiConfig = apiService?.app_config ?? {} return ( From f723d165891bc91791b77db78904f1885e3ddaff Mon Sep 17 00:00:00 2001 From: phamhieu Date: Tue, 19 Apr 2022 11:46:10 +0700 Subject: [PATCH 09/12] fix: typo --- studio/components/ui/ProjectSettings/DisplayApiSettings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx b/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx index 1ff735751ed..8f59b791745 100644 --- a/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx +++ b/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx @@ -29,7 +29,7 @@ const DisplayApiSettings = () => { return ( - {!isProjectSettingsLoading || isJwtSecretUpdateStatusLoading ? ( + {isProjectSettingsLoading || isJwtSecretUpdateStatusLoading ? ( Date: Tue, 19 Apr 2022 12:19:52 +0700 Subject: [PATCH 10/12] fix: useProjectSettings refreshInterval to wait for service_api_keys data --- studio/hooks/queries/useProjectSettings.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/studio/hooks/queries/useProjectSettings.ts b/studio/hooks/queries/useProjectSettings.ts index 833bb17f489..e97eba1a0c9 100644 --- a/studio/hooks/queries/useProjectSettings.ts +++ b/studio/hooks/queries/useProjectSettings.ts @@ -4,7 +4,18 @@ import useSWR from 'swr' export function useProjectSettings(ref?: string) { const url = `${API_URL}/props/project/${ref}/settings` - const { data, error } = useSWR(ref ? url : null, get) + const { data, error } = useSWR(ref ? url : null, get, { + /** + * on project creation, the service_api_keys will be populated with a delay + * check for data.services.service_api_keys, return a valid refresh interval if it's empty + */ + refreshInterval: function (data: any) { + const apiService = (data?.services ?? []).find((x: any) => x.app.id == DEFAULT_API_SERVICE_ID) + const apiKeys = apiService?.service_api_keys ?? [] + const interval = apiKeys.length === 0 ? 2000 : 0 + return interval + }, + }) const anyError = data?.error || error return { @@ -15,3 +26,5 @@ export function useProjectSettings(ref?: string) { isError: !!anyError, } } + +const DEFAULT_API_SERVICE_ID = 1 From 7b4e1d2de4554868b181c17e8f2361266b1cff9e Mon Sep 17 00:00:00 2001 From: phamhieu Date: Tue, 19 Apr 2022 12:23:57 +0700 Subject: [PATCH 11/12] fix: show loading state when apikeys is empty --- studio/components/ui/ProjectSettings/DisplayApiSettings.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx b/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx index 8f59b791745..249b9f3c34d 100644 --- a/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx +++ b/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx @@ -26,10 +26,12 @@ const DisplayApiSettings = () => { // Get the API service const apiService = (services ?? []).find((x: any) => x.app.id == DEFAULT_API_SERVICE_ID) const apiKeys = apiService?.service_api_keys ?? [] + // api keys should not be empty. However it can be populated with a delay on project creation + const isApikeysEmpty = apiKeys.length === 0 return ( - {isProjectSettingsLoading || isJwtSecretUpdateStatusLoading ? ( + {isProjectSettingsLoading || isJwtSecretUpdateStatusLoading || isApikeysEmpty ? ( Date: Tue, 19 Apr 2022 12:29:43 +0700 Subject: [PATCH 12/12] fix: create DEFAULT_PROJECT_API_SERVICE_ID constant --- .../components/ui/ProjectSettings/DisplayApiSettings.tsx | 4 ++-- .../ui/ProjectSettings/DisplayConfigSettings.tsx | 4 ++-- studio/components/ui/ProjectSettings/index.constants.tsx | 1 - studio/hooks/queries/useProjectSettings.ts | 8 ++++---- studio/lib/constants/infrastructure.ts | 2 ++ 5 files changed, 10 insertions(+), 9 deletions(-) delete mode 100644 studio/components/ui/ProjectSettings/index.constants.tsx diff --git a/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx b/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx index 249b9f3c34d..512a986c997 100644 --- a/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx +++ b/studio/components/ui/ProjectSettings/DisplayApiSettings.tsx @@ -4,7 +4,7 @@ import { Input } from '@supabase/ui' import { JwtSecretUpdateStatus } from '@supabase/shared-types/out/events' import { useJwtSecretUpdateStatus, useProjectSettings } from 'hooks' import { SettingsLoadingState } from './SettingsLoadingState' -import { DEFAULT_API_SERVICE_ID } from './index.constants' +import { DEFAULT_PROJECT_API_SERVICE_ID } from 'lib/constants' import Panel from 'components/to-be-cleaned/Panel' const DisplayApiSettings = () => { @@ -24,7 +24,7 @@ const DisplayApiSettings = () => { const isNotUpdatingJwtSecret = jwtSecretUpdateStatus === undefined || jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updated // Get the API service - const apiService = (services ?? []).find((x: any) => x.app.id == DEFAULT_API_SERVICE_ID) + const apiService = (services ?? []).find((x: any) => x.app.id == DEFAULT_PROJECT_API_SERVICE_ID) const apiKeys = apiService?.service_api_keys ?? [] // api keys should not be empty. However it can be populated with a delay on project creation const isApikeysEmpty = apiKeys.length === 0 diff --git a/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx b/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx index aea68e910dc..eb099f75976 100644 --- a/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx +++ b/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx @@ -4,7 +4,7 @@ import { Input } from '@supabase/ui' import { JwtSecretUpdateStatus } from '@supabase/shared-types/out/events' import { useJwtSecretUpdateStatus, useProjectSettings } from 'hooks' import { SettingsLoadingState } from './SettingsLoadingState' -import { DEFAULT_API_SERVICE_ID } from './index.constants' +import { DEFAULT_PROJECT_API_SERVICE_ID } from 'lib/constants' import Panel from 'components/to-be-cleaned/Panel' const DisplayConfigSettings = () => { @@ -26,7 +26,7 @@ const DisplayConfigSettings = () => { jwtSecretUpdateStatus === undefined || jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updated // Get the API service const jwtSecret = project?.jwt_secret ?? '' - const apiService = (services ?? []).find((x: any) => x.app.id == DEFAULT_API_SERVICE_ID) + const apiService = (services ?? []).find((x: any) => x.app.id == DEFAULT_PROJECT_API_SERVICE_ID) const apiConfig = apiService?.app_config ?? {} return ( diff --git a/studio/components/ui/ProjectSettings/index.constants.tsx b/studio/components/ui/ProjectSettings/index.constants.tsx deleted file mode 100644 index fdd902e2733..00000000000 --- a/studio/components/ui/ProjectSettings/index.constants.tsx +++ /dev/null @@ -1 +0,0 @@ -export const DEFAULT_API_SERVICE_ID = 1 diff --git a/studio/hooks/queries/useProjectSettings.ts b/studio/hooks/queries/useProjectSettings.ts index e97eba1a0c9..946c4cc074d 100644 --- a/studio/hooks/queries/useProjectSettings.ts +++ b/studio/hooks/queries/useProjectSettings.ts @@ -1,5 +1,5 @@ import { get } from 'lib/common/fetch' -import { API_URL } from 'lib/constants' +import { API_URL, DEFAULT_PROJECT_API_SERVICE_ID } from 'lib/constants' import useSWR from 'swr' export function useProjectSettings(ref?: string) { @@ -10,7 +10,9 @@ export function useProjectSettings(ref?: string) { * check for data.services.service_api_keys, return a valid refresh interval if it's empty */ refreshInterval: function (data: any) { - const apiService = (data?.services ?? []).find((x: any) => x.app.id == DEFAULT_API_SERVICE_ID) + const apiService = (data?.services ?? []).find( + (x: any) => x.app.id == DEFAULT_PROJECT_API_SERVICE_ID + ) const apiKeys = apiService?.service_api_keys ?? [] const interval = apiKeys.length === 0 ? 2000 : 0 return interval @@ -26,5 +28,3 @@ export function useProjectSettings(ref?: string) { isError: !!anyError, } } - -const DEFAULT_API_SERVICE_ID = 1 diff --git a/studio/lib/constants/infrastructure.ts b/studio/lib/constants/infrastructure.ts index da1d6c4efaf..4dbba6f83e8 100644 --- a/studio/lib/constants/infrastructure.ts +++ b/studio/lib/constants/infrastructure.ts @@ -140,3 +140,5 @@ export const PASSWORD_STRENGTH_PERCENTAGE = { export const DEFAULT_FREE_PROJECTS_LIMIT = 2 export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES = 5 * 1024 * 1024 * 1024 // 5 GB + +export const DEFAULT_PROJECT_API_SERVICE_ID = 1