+
+ >
+ )
+}
+
+export default APIAuthorizationLayout
diff --git a/studio/data/api-authorization/api-authorization-approve-mutation.ts b/studio/data/api-authorization/api-authorization-approve-mutation.ts
new file mode 100644
index 00000000000..5654041d540
--- /dev/null
+++ b/studio/data/api-authorization/api-authorization-approve-mutation.ts
@@ -0,0 +1,43 @@
+import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query'
+import { post } from 'lib/common/fetch'
+import { API_ADMIN_URL } from 'lib/constants'
+// import { resourceKeys } from './keys'
+
+export type ApiAuthorizationApproveVariables = {
+ id: string
+}
+
+export async function approveApiAuthorization({ id }: ApiAuthorizationApproveVariables) {
+ if (!id) throw new Error('Authorization ID is required')
+
+ const response = await post(
+ `${API_ADMIN_URL}/oauth/authorization/${id}?skip_browser_redirect=true`,
+ {}
+ )
+ if (response.error) throw response.error
+ return response
+}
+
+type ApiAuthorizationApproveData = Awaited>
+
+export const useApiAuthorizationApproveMutation = ({
+ onSuccess,
+ ...options
+}: Omit<
+ UseMutationOptions,
+ 'mutationFn'
+> = {}) => {
+ const queryClient = useQueryClient()
+
+ return useMutation(
+ (vars) => approveApiAuthorization(vars),
+ {
+ async onSuccess(data, variables, context) {
+ // const { id } = variables
+ // await queryClient.invalidateQueries(networkRestrictionKeys.list(projectRef))
+ // await onSuccess?.(data, variables, context)
+ },
+ ...options,
+ }
+ )
+}
diff --git a/studio/data/api-authorization/api-authorization-decline-mutation.ts b/studio/data/api-authorization/api-authorization-decline-mutation.ts
new file mode 100644
index 00000000000..f623f7d2905
--- /dev/null
+++ b/studio/data/api-authorization/api-authorization-decline-mutation.ts
@@ -0,0 +1,40 @@
+import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query'
+import { delete_ } from 'lib/common/fetch'
+import { API_ADMIN_URL } from 'lib/constants'
+// import { resourceKeys } from './keys'
+
+export type ApiAuthorizationDeclineVariables = {
+ id: string
+}
+
+export async function declineApiAuthorization({ id }: ApiAuthorizationDeclineVariables) {
+ if (!id) throw new Error('Authorization ID is required')
+
+ const response = await delete_(`${API_ADMIN_URL}/oauth/authorization/${id}`, {})
+ if (response.error) throw response.error
+ return response
+}
+
+type ApiAuthorizationDeclineData = Awaited>
+
+export const useApiAuthorizationDeclineMutation = ({
+ onSuccess,
+ ...options
+}: Omit<
+ UseMutationOptions,
+ 'mutationFn'
+> = {}) => {
+ const queryClient = useQueryClient()
+
+ return useMutation(
+ (vars) => declineApiAuthorization(vars),
+ {
+ async onSuccess(data, variables, context) {
+ // const { id } = variables
+ // await queryClient.invalidateQueries(networkRestrictionKeys.list(projectRef))
+ // await onSuccess?.(data, variables, context)
+ },
+ ...options,
+ }
+ )
+}
diff --git a/studio/data/api-authorization/api-authorization-query.ts b/studio/data/api-authorization/api-authorization-query.ts
new file mode 100644
index 00000000000..f687e49c403
--- /dev/null
+++ b/studio/data/api-authorization/api-authorization-query.ts
@@ -0,0 +1,52 @@
+import { useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query'
+import { get } from 'lib/common/fetch'
+import { API_ADMIN_URL } from 'lib/constants'
+import { useCallback } from 'react'
+import { resourceKeys } from './keys'
+
+export type ApiAuthorizationVariables = {
+ id?: string
+}
+
+export type ApiAuthorizationResponse = {
+ id: string
+}
+
+export async function getApiAuthorizationDetails(
+ { id }: ApiAuthorizationVariables,
+ signal?: AbortSignal
+) {
+ if (!id) throw new Error('Authorization ID is required')
+
+ const response = await get(`${API_ADMIN_URL}/oauth/authorization/${id}`, { signal })
+ if (response.error) throw response.error
+ return response as ApiAuthorizationResponse
+}
+
+export type ResourceData = Awaited>
+export type ResourceError = unknown
+
+export const useApiAuthorizationQuery = (
+ { id }: ApiAuthorizationVariables,
+ { enabled = true, ...options }: UseQueryOptions = {}
+) =>
+ useQuery(
+ resourceKeys.resource(id),
+ ({ signal }) => getApiAuthorizationDetails({ id }, signal),
+ {
+ enabled: enabled && typeof id !== 'undefined',
+ ...options,
+ }
+ )
+
+export const useApiAuthorizationPrefetch = ({ id }: ApiAuthorizationVariables) => {
+ const client = useQueryClient()
+
+ return useCallback(() => {
+ if (id) {
+ client.prefetchQuery(resourceKeys.resource(id), ({ signal }) =>
+ getApiAuthorizationDetails({ id }, signal)
+ )
+ }
+ }, [id])
+}
diff --git a/studio/data/api-authorization/keys.ts b/studio/data/api-authorization/keys.ts
new file mode 100644
index 00000000000..69660c24f42
--- /dev/null
+++ b/studio/data/api-authorization/keys.ts
@@ -0,0 +1,3 @@
+export const resourceKeys = {
+ resource: (id: string | undefined) => ['api-authorization', id] as const,
+}
diff --git a/studio/data/organizations/keys.ts b/studio/data/organizations/keys.ts
index 5b7250c028d..4aabe753ab3 100644
--- a/studio/data/organizations/keys.ts
+++ b/studio/data/organizations/keys.ts
@@ -1,4 +1,5 @@
export const organizationKeys = {
+ list: () => ['organizations'] as const,
detail: (slug: string | undefined) => ['organizations', slug, 'detail'] as const,
roles: (slug: string | undefined) => ['organizations', slug, 'roles'] as const,
freeProjectLimitCheck: (slug: string | undefined) =>
diff --git a/studio/data/organizations/organizations-query.ts b/studio/data/organizations/organizations-query.ts
new file mode 100644
index 00000000000..598c8b17df4
--- /dev/null
+++ b/studio/data/organizations/organizations-query.ts
@@ -0,0 +1,37 @@
+import { useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query'
+import { get } from 'lib/common/fetch'
+import { API_URL } from 'lib/constants'
+import { useCallback } from 'react'
+import { Organization } from 'types'
+import { organizationKeys } from './keys'
+
+export type OrganizationsResponse = Organization[]
+
+export async function getOrganizations(signal?: AbortSignal) {
+ const data = await get(`${API_URL}/organizations`, { signal })
+ if (data.error) throw data.error
+
+ return data as OrganizationsResponse
+}
+
+export type OrganizationsData = Awaited>
+export type OrganizationsError = unknown
+
+export const useOrganizationsQuery = ({
+ enabled = true,
+ ...options
+}: UseQueryOptions = {}) =>
+ useQuery(
+ organizationKeys.list(),
+ ({ signal }) => getOrganizations(signal),
+ { enabled: enabled, ...options }
+ )
+
+export const useOrganizationsPrefetch = () => {
+ const client = useQueryClient()
+
+ return useCallback(
+ () => client.prefetchQuery(organizationKeys.list(), ({ signal }) => getOrganizations(signal)),
+ []
+ )
+}
diff --git a/studio/pages/authorize.tsx b/studio/pages/authorize.tsx
new file mode 100644
index 00000000000..9d4e3aed96f
--- /dev/null
+++ b/studio/pages/authorize.tsx
@@ -0,0 +1,96 @@
+import { useParams } from 'common'
+
+import { withAuth } from 'hooks'
+import { NextPageWithLayout } from 'types'
+import { FormPanel } from 'components/ui/Forms'
+import APIAuthorizationLayout from 'components/layouts/APIAuthorizationLayout'
+import { Alert, Button, Listbox } from 'ui'
+import { useOrganizationsQuery } from 'data/organizations/organizations-query'
+import ShimmeringLoader from 'components/ui/ShimmeringLoader'
+import { useApiAuthorizationQuery } from 'data/api-authorization/api-authorization-query'
+
+const APIAuthorizationPage: NextPageWithLayout = () => {
+ const { auth_id } = useParams()
+ const { data: organizations, isLoading: isLoadingOrganizations } = useOrganizationsQuery()
+ // const { data: apiAuthDetails, isLoading, isSuccess, isError } = useApiAuthorizationQuery({ id: auth_id })
+
+ // [Joshen] To be replaced with actual API call above
+ const apiAuthDetails = {
+ icon: 'https://cdn-icons-png.flaticon.com/512/5969/5969044.png',
+ host: 'cloudflare.com',
+ name: 'Cloudflare',
+ }
+
+ const isExpired = false
+
+ return (
+
+
+
+
+
+
+ }
+ >
+
+ {/* API Authorization requester details */}
+
+
Authorize {apiAuthDetails.name}
+
+
+
+
+
+
+
+ {apiAuthDetails.name} is requesting API access to an organization. The application
+ will be able to{' '}
+
+ read and write the organization's settings and projects
+
+
- {apiAuthDetails.name} is requesting API access to an organization. The application
- will be able to{' '}
-
- read and write the organization's settings and projects
-
-
+
+ {apiAuthDetails.name} is requesting API access to an organization. The application will
+ be able to{' '}
+
+ read and write the organization's settings and projects
+
+
{/* Expiry warning */}
From 63267c1fa9fe41d5c30aeec2ccccc9b624d9cdbc Mon Sep 17 00:00:00 2001
From: Joshen Lim
Date: Tue, 25 Apr 2023 17:26:41 +0800
Subject: [PATCH 03/70] Scaffold error states to be handled
---
studio/pages/authorize.tsx | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/studio/pages/authorize.tsx b/studio/pages/authorize.tsx
index c41f134f31f..6161eca20ec 100644
--- a/studio/pages/authorize.tsx
+++ b/studio/pages/authorize.tsx
@@ -21,8 +21,38 @@ const APIAuthorizationPage: NextPageWithLayout = () => {
name: 'Cloudflare',
}
+ // [Joshen] To be replaced with actual API call above
+ const isApiAuthDetailsError = false
const isExpired = false
+ if (auth_id === undefined) {
+ return (
+ Authorization for API access}>
+
+
+ Please provide a valid authorization ID in the URL
+
+
+
+ )
+ }
+
+ if (isApiAuthDetailsError) {
+ return (
+ Authorization for API access}>
+
+
+ Please retry your authorization request from the requesting app
+
+
+
+ )
+ }
+
return (
Authorize API access for {apiAuthDetails.name}}
From f13c7299238e8a3102ebf49d45a41e7f93e1bf72 Mon Sep 17 00:00:00 2001
From: Francesco Sansalvadore
Date: Wed, 3 May 2023 15:42:56 +0200
Subject: [PATCH 04/70] chore: add analytics event to hp herop
---
apps/www/components/Hero.tsx | 23 +++++++++++--
apps/www/hooks/useGoogleAnalyticsProps.tsx | 13 ++++++++
apps/www/lib/constants.ts | 2 ++
apps/www/lib/fetchWrapper.ts | 4 +--
apps/www/lib/gaEvents.ts | 20 ++++++++++++
apps/www/lib/telemetry.ts | 38 ++++++++++++++++++++++
apps/www/pages/_app.tsx | 2 +-
7 files changed, 95 insertions(+), 7 deletions(-)
create mode 100644 apps/www/hooks/useGoogleAnalyticsProps.tsx
create mode 100644 apps/www/lib/gaEvents.ts
create mode 100644 apps/www/lib/telemetry.ts
diff --git a/apps/www/components/Hero.tsx b/apps/www/components/Hero.tsx
index e97cd31fa74..5557da55251 100644
--- a/apps/www/components/Hero.tsx
+++ b/apps/www/components/Hero.tsx
@@ -1,10 +1,18 @@
-import { Button, IconBookOpen, Space } from 'ui'
+import { Button, IconBookOpen } from 'ui'
import Link from 'next/link'
import { useRouter } from 'next/router'
import SectionContainer from './Layouts/SectionContainer'
+import Telemetry, { GoogleAnalyticsEvent } from '~/lib/telemetry'
+import gaEvents from '~/lib/gaEvents'
+import useGoogleAnalyticsProps from '~/hooks/useGoogleAnalyticsProps'
const Hero = () => {
const { basePath } = useRouter()
+ const googleAnalyticsProps = useGoogleAnalyticsProps()
+
+ const handleClick = (event: GoogleAnalyticsEvent) => {
+ Telemetry.sendEvent(event, googleAnalyticsProps)
+ }
return (
diff --git a/apps/www/lib/constants.ts b/apps/www/lib/constants.ts
index 1b7537a438d..5586bb59f81 100644
--- a/apps/www/lib/constants.ts
+++ b/apps/www/lib/constants.ts
@@ -1,7 +1,7 @@
export const APP_NAME = 'Supabase'
export const DESCRIPTION = 'The Open Source Alternative to Firebase.'
export const IS_PROD = process.env.NEXT_PUBLIC_VERCEL_ENV === 'production'
-export const API_URL = !IS_PROD ? process.env.NEXT_PUBLIC_API_URL : '/api'
+export const API_URL = IS_PROD ? process.env.NEXT_PUBLIC_API_URL : '/api'
// Launch Weeek
export const SAMPLE_TICKET_NUMBER = 1234
From 9952fec9d9ebd9e5edea58f00851670b249b510d Mon Sep 17 00:00:00 2001
From: Andrew Smith
Date: Wed, 3 May 2023 22:58:47 +0000
Subject: [PATCH 06/70] Add password reset guide to the auth section
---
.../NavigationMenu.constants.ts | 1 +
.../pages/guides/auth/auth-password-reset.mdx | 104 ++++++++++++++++++
2 files changed, 105 insertions(+)
create mode 100644 apps/docs/pages/guides/auth/auth-password-reset.mdx
diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
index 78292feff7e..d2d9a91bfe6 100644
--- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
+++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
@@ -295,6 +295,7 @@ export const auth = {
},
],
},
+ { name: 'Password Reset', url: '/guides/auth/auth-password-reset' },
{ name: 'Email Templates', url: '/guides/auth/auth-email-templates' },
],
},
diff --git a/apps/docs/pages/guides/auth/auth-password-reset.mdx b/apps/docs/pages/guides/auth/auth-password-reset.mdx
new file mode 100644
index 00000000000..88664550455
--- /dev/null
+++ b/apps/docs/pages/guides/auth/auth-password-reset.mdx
@@ -0,0 +1,104 @@
+import Layout from '~/layouts/DefaultGuideLayout'
+import { Accordion } from 'ui'
+
+export const meta = {
+ id: 'auth-password-reset',
+ title: 'Password Reset',
+ description: 'How to reset your password for your Supabase project',
+}
+
+- Send email for password reset using [`.resetPasswordForEmail`](/docs/reference/javascript/auth-resetpasswordforemail) while providing a `redirectTo` parameter
+- Email link will work as a magic link and log the user in then take them to the url specified in the `redirectTo` parameter
+- Create form to update the password and call the [`.updateUser`](/docs/reference/javascript/auth-updateuser) method with the new password
+
+## Single Page Application (SPA)
+
+### Sending password reset email
+Supabase provides a convenient method [`.resetPasswordForEmail`](/docs/reference/javascript/auth-resetpasswordforemail)
+to reset a user password. This method takes a parameter of `redirectTo` which we will use to pass an absolute URL to
+the update password page. This url must be saved in your allowed [Redirect URLs](https://app.supabase.com/project/sgbluorgiylcyqpqihab/auth/url-configuration)
+list or it won't work.
+
+```ts
+await supabase.auth.resetPasswordForEmail("hello@example.com", {
+ redirectTo: 'http://example.com/account/update-password'
+})
+```
+
+### Email link
+The email link you receive will work like a magic link. This way when you click the link you will be logged into
+the website. Since we passed a redirect URL to the [`.resetPasswordForEmail`](https://supabase.com/docs/reference/javascript/auth-resetpasswordforemail)
+method the user should be sent to the update password page.
+
+### Update Password
+To update the password we call the [`.updateUser`](/docs/reference/javascript/auth-updateuser) method and pass along the new password to this method.
+
+```ts
+await supabase.auth.updateUser({ password: new_password })
+```
+
+## Server-Side Rendering (SSR)
+
+### Sending password reset email
+Supabase provides a convenient method [`.resetPasswordForEmail`](/docs/reference/javascript/auth-resetpasswordforemail)
+to reset a user password. This method takes a parameter of `redirectTo` which we will use to pass an absolute URL to
+the callback page along with a query parameter to the update password page. This URL must be saved in your
+allowed [Redirect URLs](https://app.supabase.com/project/_/auth/url-configuration) list found at
+[Authentication > Redirect Configuration](https://app.supabase.com/project/_/auth/url-configuration) or it won't
+redirect the user.
+
+```ts
+await supabase.auth.resetPasswordForEmail("hello@example.com", {
+ redirectTo: 'http://example.com/auth/callback?next=/account/update-password'
+})
+```
+
+
+
+We are using `next` as our query parameter but this is just personal preference, you could
+use a different query parameter name.
+
+
+
+### Email link
+The email link you receive will behave like a magic link. When the link is clicked you will be sent to the `redirectTo` URL you specified that points to the path with the code exchange code.
+
+### Exchange authorization code
+After getting redirected to the server page we need to retrieve the code from the query parameter called `code` and pass it to the `.exchangeAuthCodeForSession` function.
+```ts
+// code can be retrieved using your framework of choice way of getting query parameter from the url.
+// This is just example code
+const code = url.searchParams.get('code')
+
+// call the Supabase API to exchange the code for a session
+await supabase.auth.exchangeCodeForSession(code)
+```
+
+
+
+The query parameter is always `code` for the authorization code returned from the Supabase API
+
+
+
+We will also need to check for the `next` query parameter to redirect the user to the update password page.
+```ts
+// password page path can be retrieved using your framework of choice way of getting query parameter from the url.
+// This is just example code
+const next = url.searchParams.get('next')
+
+// using NextJS response object in this example
+res.redirect(next)
+```
+
+### Update Password
+To update the password we call the [`.updateUser`](/docs/reference/javascript/auth-updateuser) method and
+pass along the new password to this method.
+
+```ts
+await supabase.auth.updateUser({ password: new_password })
+```
+
+export const Page = ({ children }) =>
+
+export default Page
+
From 3ab93886d9d6d17c5c2cd27ae5f0fd1fa70c5dd2 Mon Sep 17 00:00:00 2001
From: Andrew Smith
Date: Wed, 3 May 2023 23:23:46 +0000
Subject: [PATCH 07/70] Update code snippets with filename
---
apps/docs/pages/guides/auth/auth-password-reset.mdx | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/apps/docs/pages/guides/auth/auth-password-reset.mdx b/apps/docs/pages/guides/auth/auth-password-reset.mdx
index 88664550455..9bd052a0345 100644
--- a/apps/docs/pages/guides/auth/auth-password-reset.mdx
+++ b/apps/docs/pages/guides/auth/auth-password-reset.mdx
@@ -49,7 +49,7 @@ redirect the user.
```ts
await supabase.auth.resetPasswordForEmail("hello@example.com", {
- redirectTo: 'http://example.com/auth/callback?next=/account/update-password'
+ redirectTo: 'http://example.com/api/auth/callback?next=/account/update-password'
})
```
@@ -66,6 +66,8 @@ The email link you receive will behave like a magic link. When the link is click
### Exchange authorization code
After getting redirected to the server page we need to retrieve the code from the query parameter called `code` and pass it to the `.exchangeAuthCodeForSession` function.
```ts
+// api/auth/callback.ts
+
// code can be retrieved using your framework of choice way of getting query parameter from the url.
// This is just example code
const code = url.searchParams.get('code')
@@ -82,6 +84,8 @@ The query parameter is always `code` for the authorization code returned from th
We will also need to check for the `next` query parameter to redirect the user to the update password page.
```ts
+// api/auth/callback.ts
+
// password page path can be retrieved using your framework of choice way of getting query parameter from the url.
// This is just example code
const next = url.searchParams.get('next')
From 5e2dca6cc07904482590eefb7e07e2ddcd3c8a9a Mon Sep 17 00:00:00 2001
From: Francesco Sansalvadore
Date: Wed, 3 May 2023 16:19:51 +0200
Subject: [PATCH 08/70] chore: update API_URL preview check
---
apps/www/components/Hero.tsx | 5 ++---
apps/www/lib/constants.ts | 3 ++-
apps/www/lib/gaEvents.ts | 4 ++--
apps/www/lib/telemetry.ts | 4 ++--
4 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/apps/www/components/Hero.tsx b/apps/www/components/Hero.tsx
index 53df050350e..32f699d2939 100644
--- a/apps/www/components/Hero.tsx
+++ b/apps/www/components/Hero.tsx
@@ -1,5 +1,4 @@
import { Button, IconBookOpen } from 'ui'
-import Link from 'next/link'
import { useRouter } from 'next/router'
import SectionContainer from './Layouts/SectionContainer'
import Telemetry, { GoogleAnalyticsEvent } from '~/lib/telemetry'
@@ -10,8 +9,8 @@ const Hero = () => {
const router = useRouter()
const googleAnalyticsProps = useGoogleAnalyticsProps()
- const trackAndNavigate = (url: string, event: GoogleAnalyticsEvent) => {
- Telemetry.sendEvent(event, googleAnalyticsProps)
+ const trackAndNavigate = async (url: string, event: GoogleAnalyticsEvent) => {
+ await Telemetry.sendEvent(event, googleAnalyticsProps)
router.push(url)
}
diff --git a/apps/www/lib/constants.ts b/apps/www/lib/constants.ts
index 5586bb59f81..445c0a6810a 100644
--- a/apps/www/lib/constants.ts
+++ b/apps/www/lib/constants.ts
@@ -1,7 +1,8 @@
export const APP_NAME = 'Supabase'
export const DESCRIPTION = 'The Open Source Alternative to Firebase.'
export const IS_PROD = process.env.NEXT_PUBLIC_VERCEL_ENV === 'production'
-export const API_URL = IS_PROD ? process.env.NEXT_PUBLIC_API_URL : '/api'
+export const IS_PREVIEW = process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview'
+export const API_URL = process.env.NEXT_PUBLIC_API_URL
// Launch Weeek
export const SAMPLE_TICKET_NUMBER = 1234
diff --git a/apps/www/lib/gaEvents.ts b/apps/www/lib/gaEvents.ts
index 53908751474..167aa356a0c 100644
--- a/apps/www/lib/gaEvents.ts
+++ b/apps/www/lib/gaEvents.ts
@@ -8,12 +8,12 @@ const GoogleAnalyticsEvents: GoogleAnalyticsEventType = {
www_hp_hero_startProject: {
action: 'www_hp_hero_startProject',
category: 'link',
- label: 'Clicks on “Start your project” button in the home page hero',
+ label: '',
},
www_hp_hero_documentation: {
action: 'www_hp_hero_documentation',
category: 'link',
- label: 'Clicks on “Documentation” button in the home page hero',
+ label: '',
},
}
diff --git a/apps/www/lib/telemetry.ts b/apps/www/lib/telemetry.ts
index cdaa08cf1e8..0c6fb2ed9fe 100644
--- a/apps/www/lib/telemetry.ts
+++ b/apps/www/lib/telemetry.ts
@@ -1,5 +1,5 @@
import { post } from '~/lib/fetchWrapper'
-import { API_URL, IS_PROD } from 'lib/constants'
+import { API_URL, IS_PROD, IS_PREVIEW } from 'lib/constants'
export interface GoogleAnalyticsEvent {
category: string
@@ -17,7 +17,7 @@ export interface GoogleAnalyticsProps {
// but uses different ENV variables for www
const sendEvent = (event: GoogleAnalyticsEvent, gaProps: GoogleAnalyticsProps) => {
- if (!IS_PROD) return
+ if (!IS_PROD && !IS_PREVIEW) return
const { category, action, label, value } = event
From 6c33e30f4c91fb8a41ea123d25905314aacddacd Mon Sep 17 00:00:00 2001
From: Francesco Sansalvadore
Date: Thu, 4 May 2023 12:42:11 +0200
Subject: [PATCH 09/70] chore: update telemetry API_URL on page load
From 77ecbde3c7873f9f3b4732a54c564bfdda222298 Mon Sep 17 00:00:00 2001
From: Francesco Sansalvadore
Date: Thu, 4 May 2023 12:59:16 +0200
Subject: [PATCH 10/70] chore: update telemetry API_URL on page load
---
apps/www/pages/_app.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/apps/www/pages/_app.tsx b/apps/www/pages/_app.tsx
index 60a1a4684fe..dce4295802c 100644
--- a/apps/www/pages/_app.tsx
+++ b/apps/www/pages/_app.tsx
@@ -1,4 +1,4 @@
-import { APP_NAME, DESCRIPTION } from 'lib/constants'
+import { API_URL, APP_NAME, DESCRIPTION } from 'lib/constants'
import { DefaultSeo } from 'next-seo'
import { AppProps } from 'next/app'
import { useRouter } from 'next/router'
@@ -13,7 +13,7 @@ export default function MyApp({ Component, pageProps }: AppProps) {
const router = useRouter()
function handlePageTelemetry(route: string) {
- return post(`https://api.supabase.io/platform/telemetry/page`, {
+ return post(`${API_URL}/telemetry/page`, {
referrer: document.referrer,
title: document.title,
route,
From eaddf931eb820248938555f4bbd5ec43aea2e642 Mon Sep 17 00:00:00 2001
From: Francesco Sansalvadore
Date: Thu, 4 May 2023 13:49:16 +0200
Subject: [PATCH 11/70] chore: update hardcoded API_URL in docs page telemetry
---
apps/docs/lib/constants.ts | 1 +
apps/docs/pages/_app.tsx | 4 ++--
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/apps/docs/lib/constants.ts b/apps/docs/lib/constants.ts
index d1bbedbf47c..42c12c0d3f4 100644
--- a/apps/docs/lib/constants.ts
+++ b/apps/docs/lib/constants.ts
@@ -1,2 +1,3 @@
export const IS_PLATFORM = process.env.NEXT_PUBLIC_IS_PLATFORM === 'true'
export const LOCAL_SUPABASE = process.env.NEXT_PUBLIC_LOCAL_SUPABASE === 'true'
+export const API_URL = process.env.NEXT_PUBLIC_API_URL
diff --git a/apps/docs/pages/_app.tsx b/apps/docs/pages/_app.tsx
index 0ac7df56c63..ed75637efaa 100644
--- a/apps/docs/pages/_app.tsx
+++ b/apps/docs/pages/_app.tsx
@@ -10,7 +10,7 @@ import { CommandMenuProvider } from 'ui'
import components from '~/components'
import Favicons from '~/components/Favicons'
import SiteLayout from '~/layouts/SiteLayout'
-import { IS_PLATFORM, LOCAL_SUPABASE } from '~/lib/constants'
+import { API_URL, IS_PLATFORM, LOCAL_SUPABASE } from '~/lib/constants'
import { post } from '~/lib/fetchWrappers'
import '../styles/ch.scss'
import '../styles/main.scss?v=1.0.0'
@@ -25,7 +25,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
)
function handlePageTelemetry(route: string) {
- return post(`https://api.supabase.io/platform/telemetry/page`, {
+ return post(`${API_URL}/telemetry/page`, {
referrer: document.referrer,
title: document.title,
route,
From 03f7a509285e05365bbafead2eb47df9c70627ee Mon Sep 17 00:00:00 2001
From: Francesco Sansalvadore
Date: Thu, 4 May 2023 17:12:32 +0200
Subject: [PATCH 12/70] chore: add product links ga events
---
apps/www/components/Features/index.tsx | 48 ++++++++++++++++++++++--
apps/www/components/Hero.tsx | 52 ++++++++++++--------------
apps/www/components/TextLink.tsx | 28 +++++++-------
apps/www/lib/gaEvents.ts | 25 +++++++++++++
4 files changed, 107 insertions(+), 46 deletions(-)
diff --git a/apps/www/components/Features/index.tsx b/apps/www/components/Features/index.tsx
index b5c8dc3f68f..132690287bf 100644
--- a/apps/www/components/Features/index.tsx
+++ b/apps/www/components/Features/index.tsx
@@ -1,12 +1,46 @@
-import { Button, Badge, IconArrowRight } from 'ui'
-import SectionHeader from 'components/UI/SectionHeader'
+import { Badge } from 'ui'
import Solutions from 'data/Solutions.json'
-import Link from 'next/link'
+import useGoogleAnalyticsProps from '~/hooks/useGoogleAnalyticsProps'
+import Telemetry from '~/lib/telemetry'
+import gaEvents from '~/lib/gaEvents'
import SectionContainer from '../Layouts/SectionContainer'
import ProductIcon from '../ProductIcon'
import TextLink from '../TextLink'
const Features = () => {
+ const googleAnalyticsProps = useGoogleAnalyticsProps()
+
+ const sendTelemetryEvent = async (product: any) => {
+ switch (product) {
+ case 'Database':
+ return await Telemetry.sendEvent(
+ gaEvents['www_hp_subhero_products_database'],
+ googleAnalyticsProps
+ )
+ case 'Authentication':
+ return await Telemetry.sendEvent(
+ gaEvents['www_hp_subhero_products_auth'],
+ googleAnalyticsProps
+ )
+ case 'Storage':
+ return await Telemetry.sendEvent(
+ gaEvents['www_hp_subhero_products_storage'],
+ googleAnalyticsProps
+ )
+ case 'Edge Functions':
+ console.log('edge!!')
+ return await Telemetry.sendEvent(
+ gaEvents['www_hp_subhero_products_edgeFunctions'],
+ googleAnalyticsProps
+ )
+ case 'Realtime':
+ return await Telemetry.sendEvent(
+ gaEvents['www_hp_subhero_products_realtime'],
+ googleAnalyticsProps
+ )
+ }
+ }
+
const IconSections = Object.values(Solutions).map((solution: any) => {
const { name, description, icon, label, url } = solution
if (solution.name === 'Realtime') return null
@@ -24,7 +58,13 @@ const Features = () => {
{label}
+ className="w-8 h-8 md:w-10 md:h-10 bg-center bg-no-repeat bg-cover flex items-center justify-center"
+ // [Joshen] For when we support icons
+ // style={{ backgroundImage: `url('${requester?.icon}')` }}
+ >
+
{requester?.name[0]}
+
- {apiAuthDetails.name} is requesting API access to an organization. The application will
- be able to{' '}
-
- read and write the organization's settings and projects
+ {requester?.name} ({requester?.domain}) is requesting API access to an organization. The
+ application will be able to{' '}
+
+ read and write the organization's settings and all of its projects.
{/* Expiry warning */}
{isExpired && (
-
+
Please retry your authorization request from the requesting app
)}
@@ -104,12 +205,17 @@ const APIAuthorizationPage: NextPageWithLayout = () => {
) : (
-
+
{(organizations ?? []).map((organization) => (
{organization.name}
From 0d17d50eb529750f87c0368a05f58ed965b03971 Mon Sep 17 00:00:00 2001
From: Andrew Smith
Date: Fri, 5 May 2023 16:42:04 +0000
Subject: [PATCH 17/70] Update
apps/docs/pages/guides/auth/auth-password-reset.mdx
Co-authored-by: Greg Richardson
---
apps/docs/pages/guides/auth/auth-password-reset.mdx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/apps/docs/pages/guides/auth/auth-password-reset.mdx b/apps/docs/pages/guides/auth/auth-password-reset.mdx
index 9bd052a0345..8e769e955b2 100644
--- a/apps/docs/pages/guides/auth/auth-password-reset.mdx
+++ b/apps/docs/pages/guides/auth/auth-password-reset.mdx
@@ -20,8 +20,8 @@ the update password page. This url must be saved in your allowed [Redirect URLs]
list or it won't work.
```ts
-await supabase.auth.resetPasswordForEmail("hello@example.com", {
- redirectTo: 'http://example.com/account/update-password'
+await supabase.auth.resetPasswordForEmail('hello@example.com', {
+ redirectTo: 'http://example.com/account/update-password',
})
```
From 45141a745977ebbdae5c1eea38d7d3cf24b169c4 Mon Sep 17 00:00:00 2001
From: Andrew Smith
Date: Fri, 5 May 2023 16:42:18 +0000
Subject: [PATCH 18/70] Update
apps/docs/pages/guides/auth/auth-password-reset.mdx
Co-authored-by: Greg Richardson
---
apps/docs/pages/guides/auth/auth-password-reset.mdx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/apps/docs/pages/guides/auth/auth-password-reset.mdx b/apps/docs/pages/guides/auth/auth-password-reset.mdx
index 8e769e955b2..2d56f06c77b 100644
--- a/apps/docs/pages/guides/auth/auth-password-reset.mdx
+++ b/apps/docs/pages/guides/auth/auth-password-reset.mdx
@@ -48,8 +48,8 @@ allowed [Redirect URLs](https://app.supabase.com/project/_/auth/url-configuratio
redirect the user.
```ts
-await supabase.auth.resetPasswordForEmail("hello@example.com", {
- redirectTo: 'http://example.com/api/auth/callback?next=/account/update-password'
+await supabase.auth.resetPasswordForEmail('hello@example.com', {
+ redirectTo: 'http://example.com/api/auth/callback?next=/account/update-password',
})
```
From 7673a3c4b02bbe2625e8e94c04d89b13ada08f91 Mon Sep 17 00:00:00 2001
From: Andrew Smith
Date: Fri, 5 May 2023 16:42:32 +0000
Subject: [PATCH 19/70] Update
apps/docs/pages/guides/auth/auth-password-reset.mdx
Co-authored-by: Greg Richardson
---
apps/docs/pages/guides/auth/auth-password-reset.mdx | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/apps/docs/pages/guides/auth/auth-password-reset.mdx b/apps/docs/pages/guides/auth/auth-password-reset.mdx
index 2d56f06c77b..b5625e09047 100644
--- a/apps/docs/pages/guides/auth/auth-password-reset.mdx
+++ b/apps/docs/pages/guides/auth/auth-password-reset.mdx
@@ -55,8 +55,7 @@ await supabase.auth.resetPasswordForEmail('hello@example.com', {
-We are using `next` as our query parameter but this is just personal preference, you could
-use a different query parameter name.
+We are using `next` as our query parameter, but this can name whatever you like.
From 899921de63d54876008731dcfd31830193995d4a Mon Sep 17 00:00:00 2001
From: Andrew Smith
Date: Fri, 5 May 2023 16:42:44 +0000
Subject: [PATCH 20/70] Update
apps/docs/pages/guides/auth/auth-password-reset.mdx
Co-authored-by: Greg Richardson
---
apps/docs/pages/guides/auth/auth-password-reset.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/docs/pages/guides/auth/auth-password-reset.mdx b/apps/docs/pages/guides/auth/auth-password-reset.mdx
index b5625e09047..217dcf9fb2a 100644
--- a/apps/docs/pages/guides/auth/auth-password-reset.mdx
+++ b/apps/docs/pages/guides/auth/auth-password-reset.mdx
@@ -60,7 +60,7 @@ We are using `next` as our query parameter, but this can name whatever you like.
### Email link
-The email link you receive will behave like a magic link. When the link is clicked you will be sent to the `redirectTo` URL you specified that points to the path with the code exchange code.
+The email link you receive will behave like a magic link. When the link is clicked you will be sent to the `redirectTo` URL you specified that points to the path with the exchange code.
### Exchange authorization code
After getting redirected to the server page we need to retrieve the code from the query parameter called `code` and pass it to the `.exchangeAuthCodeForSession` function.
From 296d4bdfc1d8b0806a44a00b5bba3bfba775a90e Mon Sep 17 00:00:00 2001
From: Andrew Smith
Date: Fri, 5 May 2023 16:42:57 +0000
Subject: [PATCH 21/70] Update
apps/docs/pages/guides/auth/auth-password-reset.mdx
Co-authored-by: Greg Richardson
---
apps/docs/pages/guides/auth/auth-password-reset.mdx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apps/docs/pages/guides/auth/auth-password-reset.mdx b/apps/docs/pages/guides/auth/auth-password-reset.mdx
index 217dcf9fb2a..aef61326399 100644
--- a/apps/docs/pages/guides/auth/auth-password-reset.mdx
+++ b/apps/docs/pages/guides/auth/auth-password-reset.mdx
@@ -63,7 +63,7 @@ We are using `next` as our query parameter, but this can name whatever you like.
The email link you receive will behave like a magic link. When the link is clicked you will be sent to the `redirectTo` URL you specified that points to the path with the exchange code.
### Exchange authorization code
-After getting redirected to the server page we need to retrieve the code from the query parameter called `code` and pass it to the `.exchangeAuthCodeForSession` function.
+After redirecting to the server page, we need to retrieve the code from the query parameter called `code` and pass it to the `.exchangeAuthCodeForSession` function.
```ts
// api/auth/callback.ts
From 7bef3f1cd0eb2e152667a2a356d7a053e2480d9c Mon Sep 17 00:00:00 2001
From: Andrew Smith
Date: Fri, 5 May 2023 16:44:15 +0000
Subject: [PATCH 22/70] Update
apps/docs/pages/guides/auth/auth-password-reset.mdx
Co-authored-by: Greg Richardson
---
apps/docs/pages/guides/auth/auth-password-reset.mdx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/apps/docs/pages/guides/auth/auth-password-reset.mdx b/apps/docs/pages/guides/auth/auth-password-reset.mdx
index aef61326399..85cf47c82e5 100644
--- a/apps/docs/pages/guides/auth/auth-password-reset.mdx
+++ b/apps/docs/pages/guides/auth/auth-password-reset.mdx
@@ -67,8 +67,8 @@ After redirecting to the server page, we need to retrieve the code from the quer
```ts
// api/auth/callback.ts
-// code can be retrieved using your framework of choice way of getting query parameter from the url.
-// This is just example code
+// The code is retrieved from the query parameter - use whichever mechanism is recommended
+// for your app/framework. This is just an example.
const code = url.searchParams.get('code')
// call the Supabase API to exchange the code for a session
From 026c0633753acb047f4a1fe29f66e7d7d8036921 Mon Sep 17 00:00:00 2001
From: Andrew Smith
Date: Fri, 5 May 2023 16:44:23 +0000
Subject: [PATCH 23/70] Update
apps/docs/pages/guides/auth/auth-password-reset.mdx
Co-authored-by: Greg Richardson
---
apps/docs/pages/guides/auth/auth-password-reset.mdx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/apps/docs/pages/guides/auth/auth-password-reset.mdx b/apps/docs/pages/guides/auth/auth-password-reset.mdx
index 85cf47c82e5..03ff57c24a3 100644
--- a/apps/docs/pages/guides/auth/auth-password-reset.mdx
+++ b/apps/docs/pages/guides/auth/auth-password-reset.mdx
@@ -85,8 +85,8 @@ We will also need to check for the `next` query parameter to redirect the user t
```ts
// api/auth/callback.ts
-// password page path can be retrieved using your framework of choice way of getting query parameter from the url.
-// This is just example code
+// The password page path is retrieved from the query parameter - use whichever mechanism is recommended
+// for your app/framework. This is just an example.
const next = url.searchParams.get('next')
// using NextJS response object in this example
From ffcfca5f552bbb4122a20f8c7d11018bd77e5aa6 Mon Sep 17 00:00:00 2001
From: "joel@joellee.org"
Date: Sat, 6 May 2023 01:13:38 +0800
Subject: [PATCH 24/70] fix: add exchangeCodeForSession to spec
---
spec/supabase_js_v2.yml | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/spec/supabase_js_v2.yml b/spec/supabase_js_v2.yml
index 780e982f411..b7c659d38f6 100644
--- a/spec/supabase_js_v2.yml
+++ b/spec/supabase_js_v2.yml
@@ -567,6 +567,20 @@ functions:
if (event == 'USER_UPDATED') console.log('USER_UPDATED', session)
})
```
+ - id: exchange-code-for-session
+ title: 'exchangeCodeForSession()'
+ $ref: '@supabase/gotrue-js.GoTrueClient.exchangeCodeForSession'
+ notes: |
+ - Used when `flowType` is set to True in client options.
+ examples:
+ - id: exchange-auth-code
+ name: Exchange Auth Code
+ isSpotlight: true
+ code: |
+ ```js
+ supabase.auth.exchangeCodeForSession('34e770dd-9ff9-416c-87fa-43b31d7ef225')
+ ```
+
- id: mfa-enroll
title: 'mfa.enroll()'
$ref: '@supabase/gotrue-js.GoTrueMFAApi.enroll'
From 30adf466f00af4188b4b6b42c50ca91640f050db Mon Sep 17 00:00:00 2001
From: "joel@joellee.org"
Date: Sat, 6 May 2023 01:18:05 +0800
Subject: [PATCH 25/70] fix: update client spec
---
spec/common-client-libs-sections.json | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/spec/common-client-libs-sections.json b/spec/common-client-libs-sections.json
index 06bacd153c6..52b654279d2 100644
--- a/spec/common-client-libs-sections.json
+++ b/spec/common-client-libs-sections.json
@@ -558,6 +558,14 @@
"product": "auth",
"type": "function"
},
+ {
+ "id": "exchange-code-for-session",
+ "title": "Exchange an auth code for a session",
+ "slug": "auth-exchangecodeforsession",
+ "product": "auth",
+ "type": "function"
+
+ },
{
"id": "mfa-enroll",
"title": "Enroll a factor",
From d73dabe08235d6fc923794f75e4a5335343914c4 Mon Sep 17 00:00:00 2001
From: Terry Sutton
Date: Fri, 5 May 2023 16:12:14 -0230
Subject: [PATCH 26/70] Move home sidebar icons to own component
---
.../components/Navigation/Navigation.types.ts | 11 +
.../Navigation/NavigationMenu/HomeMenu.tsx | 151 +---------
.../NavigationMenu/HomeMenuIconPicker.tsx | 75 +++++
.../NavigationMenu/HomeMenuIcons.tsx | 258 ++++++++++++++++++
.../NavigationMenu.constants.ts | 143 +++++++++-
.../NavigationMenuGuideListItems.tsx | 21 +-
6 files changed, 492 insertions(+), 167 deletions(-)
create mode 100644 apps/docs/components/Navigation/NavigationMenu/HomeMenuIconPicker.tsx
create mode 100644 apps/docs/components/Navigation/NavigationMenu/HomeMenuIcons.tsx
diff --git a/apps/docs/components/Navigation/Navigation.types.ts b/apps/docs/components/Navigation/Navigation.types.ts
index 76afaa7b1c6..a644cc7c70d 100644
--- a/apps/docs/components/Navigation/Navigation.types.ts
+++ b/apps/docs/components/Navigation/Navigation.types.ts
@@ -22,3 +22,14 @@ export interface References {
currentVersion?: string
}
}
+
+type MenuItem = {
+ label: string
+ icon?: string
+ href?: string
+ level?: string
+ hasLightIcon?: boolean
+ community?: boolean
+}
+
+export type HomepageMenuItems = MenuItem[][]
diff --git a/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx b/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx
index db93e1616a2..5298b6a31a8 100644
--- a/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx
+++ b/apps/docs/components/Navigation/NavigationMenu/HomeMenu.tsx
@@ -5,145 +5,10 @@ import { useRouter } from 'next/router'
import { Fragment } from 'react'
import { Badge } from '~/../../packages/ui'
-const home = [
- [
- {
- label: 'Home',
- icon: '/img/icons/menu/home',
- href: '/',
- level: 'home',
- },
- {
- label: 'Getting Started',
- icon: '/img/icons/menu/getting-started',
- href: '/guides/getting-started',
- level: 'gettingstarted',
- },
- ],
- [
- {
- label: 'Database',
- icon: '/img/icons/menu/database',
- href: '/guides/database',
- level: 'database',
- },
- {
- label: 'Serverless APIs',
- icon: '/img/icons/menu/reference',
- href: '/guides/api',
- level: 'api',
- },
- {
- label: 'Auth',
- icon: '/img/icons/menu/auth',
- href: '/guides/auth',
- level: 'auth',
- },
- {
- label: 'Edge Functions',
- icon: '/img/icons/menu/functions',
- href: '/guides/functions',
- level: 'functions',
- },
- {
- label: 'Realtime',
- icon: '/img/icons/menu/realtime',
- href: '/guides/realtime',
- level: 'realtime',
- },
- {
- label: 'Storage',
- icon: '/img/icons/menu/storage',
- href: '/guides/storage',
- level: 'storage',
- },
- ],
- [
- {
- label: 'Platform',
- icon: '/img/icons/menu/platform',
- href: '/guides/platform',
- level: 'platform',
- },
- {
- label: 'Resources',
- icon: '/img/icons/menu/platform',
- href: '/guides/resources',
- level: 'resources',
- },
- {
- label: 'Self-Hosting',
- icon: '/img/icons/menu/platform',
- href: '/guides/self-hosting',
- level: 'self_hosting',
- },
- {
- label: 'Integrations',
- icon: '/img/icons/menu/integrations',
- hasLightIcon: true,
- href: '/guides/integrations',
- level: 'integrations',
- },
- ],
- [
- {
- label: 'Client Library Reference',
- },
- {
- label: 'JavaScript',
- icon: '/img/icons/menu/reference-javascript',
- href: '/reference/javascript/introduction',
- level: 'reference_javascript',
- },
- {
- label: 'Flutter',
- icon: '/img/icons/menu/reference-dart',
- href: '/reference/dart/introduction',
- level: 'reference_dart',
- },
- {
- label: 'Python',
- icon: '/img/icons/menu/reference-python',
- href: '/reference/python/introduction',
- level: 'reference_python',
- community: true,
- },
- {
- label: 'C#',
- icon: '/img/icons/menu/reference-csharp',
- href: '/reference/csharp/introduction',
- level: 'reference_csharp',
- community: true,
- },
- {
- label: 'Swift',
- icon: '/img/icons/menu/reference-swift',
- href: '/reference/swift/introduction',
- level: 'reference_swift',
- community: true,
- },
- {
- label: 'Tools',
- },
- {
- label: 'Management API',
- icon: '/img/icons/menu/reference-api',
- href: '/reference/api/introduction',
- level: 'reference_javascript',
- },
- {
- label: 'Supabase CLI',
- icon: '/img/icons/menu/reference-cli',
- href: '/guides/cli',
- level: 'reference_javascript',
- },
- ],
-]
+import { HOMEPAGE_MENU_ITEMS } from './NavigationMenu.constants'
+import HomeMenuIconPicker from './HomeMenuIconPicker'
const NavigationMenuHome = ({ active }) => {
- const router = useRouter()
- const { isDarkMode } = useTheme()
-
return (