diff --git a/apps/docs/content/guides/auth/server-side/nextjs.mdx b/apps/docs/content/guides/auth/server-side/nextjs.mdx
index 2cae8e90d4609..0443da172579b 100644
--- a/apps/docs/content/guides/auth/server-side/nextjs.mdx
+++ b/apps/docs/content/guides/auth/server-side/nextjs.mdx
@@ -264,7 +264,8 @@ export async function updateSession(request: NextRequest) {
if (
!user &&
!request.nextUrl.pathname.startsWith('/login') &&
- !request.nextUrl.pathname.startsWith('/auth')
+ !request.nextUrl.pathname.startsWith('/auth') &&
+ !request.nextUrl.pathname.startsWith('/error')
) {
// no user, potentially respond by redirecting the user to the login page
const url = request.nextUrl.clone()
diff --git a/apps/docs/content/guides/deployment/branching/dashboard.mdx b/apps/docs/content/guides/deployment/branching/dashboard.mdx
index 2d938165736cd..a7577014d5a8d 100644
--- a/apps/docs/content/guides/deployment/branching/dashboard.mdx
+++ b/apps/docs/content/guides/deployment/branching/dashboard.mdx
@@ -58,8 +58,6 @@ When reviewing a merge request you may see a notice at the top of the page askin
There are a few limitations you should be aware of before deciding to use branching without git.
- Custom roles created through the dashboard are not captured on branch creation
-- Only public schema changes are supported right now
-- Extensions are not included in the diff process
- Branches can only be merged to main; merging between preview branches is not supported
- If your branch is out of date, you can pull in latest changes from main, but keep in mind that all functions will be overwritten
- Deleting functions must be done manually on main branch
diff --git a/apps/docs/content/guides/getting-started/tutorials/with-sveltekit.mdx b/apps/docs/content/guides/getting-started/tutorials/with-sveltekit.mdx
index 1b114f50a60be..43def489bf8d5 100644
--- a/apps/docs/content/guides/getting-started/tutorials/with-sveltekit.mdx
+++ b/apps/docs/content/guides/getting-started/tutorials/with-sveltekit.mdx
@@ -17,14 +17,14 @@ If you get stuck while working through this guide, refer to the [full example on
## Building the app
-Let's start building the Svelte app from scratch.
+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 we will be using TypeScript):
+Use the [SvelteKit Skeleton Project](https://svelte.dev/docs/kit) to initialize an app called `supabase-sveltekit` (for this tutorial, select "SvelteKit minimal" and use TypeScript):
```bash
-npm create svelte@latest supabase-sveltekit
+npx sv create supabase-sveltekit
cd supabase-sveltekit
npm install
```
@@ -35,8 +35,8 @@ Then install the Supabase client library: [supabase-js](https://github.com/supab
npm install @supabase/supabase-js
```
-And finally we want to save the environment variables in a `.env`.
-All we need are the `SUPABASE_URL` and the `SUPABASE_KEY` key that you copied [earlier](#get-the-api-keys).
+And finally, save the environment variables in a `.env` file.
+All you need are the `PUBLIC_SUPABASE_URL` and the `PUBLIC_SUPABASE_ANON_KEY` key that you copied [earlier](#get-the-api-keys).
<$CodeTabs>
@@ -47,108 +47,46 @@ PUBLIC_SUPABASE_ANON_KEY="YOUR_SUPABASE_KEY"
$CodeTabs>
-Optionally, add `src/styles.css` with the [CSS from the example](https://raw.githubusercontent.com/supabase/supabase/master/examples/user-management/sveltekit-user-management/src/styles.css).
+### App styling (optional)
+
+An optional step is to update the CSS file `src/styles.css` to make the app look nice.
+You can find the full contents of this file [in the example repository](https://raw.githubusercontent.com/supabase/supabase/master/examples/user-management/sveltekit-user-management/src/styles.css).
### Creating a Supabase client for SSR
-The `ssr` package configures Supabase to use Cookies, which is required for server-side languages and frameworks.
+The `ssr` package configures Supabase to use Cookies, which are required for server-side languages and frameworks.
-Install the Supabase packages:
+Install the SSR package:
```bash
-npm install @supabase/ssr @supabase/supabase-js
+npm install @supabase/ssr
```
-Creating a Supabase client with the `ssr` package automatically configures it to use Cookies. This means your user's session is available throughout the entire SvelteKit stack - page, layout, server, hooks.
+Creating a Supabase client with the `ssr` package automatically configures it to use Cookies. This means the user's session is available throughout the entire SvelteKit stack - page, layout, server, and hooks.
-Add the code below to your `src/hooks.server.ts` to initialize the client on the server:
+Add the code below to a `src/hooks.server.ts` file to initialize the client on the server:
<$CodeTabs>
-```ts name=src/hooks.server.ts
-// src/hooks.server.ts
-import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
-import { createServerClient } from '@supabase/ssr'
-import type { Handle } from '@sveltejs/kit'
-
-export const handle: Handle = async ({ event, resolve }) => {
- event.locals.supabase = createServerClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, {
- cookies: {
- getAll: () => event.cookies.getAll(),
- /**
- * SvelteKit's cookies API requires `path` to be explicitly set in
- * the cookie options. Setting `path` to `/` replicates previous/
- * standard behavior.
- */
- setAll: (cookiesToSet) => {
- cookiesToSet.forEach(({ name, value, options }) => {
- event.cookies.set(name, value, { ...options, path: '/' })
- })
- },
- },
- })
-
- /**
- * Unlike `supabase.auth.getSession()`, which returns the session _without_
- * validating the JWT, this function also calls `getUser()` to validate the
- * JWT before returning the session.
- */
- event.locals.safeGetSession = async () => {
- const {
- data: { session },
- } = await event.locals.supabase.auth.getSession()
- if (!session) {
- return { session: null, user: null }
- }
-
- const {
- data: { user },
- error,
- } = await event.locals.supabase.auth.getUser()
- if (error) {
- // JWT validation has failed
- return { session: null, user: null }
- }
-
- return { session, user }
- }
-
- return resolve(event, {
- filterSerializedResponseHeaders(name) {
- return name === 'content-range' || name === 'x-supabase-api-version'
- },
- })
-}
-```
+<$CodeSample
+path="/user-management/sveltekit-user-management/src/hooks.server.ts"
+lines={[[1, -1]]}
+meta="name=src/hooks.server.ts"
+/>
$CodeTabs>
<$Partial path="get_session_warning.mdx" />
-
-If you are using TypeScript the compiler might complain about `event.locals.supabase` and `event.locals.safeGetSession`, this can be fixed by updating your `src/app.d.ts` with the content below:
+{/* TODO: Change when adding JS autoconversion */}
+As this tutorial uses TypeScript the compiler complains about `event.locals.supabase` and `event.locals.safeGetSession`, you can fix this by updating the `src/app.d.ts` with the content below:
<$CodeTabs>
-```ts name=src/app.d.ts
-// src/app.d.ts
-
-import { SupabaseClient, Session } from '@supabase/supabase-js'
-
-declare global {
- namespace App {
- interface Locals {
- supabase: SupabaseClient
- safeGetSession(): Promise<{ session: Session | null; user: User | null }>
- }
- interface PageData {
- session: Session | null
- user: User | null
- }
- // interface Error {}
- // interface Platform {}
- }
-}
-```
+<$CodeSample
+path="/user-management/sveltekit-user-management/src/app.d.ts"
+lines={[[1, -1]]}
+meta="name=src/app.d.ts"
+/>
$CodeTabs>
@@ -156,26 +94,17 @@ Create a new `src/routes/+layout.server.ts` file to handle the session on the se
<$CodeTabs>
-```ts name=src/routes/+layout.server.ts
-// src/routes/+layout.server.ts
-import type { LayoutServerLoad } from './$types'
-
-export const load: LayoutServerLoad = async ({ locals: { safeGetSession }, cookies }) => {
- const { session, user } = await safeGetSession()
-
- return {
- session,
- user,
- cookies: cookies.getAll(),
- }
-}
-```
+<$CodeSample
+path="/user-management/sveltekit-user-management/src/routes/+layout.server.ts"
+lines={[[1, -1]]}
+meta="name=src/routes/+layout.server.ts"
+/>
$CodeTabs>
Login error
-``` +<$CodeSample +path="/user-management/sveltekit-user-management/src/routes/auth/error/+page.svelte" +lines={[[1, -1]]} +meta="name=src/routes/auth/error/+page.svelte" +/> $CodeTabs> ### Account page -After a user is signed in, they need to be able to edit their profile details and manage their account. +After a user signs in, they need to be able to edit their profile details page. Create a new `src/routes/account/+page.svelte` file with the content below. <$CodeTabs> -```svelte name=src/routes/account/+page.svelte - - - - -``` +<$CodeSample +path="/user-management/sveltekit-user-management/src/routes/account/+page.svelte" +lines={[[1, 3],[6,12],[15,38],[47,-1]]} +meta="name=src/routes/account/+page.svelte" +/> $CodeTabs> -Now create the associated `src/routes/account/+page.server.ts` file that will handle loading our data from the server through the `load` function -and handle all our form actions through the `actions` object. - -```ts name=src/routes/account/+page.server.ts -import { fail, redirect } from '@sveltejs/kit' -import type { Actions, PageServerLoad } from './$types' - -export const load: PageServerLoad = async ({ locals: { supabase, safeGetSession } }) => { - const { session } = await safeGetSession() - - if (!session) { - redirect(303, '/') - } - - const { data: profile } = await supabase - .from('profiles') - .select(`username, full_name, website, avatar_url`) - .eq('id', session.user.id) - .single() - - return { session, profile } -} - -export const actions: Actions = { - update: async ({ request, locals: { supabase, safeGetSession } }) => { - const formData = await request.formData() - const fullName = formData.get('fullName') as string - const username = formData.get('username') as string - const website = formData.get('website') as string - const avatarUrl = formData.get('avatarUrl') as string - - const { session } = await safeGetSession() - - const { error } = await supabase.from('profiles').upsert({ - id: session?.user.id, - full_name: fullName, - username, - website, - avatar_url: avatarUrl, - updated_at: new Date(), - }) - - if (error) { - return fail(500, { - fullName, - username, - website, - avatarUrl, - }) - } - - return { - fullName, - username, - website, - avatarUrl, - } - }, - signout: async ({ locals: { supabase, safeGetSession } }) => { - const { session } = await safeGetSession() - if (session) { - await supabase.auth.signOut() - redirect(303, '/') - } - }, -} -``` +Now, create the associated `src/routes/account/+page.server.ts` file that handles loading data from the server through the `load` function +and handle all form actions through the `actions` object. + +<$CodeSample +path="/user-management/sveltekit-user-management/src/routes/+page.server.ts" +lines={[[1, -1]]} +meta="name=src/routes/+page.server.ts" +/> ### Launch! -Now that we have all the pages in place, run this in a terminal window: +With all the pages in place, run this command in a terminal: ```bash npm run dev @@ -636,144 +250,29 @@ Every Supabase project is configured with [Storage](/docs/guides/storage) for ma ### Create an upload widget -Let's create an avatar for the user so that they can upload a profile photo. We can start by creating a new component called `Avatar.svelte` in the `src/routes/account` directory: +Create an avatar for the user so that they can upload a profile photo. Start by creating a new component called `Avatar.svelte` in the `src/routes/account` directory: <$CodeTabs> -```svelte name=src/routes/account/Avatar.svelte - - - -Limitations:
public schema changes are supported right now.
- main; merging between preview branches is
not supported.
diff --git a/apps/studio/components/interfaces/BranchManagement/CreateBranchModal.tsx b/apps/studio/components/interfaces/BranchManagement/CreateBranchModal.tsx
index f814b5f291f85..2a774632f1863 100644
--- a/apps/studio/components/interfaces/BranchManagement/CreateBranchModal.tsx
+++ b/apps/studio/components/interfaces/BranchManagement/CreateBranchModal.tsx
@@ -24,7 +24,7 @@ import { useGitHubConnectionsQuery } from 'data/integrations/github-connections-
import { projectKeys } from 'data/projects/keys'
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
-import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
+import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useFlag } from 'hooks/ui/useFlag'
@@ -114,7 +114,10 @@ export const CreateBranchModal = () => {
},
})
- const canCreateBranch = useCheckPermissions(PermissionAction.CREATE, 'preview_branches')
+ const { can: canCreateBranch } = useAsyncCheckProjectPermissions(
+ PermissionAction.CREATE,
+ 'preview_branches'
+ )
const githubConnection = connections?.find((connection) => connection.project.ref === projectRef)
diff --git a/apps/studio/components/interfaces/BranchManagement/Overview.tsx b/apps/studio/components/interfaces/BranchManagement/Overview.tsx
index eebf5b7963144..88257f88afda3 100644
--- a/apps/studio/components/interfaces/BranchManagement/Overview.tsx
+++ b/apps/studio/components/interfaces/BranchManagement/Overview.tsx
@@ -23,7 +23,7 @@ import { useBranchResetMutation } from 'data/branches/branch-reset-mutation'
import { useBranchUpdateMutation } from 'data/branches/branch-update-mutation'
import type { Branch } from 'data/branches/branches-query'
import { branchKeys } from 'data/branches/keys'
-import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
+import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
import {
Button,
DropdownMenu,
@@ -169,8 +169,14 @@ const PreviewBranchActions = ({
const queryClient = useQueryClient()
const projectRef = branch.parent_project_ref ?? branch.project_ref
- const canDeleteBranches = useCheckPermissions(PermissionAction.DELETE, 'preview_branches')
- const canUpdateBranches = useCheckPermissions(PermissionAction.UPDATE, 'preview_branches')
+ const { can: canDeleteBranches } = useAsyncCheckProjectPermissions(
+ PermissionAction.DELETE,
+ 'preview_branches'
+ )
+ const { can: canUpdateBranches } = useAsyncCheckProjectPermissions(
+ PermissionAction.UPDATE,
+ 'preview_branches'
+ )
const { data } = useBranchQuery({ projectRef, id: branch.id })
const isBranchActiveHealthy = data?.status === 'ACTIVE_HEALTHY'
@@ -381,7 +387,10 @@ const PreviewBranchActions = ({
// Actions for main (production) branch
const MainBranchActions = ({ branch, repo }: { branch: Branch; repo: string }) => {
const { ref: projectRef } = useParams()
- const canUpdateBranches = useCheckPermissions(PermissionAction.UPDATE, 'preview_branches')
+ const { can: canUpdateBranches } = useAsyncCheckProjectPermissions(
+ PermissionAction.UPDATE,
+ 'preview_branches'
+ )
const [showEditBranchModal, setShowEditBranchModal] = useState(false)
return (
diff --git a/apps/studio/components/interfaces/Connect/Connect.tsx b/apps/studio/components/interfaces/Connect/Connect.tsx
index 03482aeee43a7..6d3065c670a87 100644
--- a/apps/studio/components/interfaces/Connect/Connect.tsx
+++ b/apps/studio/components/interfaces/Connect/Connect.tsx
@@ -9,7 +9,7 @@ import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import Panel from 'components/ui/Panel'
import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query'
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
-import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
+import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { PROJECT_STATUS } from 'lib/constants'
import {
@@ -55,7 +55,10 @@ export const Connect = () => {
)
const { data: settings } = useProjectSettingsV2Query({ projectRef }, { enabled: showConnect })
- const canReadAPIKeys = useCheckPermissions(PermissionAction.READ, 'service_api_keys')
+ const { can: canReadAPIKeys } = useAsyncCheckProjectPermissions(
+ PermissionAction.READ,
+ 'service_api_keys'
+ )
const handleParentChange = (value: string) => {
setSelectedParent(value)
diff --git a/apps/studio/components/interfaces/Database/Backups/BackupItem.tsx b/apps/studio/components/interfaces/Database/Backups/BackupItem.tsx
index 7b9de9b086388..dbe0a9ca5e440 100644
--- a/apps/studio/components/interfaces/Database/Backups/BackupItem.tsx
+++ b/apps/studio/components/interfaces/Database/Backups/BackupItem.tsx
@@ -6,7 +6,7 @@ import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { InlineLink } from 'components/ui/InlineLink'
import { useBackupDownloadMutation } from 'data/database/backup-download-mutation'
import type { DatabaseBackup } from 'data/database/backups-query'
-import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
+import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
import { Badge, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
import { TimestampInfo } from 'ui-patterns'
@@ -17,9 +17,9 @@ interface BackupItemProps {
onSelectBackup: () => void
}
-const BackupItem = ({ index, isHealthy, backup, onSelectBackup }: BackupItemProps) => {
+export const BackupItem = ({ index, isHealthy, backup, onSelectBackup }: BackupItemProps) => {
const { ref: projectRef } = useParams()
- const canTriggerScheduledBackups = useCheckPermissions(
+ const { can: canTriggerScheduledBackups } = useAsyncCheckProjectPermissions(
PermissionAction.INFRA_EXECUTE,
'queue_job.restore.prepare'
)
@@ -38,8 +38,7 @@ const BackupItem = ({ index, isHealthy, backup, onSelectBackup }: BackupItemProp
})
const generateSideButtons = (backup: DatabaseBackup) => {
- // [Joshen] API typing is incorrect here, status is getting typed as Record