From 969e8aadf13846ab8edef1a41fddb8d0ca713f46 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Tue, 24 Jan 2023 12:44:27 -0330 Subject: [PATCH 01/24] Temp add Python as a community lib --- apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx | 3 +++ .../Navigation/NavigationMenu/NavigationMenuRefListItems.tsx | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx index 7738ed521d9..e4f17189b4f 100644 --- a/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx @@ -3,6 +3,7 @@ import Image from 'next/image' import Link from 'next/link' import { useRouter } from 'next/router' import { Fragment } from 'react' +import { Badge } from '~/../../packages/ui' const home = [ [ @@ -99,6 +100,7 @@ const home = [ icon: '/img/icons/menu/reference-python', href: '/reference/python/introduction', level: 'reference_python', + community: true, }, { label: 'Tools Reference', @@ -171,6 +173,7 @@ const NavigationMenuHome = ({ active }) => { className="w-4 h-4 group-hover:scale-110 ease-out transition-all" /> {link.label} + {link.community && Community} diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx index 690615b4a13..b81f6c5a1dd 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuRefListItems.tsx @@ -179,7 +179,6 @@ const Content: React.FC = ({ id, lib, commonSections, sp if (!sections) console.error('no common sections imported') const menu = NavItems[id] - console.log(menu) const databaseFunctions = find(sections, { title: 'Database' }) ? find(sections, { title: 'Database' }).items : [] From fa8a09075b47033172c40acfb8c2d55f02f7133b Mon Sep 17 00:00:00 2001 From: Ollie Halliday <22655069+Hallidayo@users.noreply.github.com> Date: Mon, 30 Jan 2023 11:15:39 +0000 Subject: [PATCH 02/24] changed intro that typescript is used --- .../pages/guides/getting-started/tutorials/with-sveltekit.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/pages/guides/getting-started/tutorials/with-sveltekit.mdx b/apps/docs/pages/guides/getting-started/tutorials/with-sveltekit.mdx index 78f236a99d9..614dac516e2 100644 --- a/apps/docs/pages/guides/getting-started/tutorials/with-sveltekit.mdx +++ b/apps/docs/pages/guides/getting-started/tutorials/with-sveltekit.mdx @@ -24,7 +24,7 @@ Let's start building the Svelte app from scratch. ### Initialize a Svelte app We can use the [SvelteKit Skeleton Project](https://kit.svelte.dev/docs) to initialize -an app called `supabase-sveltekit` (for this tutorial you do not need TypeScript, ESLint, Prettier, or Playwright): +an app called `supabase-sveltekit` (for this tutorial we will be using TypeScript): ```bash npm init svelte@next supabase-sveltekit From a98fbda1f906e328dbeb8b54ca86af97e81a4806 Mon Sep 17 00:00:00 2001 From: dannykng Date: Mon, 30 Jan 2023 17:01:30 -0800 Subject: [PATCH 03/24] Update nav menu --- spec/common-client-libs-sections.json | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/spec/common-client-libs-sections.json b/spec/common-client-libs-sections.json index 79489c67aea..c48f87c6b99 100644 --- a/spec/common-client-libs-sections.json +++ b/spec/common-client-libs-sections.json @@ -28,7 +28,8 @@ "reference_javascript_v1", "reference_dart_v0", "reference_dart_v1", - "reference_python_v2" + "reference_python_v2", + "reference_csharp_v0" ] }, { @@ -36,7 +37,12 @@ "id": "upgrade-guide", "slug": "upgrade-guide", "type": "markdown", - "excludes": ["reference_javascript_v2", "reference_dart_v1", "reference_python_v2"] + "excludes": [ + "reference_javascript_v2", + "reference_dart_v1", + "reference_python_v2", + "reference_csharp_v0" + ] }, { "title": "Database", @@ -842,7 +848,6 @@ { "title": "Misc", "excludes": ["reference_dart_v1", "reference_dart_v0"], - "items": [ { "title": "Release Notes", From fdbae8134795ed5af6029c136bc0566eee7c0475 Mon Sep 17 00:00:00 2001 From: dannykng Date: Mon, 30 Jan 2023 17:01:43 -0800 Subject: [PATCH 04/24] Update intro --- apps/docs/docs/ref/csharp/introduction.mdx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/docs/docs/ref/csharp/introduction.mdx b/apps/docs/docs/ref/csharp/introduction.mdx index 6c699314937..1762adbe5aa 100644 --- a/apps/docs/docs/ref/csharp/introduction.mdx +++ b/apps/docs/docs/ref/csharp/introduction.mdx @@ -8,13 +8,14 @@ hideTitle: true

C# Client Library

-

supabase-csharp

+

@supabase-community/supabase-csharp

- This reference documents every object and method available in Supabase's C# - library, [supabase-csharp](https://www.nuget.org/packages/supabase-csharp). You can - use supabase-csharp to interact with your Postgres database, listen to database changes, invoke - Deno Edge Functions, build login and user management functionality, and manage large files. + This reference documents every object and method available in the + [supabase-csharp](https://www.nuget.org/packages/supabase-csharp) library from the Supabase + community. You can use `supabase-csharp` to interact with your Postgres database, listen to + database changes, invoke Deno Edge Functions, build login and user management functionality, and + manage large files.
From 5c754eff9e388e18a1c4514c4bb03da7e7547e17 Mon Sep 17 00:00:00 2001 From: dshukertjr <18113850+dshukertjr@users.noreply.github.com> Date: Tue, 31 Jan 2023 11:48:34 +0900 Subject: [PATCH 05/24] adds custom domains video on our guide --- apps/docs/pages/guides/platform/custom-domains.mdx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apps/docs/pages/guides/platform/custom-domains.mdx b/apps/docs/pages/guides/platform/custom-domains.mdx index 13fedb8e525..1ca5b0aa514 100644 --- a/apps/docs/pages/guides/platform/custom-domains.mdx +++ b/apps/docs/pages/guides/platform/custom-domains.mdx @@ -14,6 +14,15 @@ Custom Domains are currently in beta, and are being slowly made available to pro Custom domains allow you to present a branded experience to your users. Custom domains are available as a [add-on for projects on a paid tier](https://app.supabase.com/project/_/settings/billing/update). Setting up a custom domain requires [Owner or Admin permissions](/docs/guides/platform/access-control#manage-team-members) for the project. Currently, you must use a subdomain (e.g., `api.example.com`, rather than `example.com`) for the purposes of this guide. +
+ +
+ ## Configure a Custom Domain using the Supabase Dashboard Follow the **Custom Domains** steps in the [General Settings](https://app.supabase.com/project/_/settings/general) page in the Dashboard to set up a custom domain for your project. From 4c52e93727fdabe72e5447e5f353cb64b14b1cf4 Mon Sep 17 00:00:00 2001 From: shandamengcheng Date: Tue, 31 Jan 2023 11:21:24 +0800 Subject: [PATCH 06/24] feat: add illa_logo.png --- .../public/img/integrations/logos/illa_logo.png | Bin 0 -> 11288 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 apps/docs/public/img/integrations/logos/illa_logo.png diff --git a/apps/docs/public/img/integrations/logos/illa_logo.png b/apps/docs/public/img/integrations/logos/illa_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ea541c278f489663cb348a2c4ba16743dfb1fdd6 GIT binary patch literal 11288 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 F{{Yy4caQ)8 literal 0 HcmV?d00001 From 7b7f3423959e7d48d85fcd4ed468929c737c0e7e Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Tue, 31 Jan 2023 09:10:01 -0330 Subject: [PATCH 07/24] Add contributor note --- .../Navigation/NavigationMenu/HomeMenu.tsx | 12 ++++++------ apps/docs/docs/ref/csharp/introduction.mdx | 14 ++++++++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx index 98441a8039a..fadcad59bed 100644 --- a/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx @@ -95,17 +95,17 @@ const home = [ href: '/reference/dart/introduction', level: 'reference_dart', }, - { - label: 'C#', - icon: '/img/icons/menu/reference-csharp', - href: '/reference/csharp/introduction', - level: 'reference_csharp', - }, { label: 'Python', icon: '/img/icons/menu/reference-python', href: '/reference/python/introduction', level: 'reference_python', + }, + { + label: 'C#', + icon: '/img/icons/menu/reference-csharp', + href: '/reference/csharp/introduction', + level: 'reference_csharp', community: true, }, { diff --git a/apps/docs/docs/ref/csharp/introduction.mdx b/apps/docs/docs/ref/csharp/introduction.mdx index 6c699314937..863210222fd 100644 --- a/apps/docs/docs/ref/csharp/introduction.mdx +++ b/apps/docs/docs/ref/csharp/introduction.mdx @@ -13,8 +13,14 @@ hideTitle: true

- This reference documents every object and method available in Supabase's C# - library, [supabase-csharp](https://www.nuget.org/packages/supabase-csharp). You can - use supabase-csharp to interact with your Postgres database, listen to database changes, invoke - Deno Edge Functions, build login and user management functionality, and manage large files. + This reference documents every object and method available in Supabase's C# library, + [supabase-csharp](https://www.nuget.org/packages/supabase-csharp). You can use supabase-csharp to + interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, + build login and user management functionality, and manage large files. +
+ +
+

The C# client library is created and maintained by the Supabase community, and is not an official library. Please be tolerant of areas where the library is still being developed, and — as with all the libraries — feel free to contribute wherever you find issues.

+

Huge thanks to the official maintainer, [Joseph Schultz](https://github.com/acupofjose).

+
From 259d55ac8e4f81b2697516582693f16e89432d48 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Tue, 31 Jan 2023 10:46:38 -0330 Subject: [PATCH 08/24] Update note --- apps/docs/docs/ref/csharp/introduction.mdx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/docs/docs/ref/csharp/introduction.mdx b/apps/docs/docs/ref/csharp/introduction.mdx index 863210222fd..0be280f9807 100644 --- a/apps/docs/docs/ref/csharp/introduction.mdx +++ b/apps/docs/docs/ref/csharp/introduction.mdx @@ -21,6 +21,11 @@ hideTitle: true

The C# client library is created and maintained by the Supabase community, and is not an official library. Please be tolerant of areas where the library is still being developed, and — as with all the libraries — feel free to contribute wherever you find issues.

-

Huge thanks to the official maintainer, [Joseph Schultz](https://github.com/acupofjose).

+ +

+ Huge thanks to official maintainer, [Joseph Schultz](https://github.com/acupofjose), and to [Ben + Randall](https://github.com/veleek) and [Rhuan Barros](https://github.com/rhuanbarros) for their + help. +

From fe9830626bb0af97e26bab0afe0c8752f52a0120 Mon Sep 17 00:00:00 2001 From: dannykng Date: Tue, 31 Jan 2023 13:46:17 -0800 Subject: [PATCH 09/24] Add Community badge --- apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx index fadcad59bed..36f01bf6830 100644 --- a/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx @@ -100,6 +100,7 @@ const home = [ icon: '/img/icons/menu/reference-python', href: '/reference/python/introduction', level: 'reference_python', + community: true, }, { label: 'C#', From c2bcf2503d43aa545a138ddac41f3606da563098 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 1 Feb 2023 13:29:23 +0800 Subject: [PATCH 10/24] Update sentry ignore list --- studio/sentry.client.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/sentry.client.config.js b/studio/sentry.client.config.js index ccda8e0e31f..456f62c88a0 100644 --- a/studio/sentry.client.config.js +++ b/studio/sentry.client.config.js @@ -38,7 +38,7 @@ Sentry.init({ // the URL params instead of the store, but we cannot repro locally, staging nor on prod // Safe to ignore since it's not a user-facing issue + we've not received any user feedback/report about it // Ref: https://github.com/supabase/supabase/pull/9729 - 'The provided `href` (/org/[slug]/settings) value is missing query values (slug)', + 'The provided `href` (/org/[slug]/general) value is missing query values (slug)', ], beforeSend: (event) => filterConsoleErrors(event), }) From 83f2b3c8715c3af7e9bfc0cd61c05216fb26bd0e Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 1 Feb 2023 13:41:01 +0800 Subject: [PATCH 11/24] Sentry fixes --- packages/ui/src/components/Input/Input.tsx | 2 +- studio/localStores/storageExplorer/StorageExplorerStore.js | 2 +- studio/pages/vercel/integrate.tsx | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/ui/src/components/Input/Input.tsx b/packages/ui/src/components/Input/Input.tsx index a1caec8de16..a78fade9330 100644 --- a/packages/ui/src/components/Input/Input.tsx +++ b/packages/ui/src/components/Input/Input.tsx @@ -97,7 +97,7 @@ function Input({ // }, [errors, touched]) function onCopy(value: any) { - navigator.clipboard.writeText(value).then( + navigator.clipboard.writeText(value)?.then( function () { /* clipboard successfully set */ setCopyLabel('Copied') diff --git a/studio/localStores/storageExplorer/StorageExplorerStore.js b/studio/localStores/storageExplorer/StorageExplorerStore.js index 6d75f5f36db..4596d565f6e 100644 --- a/studio/localStores/storageExplorer/StorageExplorerStore.js +++ b/studio/localStores/storageExplorer/StorageExplorerStore.js @@ -1485,7 +1485,7 @@ class StorageExplorerStore { .list(formattedPathToFolder, options) folderContents = folderContents.concat(data) options.offset += options.limit - if (data.length < options.limit) { + if ((data || []).length < options.limit) { break } } diff --git a/studio/pages/vercel/integrate.tsx b/studio/pages/vercel/integrate.tsx index 7da27b95c78..f986fdd8174 100644 --- a/studio/pages/vercel/integrate.tsx +++ b/studio/pages/vercel/integrate.tsx @@ -10,8 +10,7 @@ import Divider from 'components/ui/Divider' import { Dictionary } from 'components/grid' import { useStore, withAuth } from 'hooks' -import { API_URL, IS_PLATFORM } from 'lib/constants' -import { PROJECT_ENDPOINT_PROTOCOL } from 'pages/api/constants' +import { API_URL } from 'lib/constants' import { get } from 'lib/common/fetch' import { VERCEL_INTEGRATION_CONFIGS, @@ -370,8 +369,8 @@ const ProjectLinks: FC = observer(() => { // Then create env for vercel project with supabase project const vercelEnvs = prepareVercelEvns(defaultVercelEnvs, { - endpoint: `${projectDetails.apiService.protocol ?? 'https'}://${ - projectDetails.apiService.endpoint ?? '-' + endpoint: `${projectDetails.autoApiService.protocol ?? 'https'}://${ + projectDetails.autoApiService.endpoint ?? '-' }`, anon_key: projectDetails.autoApiService.defaultApiKey, service_key: projectDetails.autoApiService.serviceApiKey, From 37c90cbf20ff6794ec104e4d9ea0d7f6ee41f312 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 24 Jan 2023 12:15:10 +0800 Subject: [PATCH 12/24] Init network restrictions ui set up --- .../NetworkRestrictions.tsx | 22 ++++++ .../interfaces/Settings/General/index.ts | 11 ++- studio/data/network-restrictions/README.md | 49 +++++++++++++ studio/data/network-restrictions/keys.ts | 4 ++ .../network-restrictions-query.ts | 69 +++++++++++++++++++ .../network-retrictions-apply-mutation.ts | 51 ++++++++++++++ .../pages/project/[ref]/settings/general.tsx | 10 ++- 7 files changed, 213 insertions(+), 3 deletions(-) create mode 100644 studio/components/interfaces/Settings/General/NetworkRestrictions/NetworkRestrictions.tsx create mode 100644 studio/data/network-restrictions/README.md create mode 100644 studio/data/network-restrictions/keys.ts create mode 100644 studio/data/network-restrictions/network-restrictions-query.ts create mode 100644 studio/data/network-restrictions/network-retrictions-apply-mutation.ts diff --git a/studio/components/interfaces/Settings/General/NetworkRestrictions/NetworkRestrictions.tsx b/studio/components/interfaces/Settings/General/NetworkRestrictions/NetworkRestrictions.tsx new file mode 100644 index 00000000000..683e20114be --- /dev/null +++ b/studio/components/interfaces/Settings/General/NetworkRestrictions/NetworkRestrictions.tsx @@ -0,0 +1,22 @@ +import { FormHeader } from 'components/ui/Forms' + +import { useNetworkRestrictionsQuery } from 'data/network-restrictions/network-restrictions-query' +import { useParams } from 'hooks' + +const NetworkRestrictions = ({}) => { + const { ref } = useParams() + const { data, isLoading, isError, isSuccess } = useNetworkRestrictionsQuery({ projectRef: ref }) + + console.log(data) + + return ( +
+ +
+ ) +} + +export default NetworkRestrictions diff --git a/studio/components/interfaces/Settings/General/index.ts b/studio/components/interfaces/Settings/General/index.ts index 108fd79d1a7..59a3ebc7e63 100644 --- a/studio/components/interfaces/Settings/General/index.ts +++ b/studio/components/interfaces/Settings/General/index.ts @@ -1,6 +1,15 @@ import General from './General' import Infrastructure from './Infrastructure/Infrastructure' +import CustomDomainConfig from './CustomDomainConfig/CustomDomainConfig' +import NetworkRestrictions from './NetworkRestrictions/NetworkRestrictions' import DeleteProjectPanel from './DeleteProjectPanel/DeleteProjectPanel' import DeleteProjectButton from './DeleteProjectPanel/DeleteProjectButton' -export { General, Infrastructure, DeleteProjectPanel, DeleteProjectButton } +export { + General, + Infrastructure, + CustomDomainConfig, + NetworkRestrictions, + DeleteProjectPanel, + DeleteProjectButton, +} diff --git a/studio/data/network-restrictions/README.md b/studio/data/network-restrictions/README.md new file mode 100644 index 00000000000..05a0cabfb4f --- /dev/null +++ b/studio/data/network-restrictions/README.md @@ -0,0 +1,49 @@ +# Templates for Tanstack Query queries & mutations + +## Usage + +1. Duplicate the files in the \_\_templates directory you need depending on your use case. +2. Rename the files to match your resource name. +3. Update the `resource` variables to match your resource name. Usually find and replace works great here if you use case sensitive search (`resource` then `Resource`) +4. Implement your `queryFn` function. + +Referencing the other files in the `data` directory is a good way to see how to use the library. + +### SQL queries and mutations + +The dashboard often needs to query the users database directly. There are already some reusable hooks for this in `data/sql`. Reference the `data/fdw` directory for an example of how to use them. + +Note that the query key of the `useExecuteSqlQuery` hook will be automatically filled with an md5 hash of the SQL query, unless you provide a name for the query. + +## Files + +### `resources-query.ts` + +For queries that return a list of results. + +> :warning: Should only be used for simple lists, as it does not include any pagination. `useInfiniteQuery` should be used instead. + +### `resource-query.ts` + +For getting a single item based on parameter(s). `id` is used in this example but can be updated to whatever is needed. + +### `resource-update-mutation.ts` + +For updating a resource. This can easily be adapted to work for create, delete, or any verb that is needed. You may need to modify the `invalidateQueries` section depending on your use case. For example removing: + +```ts +queryClient.invalidateQueries(resourceKeys.resource(projectRef, id)) +``` + +if you are creating a new resource. + +### `keys.ts` + +Standardized keys for use with `useQuery` and `invalidateQueries`. This is not required but is a good way to keep things consistent. + +## Learning Resources + +- [Tanstack Query Docs](https://tanstack.com/query/v4/docs/react/overview) +- [TkDodo's Blog Posts](https://tanstack.com/query/v4/docs/react/community/tkdodos-blog) + +The blog posts in particular are very helpful for learning best practices and how to use the library effectively. diff --git a/studio/data/network-restrictions/keys.ts b/studio/data/network-restrictions/keys.ts new file mode 100644 index 00000000000..264e0497d4c --- /dev/null +++ b/studio/data/network-restrictions/keys.ts @@ -0,0 +1,4 @@ +export const networkRestrictionKeys = { + list: (projectRef: string | undefined) => + ['projects', projectRef, 'network-restrictions'] as const, +} diff --git a/studio/data/network-restrictions/network-restrictions-query.ts b/studio/data/network-restrictions/network-restrictions-query.ts new file mode 100644 index 00000000000..0cfc7ec1620 --- /dev/null +++ b/studio/data/network-restrictions/network-restrictions-query.ts @@ -0,0 +1,69 @@ +import { useCallback } from 'react' +import { useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query' + +import { networkRestrictionKeys } from './keys' +import { get } from 'lib/common/fetch' +import { API_ADMIN_URL } from 'lib/constants' + +export type NetworkRestrictionsVariables = { projectRef?: string } + +export type NetworkRestrictionsResponse = { + entitlement: 'disallowed' | 'allowed' + status: 'stored' | 'applied' + config: any +} + +export async function getNetworkRestrictions( + { projectRef }: NetworkRestrictionsVariables, + signal?: AbortSignal +) { + if (!projectRef) throw new Error('projectRef is required') + + const response = await get(`${API_ADMIN_URL}/projects/${projectRef}/network-restrictions`, { + signal, + }) + + // Not allowed error is a valid response to denote if a project + // has access to the network restrictions UI, so we'll handle it here + if (response.error) { + const isNotAllowedError = + (response.error as any)?.code === 400 && + (response.error as any)?.message?.includes('to be updated') + + if (isNowAllowedError) { + return {} + } else { + throw response.error + } + } + + return response as NetworkRestrictionsResponse[] +} + +export type NetworkRestrictionsData = Awaited> +export type NetworkRestrictionsError = unknown + +export const useNetworkRestrictionsQuery = ( + { projectRef }: NetworkRestrictionsVariables, + { + enabled = true, + ...options + }: UseQueryOptions = {} +) => + useQuery( + networkRestrictionKeys.list(projectRef), + ({ signal }) => getNetworkRestrictions({ projectRef }, signal), + { enabled: enabled && typeof projectRef !== 'undefined', ...options } + ) + +export const useNetworkRestrictionsPrefetch = ({ projectRef }: NetworkRestrictionsVariables) => { + const client = useQueryClient() + + return useCallback(() => { + if (projectRef) { + client.prefetchQuery(networkRestrictionKeys.list(projectRef), ({ signal }) => + getNetworkRestrictions({ projectRef }, signal) + ) + } + }, [projectRef]) +} diff --git a/studio/data/network-restrictions/network-retrictions-apply-mutation.ts b/studio/data/network-restrictions/network-retrictions-apply-mutation.ts new file mode 100644 index 00000000000..1adc146c9b3 --- /dev/null +++ b/studio/data/network-restrictions/network-retrictions-apply-mutation.ts @@ -0,0 +1,51 @@ +import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' +import { patch } from 'lib/common/fetch' +import { API_ADMIN_URL } from 'lib/constants' +import { networkRestrictionKeys } from './keys' + +export type NetworkRestrictionsApplyVariables = { + projectRef: string + id: string + updatedParam: string +} + +export async function applyNetworkRestrictions({ + projectRef, + updatedParam, +}: NetworkRestrictionsApplyVariables) { + if (!projectRef) throw new Error('projectRef is required') + + const response = await patch( + `${API_ADMIN_URL}/projects/${projectRef}/network-restrictions/apply`, + { updated_param: updatedParam } + ) + if (response.error) { + throw response.error + } + + return response +} + +type NetworkRestrictionsApplyData = Awaited> + +export const useNetworkRestrictionsApplyMutation = ({ + onSuccess, + ...options +}: Omit< + UseMutationOptions, + 'mutationFn' +> = {}) => { + const queryClient = useQueryClient() + + return useMutation( + (vars) => applyNetworkRestrictions(vars), + { + async onSuccess(data, variables, context) { + const { projectRef } = variables + await queryClient.invalidateQueries(networkRestrictionKeys.list(projectRef)) + await onSuccess?.(data, variables, context) + }, + ...options, + } + ) +} diff --git a/studio/pages/project/[ref]/settings/general.tsx b/studio/pages/project/[ref]/settings/general.tsx index 118d21cb6fe..3b722c4c841 100644 --- a/studio/pages/project/[ref]/settings/general.tsx +++ b/studio/pages/project/[ref]/settings/general.tsx @@ -1,8 +1,13 @@ import { observer } from 'mobx-react-lite' import { NextPageWithLayout } from 'types' import { SettingsLayout } from 'components/layouts' -import { General, Infrastructure, DeleteProjectPanel } from 'components/interfaces/Settings/General' -import CustomDomainConfig from 'components/interfaces/Settings/General/CustomDomainConfig/CustomDomainConfig' +import { + General, + Infrastructure, + CustomDomainConfig, + NetworkRestrictions, + DeleteProjectPanel, +} from 'components/interfaces/Settings/General' const ProjectSettings: NextPageWithLayout = () => { return ( @@ -10,6 +15,7 @@ const ProjectSettings: NextPageWithLayout = () => { + ) From d9ea92102ff0b0309c445a64ecf33ef264a4cf8a Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 25 Jan 2023 13:19:07 +0800 Subject: [PATCH 13/24] Small fix --- studio/data/network-restrictions/network-restrictions-query.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/data/network-restrictions/network-restrictions-query.ts b/studio/data/network-restrictions/network-restrictions-query.ts index 0cfc7ec1620..727148372cd 100644 --- a/studio/data/network-restrictions/network-restrictions-query.ts +++ b/studio/data/network-restrictions/network-restrictions-query.ts @@ -30,7 +30,7 @@ export async function getNetworkRestrictions( (response.error as any)?.code === 400 && (response.error as any)?.message?.includes('to be updated') - if (isNowAllowedError) { + if (isNotAllowedError) { return {} } else { throw response.error From 25f714df45e50df4405ca00d708acb75a72c16cf Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Fri, 27 Jan 2023 17:16:26 +0800 Subject: [PATCH 14/24] Hook up network restrictions --- package-lock.json | 15 ++ .../AddRestrictionModal.tsx | 126 ++++++++++++ .../NetworkRestrictions/AllowAllModal.tsx | 63 ++++++ .../NetworkRestrictions/DisallowAllModal.tsx | 64 ++++++ .../NetworkRestrictions.tsx | 185 +++++++++++++++++- .../RemoveRestrictionModal.tsx | 89 +++++++++ .../network-restrictions-query.ts | 20 +- .../network-retrictions-apply-mutation.ts | 15 +- studio/package.json | 1 + .../pages/project/[ref]/settings/general.tsx | 9 +- 10 files changed, 560 insertions(+), 27 deletions(-) create mode 100644 studio/components/interfaces/Settings/General/NetworkRestrictions/AddRestrictionModal.tsx create mode 100644 studio/components/interfaces/Settings/General/NetworkRestrictions/AllowAllModal.tsx create mode 100644 studio/components/interfaces/Settings/General/NetworkRestrictions/DisallowAllModal.tsx create mode 100644 studio/components/interfaces/Settings/General/NetworkRestrictions/RemoveRestrictionModal.tsx diff --git a/package-lock.json b/package-lock.json index 49ce07f0d47..9746c433cef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38330,6 +38330,7 @@ "generate-password": "^1.7.0", "html-to-image": "^1.10.8", "immutability-helper": "^3.1.1", + "ipaddr.js": "^2.0.1", "json-logic-js": "^2.0.2", "lodash": "^4.17.21", "lottie-react": "^2.2.1", @@ -38661,6 +38662,14 @@ "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" }, + "studio/node_modules/ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "engines": { + "node": ">= 10" + } + }, "studio/node_modules/openapi-types": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-9.3.1.tgz", @@ -64872,6 +64881,7 @@ "generate-password": "^1.7.0", "html-to-image": "^1.10.8", "immutability-helper": "^3.1.1", + "ipaddr.js": "^2.0.1", "jest": "^27.4.0", "jest-canvas-mock": "^2.3.1", "json-logic-js": "^2.0.2", @@ -65101,6 +65111,11 @@ "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" }, + "ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==" + }, "openapi-types": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-9.3.1.tgz", diff --git a/studio/components/interfaces/Settings/General/NetworkRestrictions/AddRestrictionModal.tsx b/studio/components/interfaces/Settings/General/NetworkRestrictions/AddRestrictionModal.tsx new file mode 100644 index 00000000000..6b93314d01a --- /dev/null +++ b/studio/components/interfaces/Settings/General/NetworkRestrictions/AddRestrictionModal.tsx @@ -0,0 +1,126 @@ +import { FC } from 'react' +import ipaddr from 'ipaddr.js' +import { Alert, Button, Form, Input, Modal } from 'ui' + +import { useStore, useParams } from 'hooks' +import { useNetworkRestrictionsApplyMutation } from 'data/network-restrictions/network-retrictions-apply-mutation' + +interface Props { + restrictedIps: string[] + visible: boolean + hasOverachingRestriction: boolean + onClose: () => void +} + +const AddRestrictionModal: FC = ({ + restrictedIps, + visible, + hasOverachingRestriction, + onClose, +}) => { + const formId = 'add-restriction-form' + const { ui } = useStore() + const { ref } = useParams() + const { mutateAsync: applyNetworkRestrictions } = useNetworkRestrictionsApplyMutation() + + const validate = (values: any) => { + const errors: any = {} + + try { + // Check if already exists + const alreadyExists = restrictedIps.includes(values.address) + if (alreadyExists) { + errors.address = 'This restriction already exists' + return errors + } + // Check if valid IP address + const [address, range] = ipaddr.IPv4.parseCIDR(values.address) + // Check if private range + const isPrivate = ipaddr.parse(address.octets.join('.')).range() === 'private' + if (isPrivate) errors.address = 'Private IP addresses are not supported' + } catch (error: any) { + if (error.message.includes('string is not formatted like an IPv4 CIDR range')) { + try { + // Check if IPv6 format + ipaddr.IPv6.parseCIDR(values.address) + errors.address = 'Only IPv4 addresses are supported at the moment' + } catch (error: any) { + errors.address = 'Please enter a valid address in CIDR notation' + } + } + } + return errors + } + + const onSubmit = async (values: any, { setSubmitting }: any) => { + if (!ref) return console.error('Project ref is required') + + setSubmitting(true) + const dbAllowedCidrs = hasOverachingRestriction + ? [values.address] + : [...restrictedIps, values.address] + + try { + await applyNetworkRestrictions({ projectRef: ref, dbAllowedCidrs }) + onClose() + } catch (error: any) { + ui.setNotification({ + category: 'error', + message: `Failed to add restriction: ${error.message}`, + }) + } finally { + setSubmitting(false) + } + } + + return ( + +
+ {({ isSubmitting }: any) => { + return ( + <> + +
+

+ This will add an IP address range to a list of allowed ranges that can access + your project's database. +

+ +
+
+
+ + +
+ + ) + }} +
+
+ ) +} + +export default AddRestrictionModal diff --git a/studio/components/interfaces/Settings/General/NetworkRestrictions/AllowAllModal.tsx b/studio/components/interfaces/Settings/General/NetworkRestrictions/AllowAllModal.tsx new file mode 100644 index 00000000000..084d12c3d89 --- /dev/null +++ b/studio/components/interfaces/Settings/General/NetworkRestrictions/AllowAllModal.tsx @@ -0,0 +1,63 @@ +import { FC, useState } from 'react' +import { Modal, Button } from 'ui' + +import { useStore, useParams } from 'hooks' +import { useNetworkRestrictionsApplyMutation } from 'data/network-restrictions/network-retrictions-apply-mutation' + +interface Props { + visible: boolean + onClose: () => void +} + +const AllowAllModal: FC = ({ visible, onClose }) => { + const { ui } = useStore() + const { ref } = useParams() + const [isSubmitting, setIsSubmitting] = useState(false) + const { mutateAsync: applyNetworkRestrictions } = useNetworkRestrictionsApplyMutation() + + const onSubmit = async () => { + if (!ref) return console.error('Project ref is required') + + setIsSubmitting(true) + try { + await applyNetworkRestrictions({ projectRef: ref, dbAllowedCidrs: ['0.0.0.0/0'] }) + onClose() + } catch (error: any) { + ui.setNotification({ + category: 'error', + message: `Failed to update restriction: ${error.message}`, + }) + } finally { + setIsSubmitting(false) + } + } + + return ( + + +
+

+ This will allow any IP address to access your project's database. Are you sure? +

+
+
+
+ + +
+
+ ) +} + +export default AllowAllModal diff --git a/studio/components/interfaces/Settings/General/NetworkRestrictions/DisallowAllModal.tsx b/studio/components/interfaces/Settings/General/NetworkRestrictions/DisallowAllModal.tsx new file mode 100644 index 00000000000..126c81b6bb1 --- /dev/null +++ b/studio/components/interfaces/Settings/General/NetworkRestrictions/DisallowAllModal.tsx @@ -0,0 +1,64 @@ +import { FC, useState } from 'react' +import { Modal, Button } from 'ui' + +import { useStore, useParams } from 'hooks' +import { useNetworkRestrictionsApplyMutation } from 'data/network-restrictions/network-retrictions-apply-mutation' + +interface Props { + visible: boolean + onClose: () => void +} + +const DisallowAllModal: FC = ({ visible, onClose }) => { + const { ui } = useStore() + const { ref } = useParams() + const [isSubmitting, setIsSubmitting] = useState(false) + const { mutateAsync: applyNetworkRestrictions } = useNetworkRestrictionsApplyMutation() + + const onSubmit = async () => { + if (!ref) return console.error('Project ref is required') + + setIsSubmitting(true) + try { + await applyNetworkRestrictions({ projectRef: ref, dbAllowedCidrs: ['127.0.0.1/32'] }) + onClose() + } catch (error: any) { + ui.setNotification({ + category: 'error', + message: `Failed to update restriction: ${error.message}`, + }) + } finally { + setIsSubmitting(false) + } + } + + return ( + + +
+

+ This will prevent any IP address other than your local machine from accessing your + project's database. Are you sure? +

+
+
+
+ + +
+
+ ) +} + +export default DisallowAllModal diff --git a/studio/components/interfaces/Settings/General/NetworkRestrictions/NetworkRestrictions.tsx b/studio/components/interfaces/Settings/General/NetworkRestrictions/NetworkRestrictions.tsx index 683e20114be..bd6f745867a 100644 --- a/studio/components/interfaces/Settings/General/NetworkRestrictions/NetworkRestrictions.tsx +++ b/studio/components/interfaces/Settings/General/NetworkRestrictions/NetworkRestrictions.tsx @@ -1,21 +1,188 @@ -import { FormHeader } from 'components/ui/Forms' +import Link from 'next/link' +import { useState } from 'react' +import { Button, IconAlertCircle, IconExternalLink, IconGlobe, IconLock } from 'ui' -import { useNetworkRestrictionsQuery } from 'data/network-restrictions/network-restrictions-query' import { useParams } from 'hooks' +import Panel from 'components/ui/Panel' +import { FormPanel, FormHeader } from 'components/ui/Forms' +import ShimmeringLoader from 'components/ui/ShimmeringLoader' +import AddRestrictionModal from './AddRestrictionModal' +import RemoveRestrictionModal from './RemoveRestrictionModal' +import DisallowAllModal from './DisallowAllModal' +import AllowAllModal from './AllowAllModal' +import { useNetworkRestrictionsQuery } from 'data/network-restrictions/network-restrictions-query' const NetworkRestrictions = ({}) => { const { ref } = useParams() - const { data, isLoading, isError, isSuccess } = useNetworkRestrictionsQuery({ projectRef: ref }) - console.log(data) + const [isAddingAddress, setIsAddingAddress] = useState(false) + const [isAllowingAll, setIsAllowingAll] = useState(false) + const [isDisallowingAll, setIsDisallowingAll] = useState(false) + const [selectedRestrictionToRemove, setSelectedRestrictionToRemove] = useState() + const { data, isLoading } = useNetworkRestrictionsQuery({ projectRef: ref }) + + const hasAccessToRestrictions = data?.entitlement === 'allowed' + const restrictedIps = data?.config.dbAllowedCidrs ?? [] + const restrictionStatus = data?.status ?? '' + + const hasApplyError = restrictedIps.length === 0 && restrictionStatus === 'stored' + const isUninitialized = restrictedIps.length === 0 && restrictionStatus.length === 0 + const isAllowedAll = restrictedIps.includes('0.0.0.0/0') + const isDisallowedAll = restrictedIps.length === 1 && restrictedIps[0] === '127.0.0.1/32' + + if (!hasAccessToRestrictions) return <> return ( -
- +
+
+ +
+ + + + + + +
+
+ {isLoading ? ( + + +
+ + + +
+
+
+ ) : hasApplyError ? ( + + +
+
+
+ +

Your network restrictions were not applied correctly

+
+

+ Please try to add your network restrictions again +

+
+
+ + +
+
+
+
+ ) : ( + + {isUninitialized || isAllowedAll ? ( +
+
+
+

+ Your database can be accessed by all IP addresses +

+

+ You may start limiting access to your database by adding a network + restriction. +

+
+
+
+ +
+
+ ) : isDisallowedAll ? ( +
+
+ +
+

+ Your database cannot be + accessed externally +

+

+ Only your local machine is able to access your project's database as all IP + addresses have been disallowed. +

+
+
+
+ +
+
+ ) : ( +
+
+
+

+ Only the following IP addresses have access to your database +

+

+ You may remove all of them to allow all IP addresses to have access to your + database +

+
+
+ + +
+
+ {restrictedIps.map((ip) => { + return ( +
+
+ +

{ip}

+
+ +
+ ) + })} +
+ )} +
+ )} +
+ + setIsAllowingAll(false)} /> + setIsDisallowingAll(false)} /> + + setIsAddingAddress(false)} /> -
+ setSelectedRestrictionToRemove(undefined)} + /> + ) } diff --git a/studio/components/interfaces/Settings/General/NetworkRestrictions/RemoveRestrictionModal.tsx b/studio/components/interfaces/Settings/General/NetworkRestrictions/RemoveRestrictionModal.tsx new file mode 100644 index 00000000000..cd2a4f7f654 --- /dev/null +++ b/studio/components/interfaces/Settings/General/NetworkRestrictions/RemoveRestrictionModal.tsx @@ -0,0 +1,89 @@ +import { FC, useState } from 'react' +import { Alert, Button, Form, Input, Modal } from 'ui' + +import { useStore, useParams } from 'hooks' +import { useNetworkRestrictionsApplyMutation } from 'data/network-restrictions/network-retrictions-apply-mutation' + +interface Props { + visible: boolean + restrictedIps: string[] + selectedRestriction?: string + onClose: () => void +} + +const RemoveRestrictionModal: FC = ({ + visible, + restrictedIps, + selectedRestriction, + onClose, +}) => { + const { ui } = useStore() + const { ref } = useParams() + const [isSubmitting, setIsSubmitting] = useState(false) + const { mutateAsync: applyNetworkRestrictions } = useNetworkRestrictionsApplyMutation() + + const isRemovingOnlyRestriction = + restrictedIps.length === 1 && restrictedIps[0] === selectedRestriction + + const onSubmit = async () => { + if (!ref) return console.error('Project ref is required') + + setIsSubmitting(true) + const dbAllowedCidrs = restrictedIps.filter((ip) => ip !== selectedRestriction) + + try { + if (dbAllowedCidrs.length === 0) { + await applyNetworkRestrictions({ projectRef: ref, dbAllowedCidrs: ['0.0.0.0/0'] }) + } else { + await applyNetworkRestrictions({ projectRef: ref, dbAllowedCidrs }) + } + onClose() + } catch (error: any) { + ui.setNotification({ + category: 'error', + message: `Failed to remove restriction: ${error.message}`, + }) + } finally { + setIsSubmitting(false) + } + } + + return ( + + +
+

+ The IPv4 address {selectedRestriction} will be removed + from your list of network restrictions + {isRemovingOnlyRestriction + ? '.' + : ", and no longer have access to your project's database."} +

+ {isRemovingOnlyRestriction && ( + + Removing all network restrictions will default to your database being accessible from + all IP addresses. + + )} +
+
+
+ + +
+
+ ) +} + +export default RemoveRestrictionModal diff --git a/studio/data/network-restrictions/network-restrictions-query.ts b/studio/data/network-restrictions/network-restrictions-query.ts index 727148372cd..41b70733b8f 100644 --- a/studio/data/network-restrictions/network-restrictions-query.ts +++ b/studio/data/network-restrictions/network-restrictions-query.ts @@ -9,8 +9,10 @@ export type NetworkRestrictionsVariables = { projectRef?: string } export type NetworkRestrictionsResponse = { entitlement: 'disallowed' | 'allowed' - status: 'stored' | 'applied' - config: any + status: '' | 'stored' | 'applied' + config: { dbAllowedCidrs: string[] } + old_config?: { dbAllowedCidrs: string[] } + error?: any } export async function getNetworkRestrictions( @@ -19,25 +21,29 @@ export async function getNetworkRestrictions( ) { if (!projectRef) throw new Error('projectRef is required') - const response = await get(`${API_ADMIN_URL}/projects/${projectRef}/network-restrictions`, { + const response = (await get(`${API_ADMIN_URL}/projects/${projectRef}/network-restrictions`, { signal, - }) + })) as NetworkRestrictionsResponse // Not allowed error is a valid response to denote if a project // has access to the network restrictions UI, so we'll handle it here if (response.error) { const isNotAllowedError = (response.error as any)?.code === 400 && - (response.error as any)?.message?.includes('to be updated') + (response.error as any)?.message?.includes('not allowed to set up network restrictions') if (isNotAllowedError) { - return {} + return { + entitlement: 'disallowed', + config: { dbAllowedCidrs: [] }, + status: '', + } as NetworkRestrictionsResponse } else { throw response.error } } - return response as NetworkRestrictionsResponse[] + return response } export type NetworkRestrictionsData = Awaited> diff --git a/studio/data/network-restrictions/network-retrictions-apply-mutation.ts b/studio/data/network-restrictions/network-retrictions-apply-mutation.ts index 1adc146c9b3..7321b5d9f64 100644 --- a/studio/data/network-restrictions/network-retrictions-apply-mutation.ts +++ b/studio/data/network-restrictions/network-retrictions-apply-mutation.ts @@ -1,27 +1,24 @@ import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' -import { patch } from 'lib/common/fetch' +import { post } from 'lib/common/fetch' import { API_ADMIN_URL } from 'lib/constants' import { networkRestrictionKeys } from './keys' export type NetworkRestrictionsApplyVariables = { projectRef: string - id: string - updatedParam: string + dbAllowedCidrs: string[] } export async function applyNetworkRestrictions({ projectRef, - updatedParam, + dbAllowedCidrs, }: NetworkRestrictionsApplyVariables) { if (!projectRef) throw new Error('projectRef is required') - const response = await patch( + const response = await post( `${API_ADMIN_URL}/projects/${projectRef}/network-restrictions/apply`, - { updated_param: updatedParam } + { dbAllowedCidrs } ) - if (response.error) { - throw response.error - } + if (response.error) throw response.error return response } diff --git a/studio/package.json b/studio/package.json index 0e6a6fc37a7..4bcbb3b9072 100644 --- a/studio/package.json +++ b/studio/package.json @@ -45,6 +45,7 @@ "generate-password": "^1.7.0", "html-to-image": "^1.10.8", "immutability-helper": "^3.1.1", + "ipaddr.js": "^2.0.1", "json-logic-js": "^2.0.2", "lodash": "^4.17.21", "lottie-react": "^2.2.1", diff --git a/studio/pages/project/[ref]/settings/general.tsx b/studio/pages/project/[ref]/settings/general.tsx index 3b722c4c841..d86bd8a0d5a 100644 --- a/studio/pages/project/[ref]/settings/general.tsx +++ b/studio/pages/project/[ref]/settings/general.tsx @@ -1,5 +1,7 @@ import { observer } from 'mobx-react-lite' + import { NextPageWithLayout } from 'types' +import { useFlag } from 'hooks' import { SettingsLayout } from 'components/layouts' import { General, @@ -10,12 +12,15 @@ import { } from 'components/interfaces/Settings/General' const ProjectSettings: NextPageWithLayout = () => { + const networkRestrictions = useFlag('networkRestrictions') + + // [Joshen] Opting for larger gap instead of gap-8 as compared to other pages for better grouping of content return ( -
+
- + {networkRestrictions && }
) From ea5a74af7d37fac24a816e500f32afbe6dc99004 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 30 Jan 2023 14:12:20 +0800 Subject: [PATCH 15/24] Shift network restrictions to database settings page --- studio/components/interfaces/Database/index.ts | 2 +- .../Database}/ConnectionPooling.tsx | 0 .../NetworkRestrictions/AddRestrictionModal.tsx | 11 +++++++++-- .../NetworkRestrictions/AllowAllModal.tsx | 0 .../NetworkRestrictions/DisallowAllModal.tsx | 9 ++++++++- .../NetworkRestrictions/NetworkRestrictions.tsx | 9 +++++++-- .../NetworkRestrictions/RemoveRestrictionModal.tsx | 0 .../components/interfaces/Settings/Database/index.ts | 4 ++++ .../components/interfaces/Settings/General/index.ts | 10 +--------- studio/pages/project/[ref]/settings/database.tsx | 9 ++++++--- studio/pages/project/[ref]/settings/general.tsx | 5 ----- 11 files changed, 36 insertions(+), 23 deletions(-) rename studio/components/interfaces/{Database/Pooling => Settings/Database}/ConnectionPooling.tsx (100%) rename studio/components/interfaces/Settings/{General => Database}/NetworkRestrictions/AddRestrictionModal.tsx (88%) rename studio/components/interfaces/Settings/{General => Database}/NetworkRestrictions/AllowAllModal.tsx (100%) rename studio/components/interfaces/Settings/{General => Database}/NetworkRestrictions/DisallowAllModal.tsx (83%) rename studio/components/interfaces/Settings/{General => Database}/NetworkRestrictions/NetworkRestrictions.tsx (94%) rename studio/components/interfaces/Settings/{General => Database}/NetworkRestrictions/RemoveRestrictionModal.tsx (100%) create mode 100644 studio/components/interfaces/Settings/Database/index.ts diff --git a/studio/components/interfaces/Database/index.ts b/studio/components/interfaces/Database/index.ts index d866b7d8c8c..94c664c2854 100644 --- a/studio/components/interfaces/Database/index.ts +++ b/studio/components/interfaces/Database/index.ts @@ -15,7 +15,7 @@ import PublicationsTables from './Publications/PublicationsTables' import BackupsList from './Backups/BackupsList' -import ConnectionPooling from './Pooling/ConnectionPooling' +import ConnectionPooling from '../Settings/Database/ConnectionPooling' import CreateTrigger from './Triggers/CreateTrigger' import DeleteTrigger from './Triggers/DeleteTrigger' diff --git a/studio/components/interfaces/Database/Pooling/ConnectionPooling.tsx b/studio/components/interfaces/Settings/Database/ConnectionPooling.tsx similarity index 100% rename from studio/components/interfaces/Database/Pooling/ConnectionPooling.tsx rename to studio/components/interfaces/Settings/Database/ConnectionPooling.tsx diff --git a/studio/components/interfaces/Settings/General/NetworkRestrictions/AddRestrictionModal.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx similarity index 88% rename from studio/components/interfaces/Settings/General/NetworkRestrictions/AddRestrictionModal.tsx rename to studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx index 6b93314d01a..94ecb1fabf4 100644 --- a/studio/components/interfaces/Settings/General/NetworkRestrictions/AddRestrictionModal.tsx +++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx @@ -4,6 +4,7 @@ import { Alert, Button, Form, Input, Modal } from 'ui' import { useStore, useParams } from 'hooks' import { useNetworkRestrictionsApplyMutation } from 'data/network-restrictions/network-retrictions-apply-mutation' +import InformationBox from 'components/ui/InformationBox' interface Props { restrictedIps: string[] @@ -77,7 +78,7 @@ const AddRestrictionModal: FC = ({ = ({ validate={validate} onSubmit={onSubmit} > - {({ isSubmitting }: any) => { + {({ isSubmitting }: { isSubmitting: boolean }) => { return ( <> @@ -99,6 +100,12 @@ const AddRestrictionModal: FC = ({ This will add an IP address range to a list of allowed ranges that can access your project's database.

+ = ({ visible, onClose }) => { = ({ visible, onClose }) => { This will prevent any IP address other than your local machine from accessing your project's database. Are you sure?

+
diff --git a/studio/components/interfaces/Settings/General/NetworkRestrictions/NetworkRestrictions.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/NetworkRestrictions.tsx similarity index 94% rename from studio/components/interfaces/Settings/General/NetworkRestrictions/NetworkRestrictions.tsx rename to studio/components/interfaces/Settings/Database/NetworkRestrictions/NetworkRestrictions.tsx index bd6f745867a..2e074e77c5c 100644 --- a/studio/components/interfaces/Settings/General/NetworkRestrictions/NetworkRestrictions.tsx +++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/NetworkRestrictions.tsx @@ -116,8 +116,9 @@ const NetworkRestrictions = ({}) => { accessed externally

- Only your local machine is able to access your project's database as all IP - addresses have been disallowed. + Only IP addresses within the range of{' '} + 127.0.0.1/32 can access your project's + database as all IP addresses have been disallowed.

@@ -138,6 +139,10 @@ const NetworkRestrictions = ({}) => { You may remove all of them to allow all IP addresses to have access to your database

+

+ Note: Restrictions only apply to your database, and not to APIs offered over + HTTPS +

diff --git a/studio/package.json b/studio/package.json index 4bcbb3b9072..fa76b3dcf83 100644 --- a/studio/package.json +++ b/studio/package.json @@ -45,6 +45,7 @@ "generate-password": "^1.7.0", "html-to-image": "^1.10.8", "immutability-helper": "^3.1.1", + "ip-address": "^8.1.0", "ipaddr.js": "^2.0.1", "json-logic-js": "^2.0.2", "lodash": "^4.17.21", From f8edb924a1f78b69210210c6525d50d063accd5c Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 30 Jan 2023 17:18:43 +0800 Subject: [PATCH 17/24] Replace ipaddr.js with ip-address module --- package-lock.json | 17 +----- .../AddRestrictionModal.tsx | 59 ++++--------------- .../NetworkRestrictions.utils.ts | 22 +++++++ studio/package.json | 1 - 4 files changed, 36 insertions(+), 63 deletions(-) create mode 100644 studio/components/interfaces/Settings/Database/NetworkRestrictions/NetworkRestrictions.utils.ts diff --git a/package-lock.json b/package-lock.json index e5700845757..29773e5561f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38353,7 +38353,6 @@ "html-to-image": "^1.10.8", "immutability-helper": "^3.1.1", "ip-address": "^8.1.0", - "ipaddr.js": "^2.0.1", "json-logic-js": "^2.0.2", "lodash": "^4.17.21", "lottie-react": "^2.2.1", @@ -38685,14 +38684,6 @@ "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" }, - "studio/node_modules/ipaddr.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", - "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", - "engines": { - "node": ">= 10" - } - }, "studio/node_modules/openapi-types": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-9.3.1.tgz", @@ -64925,8 +64916,7 @@ "generate-password": "^1.7.0", "html-to-image": "^1.10.8", "immutability-helper": "^3.1.1", - "ip-address": "*", - "ipaddr.js": "^2.0.1", + "ip-address": "^8.1.0", "jest": "^27.4.0", "jest-canvas-mock": "^2.3.1", "json-logic-js": "^2.0.2", @@ -65156,11 +65146,6 @@ "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" }, - "ipaddr.js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", - "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==" - }, "openapi-types": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-9.3.1.tgz", diff --git a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx index 0edccfe8841..16efbdf24be 100644 --- a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx +++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx @@ -1,12 +1,12 @@ import { FC } from 'react' import { Address4 } from 'ip-address' -import ipaddr from 'ipaddr.js' import { Button, Form, IconHelpCircle, Input, Modal } from 'ui' import * as Tooltip from '@radix-ui/react-tooltip' import { useStore, useParams } from 'hooks' -import { useNetworkRestrictionsApplyMutation } from 'data/network-restrictions/network-retrictions-apply-mutation' import InformationBox from 'components/ui/InformationBox' +import { checkIfPrivate, getAddressEndRange } from './NetworkRestrictions.utils' +import { useNetworkRestrictionsApplyMutation } from 'data/network-restrictions/network-retrictions-apply-mutation' interface Props { restrictedIps: string[] @@ -26,57 +26,24 @@ const AddRestrictionModal: FC = ({ const { ref } = useParams() const { mutateAsync: applyNetworkRestrictions } = useNetworkRestrictionsApplyMutation() - const getAddressEndRange = (cidr: string) => { - try { - const address = new Address4(cidr) - return { start: address.startAddress().address, end: address.endAddress().address } - } catch (error: any) { - return undefined - } - } - const validate = (values: any) => { const errors: any = {} const { ipAddress, cidrBlockSize } = values - if (cidrBlockSize < 0 || cidrBlockSize > 32) { - errors.cidrBlockSize = 'Size has to be between 0 to 32' - } + // Validate CIDR block size + const isOutOfCidrSizeRange = cidrBlockSize < 0 || cidrBlockSize > 32 + if (isOutOfCidrSizeRange) errors.cidrBlockSize = 'Size has to be between 0 to 32' - try { - const address = ipaddr.parse(ipAddress) - const isIpv6 = address.kind() === 'ipv6' - const isPrivate = address.range() === 'private' - - if (isIpv6) errors.ipAddress = 'Only IPv4 addresses are supported at the moment' - else if (isPrivate) errors.ipAddress = 'Private IP addresses are not supported' - } catch (error: any) { + // Validate IP address + const isValid = Address4.isValid(ipAddress) + if (!isValid) { errors.ipAddress = 'Please enter a valid IP address' + return errors } - // try { - // // Check if already exists - // const alreadyExists = restrictedIps.includes(values.address) - // if (alreadyExists) { - // errors.address = 'This restriction already exists' - // return errors - // } - // // Check if valid IP address - // const [address, range] = ipaddr.IPv4.parseCIDR(values.address) - // // Check if private range - // const isPrivate = ipaddr.parse(address.octets.join('.')).range() === 'private' - // if (isPrivate) errors.address = 'Private IP addresses are not supported' - // } catch (error: any) { - // if (error.message.includes('string is not formatted like an IPv4 CIDR range')) { - // try { - // // Check if IPv6 format - // ipaddr.IPv6.parseCIDR(values.address) - // errors.address = 'Only IPv4 addresses are supported at the moment' - // } catch (error: any) { - // errors.address = 'Please enter a valid address in CIDR notation' - // } - // } - // } + const isPrivate = checkIfPrivate(ipAddress) + if (isPrivate) errors.ipAddress = 'Private IP addresses are not supported' + return errors } @@ -129,7 +96,7 @@ const AddRestrictionModal: FC = ({

This will add an IP address range to a list of allowed ranges that can access - your project's database. + your database. Only IPv4 addresses are supported at the moment.

{ + const address = new Address4(cidr) + const res = privateIPRanges.map((range) => address.isInSubnet(range)) + return res.includes(true) +} + +export const getAddressEndRange = (cidr: string) => { + try { + const address = new Address4(cidr) + return { start: address.startAddress().address, end: address.endAddress().address } + } catch (error: any) { + return undefined + } +} diff --git a/studio/package.json b/studio/package.json index fa76b3dcf83..1e7e8d7ea37 100644 --- a/studio/package.json +++ b/studio/package.json @@ -46,7 +46,6 @@ "html-to-image": "^1.10.8", "immutability-helper": "^3.1.1", "ip-address": "^8.1.0", - "ipaddr.js": "^2.0.1", "json-logic-js": "^2.0.2", "lodash": "^4.17.21", "lottie-react": "^2.2.1", From e1409501a17d6bb82b03b82a64df925d0409a2fd Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 31 Jan 2023 14:20:21 +0800 Subject: [PATCH 18/24] Update copy writing --- .../NetworkRestrictions/AddRestrictionModal.tsx | 2 +- .../NetworkRestrictions/DisallowAllModal.tsx | 5 +++-- .../NetworkRestrictions/NetworkRestrictions.tsx | 13 +++++++------ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx index 16efbdf24be..fcf9d63cc30 100644 --- a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx +++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx @@ -102,7 +102,7 @@ const AddRestrictionModal: FC = ({ defaultVisibility hideCollapse title="Note: Restrictions only apply to your database and PgBouncer" - description="They do not currently apply to APIs offered over HTTPS, such as PostgREST, Storage, or Authentication" + description="They do not currently apply to Supabase services such as PostgREST, Storage, or Authentication" />
diff --git a/studio/components/interfaces/Settings/Database/NetworkRestrictions/DisallowAllModal.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/DisallowAllModal.tsx index 61010018a82..0f5d8cb20e5 100644 --- a/studio/components/interfaces/Settings/Database/NetworkRestrictions/DisallowAllModal.tsx +++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/DisallowAllModal.tsx @@ -45,8 +45,9 @@ const DisallowAllModal: FC = ({ visible, onClose }) => {

- This will prevent any IP address other than your local machine from accessing your - project's database. Are you sure? + This will prevent any IP addresses other than those within the range of{' '} + 127.0.0.1/32 from accessing your project's database. + Are you sure?

{ Your database cannot be accessed externally

-

- Only IP addresses within the range of{' '} - 127.0.0.1/32 can access your project's - database as all IP addresses have been disallowed. +

+ All external IP addresses have been disallowed from accessing your project's + database. +

+

+ Note: Restrictions only apply to your database, and not to Supabase services

@@ -140,8 +142,7 @@ const NetworkRestrictions = ({}) => { database

- Note: Restrictions only apply to your database, and not to APIs offered over - HTTPS + Note: Restrictions only apply to your database, and not to Supabase services

From 4efe4b1bd01a194bb8763df709e68a0c1cb0c6f2 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 31 Jan 2023 16:21:14 +0800 Subject: [PATCH 19/24] Add additional meta in add restriction modal --- .../AddRestrictionModal.tsx | 54 +++++++++++++------ 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx index fcf9d63cc30..b5c9c4ac695 100644 --- a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx +++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx @@ -85,11 +85,16 @@ const AddRestrictionModal: FC = ({ onSubmit={onSubmit} > {({ isSubmitting, values }: { isSubmitting: boolean; values: any }) => { + const isPrivate = Address4.isValid(values.ipAddress) + ? checkIfPrivate(values.ipAddress) + : false const isValidBlockSize = values.cidrBlockSize !== '' && values.cidrBlockSize >= 0 && values.cidrBlockSize <= 32 const availableAddresses = Math.pow(2, 32 - (values?.cidrBlockSize ?? 0)) const addressRange = getAddressEndRange(`${values.ipAddress}/${values.cidrBlockSize}`) + const isValidCIDR = isValidBlockSize && !isPrivate && addressRange !== undefined + return ( <> @@ -99,8 +104,6 @@ const AddRestrictionModal: FC = ({ your database. Only IPv4 addresses are supported at the moment.

@@ -111,6 +114,7 @@ const AddRestrictionModal: FC = ({ id="ipAddress" name="ipAddress" placeholder="0.0.0.0" + className="" />
@@ -150,22 +154,40 @@ const AddRestrictionModal: FC = ({ />
- {isValidBlockSize && ( -
-

- Number of addresses: {availableAddresses} -

- {addressRange !== undefined && ( -

- Selected address space:{' '} - {addressRange.start} to{' '} - {addressRange.end} -

- )} -
- )}
+ + {isValidCIDR ? ( + +
+

+ The address range{' '} + + {values.ipAddress}/{values.cidrBlockSize} + {' '} + will be restricted +

+

+ Selected address space: {addressRange.start}{' '} + to {addressRange.end}{' '} +

+

+ Number of addresses: {availableAddresses} +

+
+
+ ) : ( + +
+
+

+ A summary of your restriction will be shown here after entering a valid IP + address and CIDR block size +

+
+
+
+ )}
+

getClientIpAddress()} + > + Use my IP address +

From b34d8992f7b4336f662ccc885bc6b01be1330a06 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 1 Feb 2023 02:02:24 +0800 Subject: [PATCH 21/24] Add validation for existing restriction --- .../NetworkRestrictions/AddRestrictionModal.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx index 3aa1a87a3eb..1187bcc8a3b 100644 --- a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx +++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx @@ -53,6 +53,14 @@ const AddRestrictionModal: FC = ({ setSubmitting(true) const cidr = `${values.ipAddress}/${values.cidrBlockSize}` + const alreadyExists = restrictedIps.includes(cidr) + if (alreadyExists) { + return ui.setNotification({ + category: 'info', + message: `The address ${cidr} is already restricted`, + }) + } + const dbAllowedCidrs = hasOverachingRestriction ? [cidr] : [...restrictedIps, cidr] try { @@ -172,7 +180,7 @@ const AddRestrictionModal: FC = ({

getClientIpAddress()} > Use my IP address From e9682bfc49fea33bb2d12ea37398805b54de4714 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 1 Feb 2023 11:36:59 +0800 Subject: [PATCH 22/24] Update copy --- .../Database/NetworkRestrictions/DisallowAllModal.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/studio/components/interfaces/Settings/Database/NetworkRestrictions/DisallowAllModal.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/DisallowAllModal.tsx index 0f5d8cb20e5..537d509515e 100644 --- a/studio/components/interfaces/Settings/Database/NetworkRestrictions/DisallowAllModal.tsx +++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/DisallowAllModal.tsx @@ -45,9 +45,8 @@ const DisallowAllModal: FC = ({ visible, onClose }) => {

- This will prevent any IP addresses other than those within the range of{' '} - 127.0.0.1/32 from accessing your project's database. - Are you sure? + This will prevent any external IP addresses from accessing your project's database. Are + you sure?

Date: Wed, 1 Feb 2023 11:38:14 +0800 Subject: [PATCH 23/24] Remove template readme --- studio/data/network-restrictions/README.md | 49 ---------------------- 1 file changed, 49 deletions(-) delete mode 100644 studio/data/network-restrictions/README.md diff --git a/studio/data/network-restrictions/README.md b/studio/data/network-restrictions/README.md deleted file mode 100644 index 05a0cabfb4f..00000000000 --- a/studio/data/network-restrictions/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# Templates for Tanstack Query queries & mutations - -## Usage - -1. Duplicate the files in the \_\_templates directory you need depending on your use case. -2. Rename the files to match your resource name. -3. Update the `resource` variables to match your resource name. Usually find and replace works great here if you use case sensitive search (`resource` then `Resource`) -4. Implement your `queryFn` function. - -Referencing the other files in the `data` directory is a good way to see how to use the library. - -### SQL queries and mutations - -The dashboard often needs to query the users database directly. There are already some reusable hooks for this in `data/sql`. Reference the `data/fdw` directory for an example of how to use them. - -Note that the query key of the `useExecuteSqlQuery` hook will be automatically filled with an md5 hash of the SQL query, unless you provide a name for the query. - -## Files - -### `resources-query.ts` - -For queries that return a list of results. - -> :warning: Should only be used for simple lists, as it does not include any pagination. `useInfiniteQuery` should be used instead. - -### `resource-query.ts` - -For getting a single item based on parameter(s). `id` is used in this example but can be updated to whatever is needed. - -### `resource-update-mutation.ts` - -For updating a resource. This can easily be adapted to work for create, delete, or any verb that is needed. You may need to modify the `invalidateQueries` section depending on your use case. For example removing: - -```ts -queryClient.invalidateQueries(resourceKeys.resource(projectRef, id)) -``` - -if you are creating a new resource. - -### `keys.ts` - -Standardized keys for use with `useQuery` and `invalidateQueries`. This is not required but is a good way to keep things consistent. - -## Learning Resources - -- [Tanstack Query Docs](https://tanstack.com/query/v4/docs/react/overview) -- [TkDodo's Blog Posts](https://tanstack.com/query/v4/docs/react/community/tkdodos-blog) - -The blog posts in particular are very helpful for learning best practices and how to use the library effectively. From 62dd4fdbf99d037cb2ca1e79b3ad07a69150db7c Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 1 Feb 2023 14:11:04 +0800 Subject: [PATCH 24/24] Update --- .../NetworkRestrictions/AddRestrictionModal.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx index 1187bcc8a3b..c3039d22494 100644 --- a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx +++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx @@ -1,4 +1,4 @@ -import { FC } from 'react' +import { FC, useState } from 'react' import { Address4 } from 'ip-address' import { Button, Form, IconHelpCircle, Input, Modal } from 'ui' import * as Tooltip from '@radix-ui/react-tooltip' @@ -104,7 +104,10 @@ const AddRestrictionModal: FC = ({ const isValidCIDR = isValidBlockSize && !isPrivate && addressRange !== undefined + const [isFetchingAddress, setIsFetchingAddress] = useState(false) + const getClientIpAddress = async () => { + setIsFetchingAddress(true) const res = await fetch('http://www.geoplugin.net/json.gp', { method: 'GET' }) const { geoplugin_request } = await res.json() @@ -118,6 +121,7 @@ const AddRestrictionModal: FC = ({ message: 'Failed to retrieve client IP address, please enter address manually', }) } + setIsFetchingAddress(false) } return ( @@ -142,7 +146,7 @@ const AddRestrictionModal: FC = ({ className="" />
-
+
@@ -179,12 +183,14 @@ const AddRestrictionModal: FC = ({ />
-

getClientIpAddress()} > Use my IP address -

+