mirror of
https://github.com/supabase/supabase.git
synced 2026-10-17 07:09:07 +03:00
Merge branch 'master' of github.com:supabase/supabase
This commit is contained in:
commit
ef1110bcc4
4 files changed
+63
-50
No files matched your search
@@ -142,7 +142,7 @@ The PKCE flow allows for server-side authentication. Unlike the implicit flow, w
|
||||
|
||||
##### Step 1: Update signup confirmation email
|
||||
|
||||
Update your signup email template to send the token hash. See [Email Templates](/docs/guides/auth/auth-email-templates) for how to configure your email templates.
|
||||
Update your signup email template to send the token hash. For detailed instructions on how to configure your email templates, including the use of variables like `{{ .SiteURL }}`, `{{ .TokenHash }}`, and `{{ .RedirectTo }}`, please refer to our [Email Templates](/docs/guides/auth/auth-email-templates) guide.
|
||||
|
||||
Your signup email template should contain the following HTML:
|
||||
|
||||
@@ -151,7 +151,8 @@ Your signup email template should contain the following HTML:
|
||||
|
||||
<p>Follow this link to confirm your user:</p>
|
||||
<p>
|
||||
<a href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email"
|
||||
<a
|
||||
href="{{ .SiteURL }}/auth/confirm?token_hash={{ .TokenHash }}&type=email&next={{ .RedirectTo }}"
|
||||
>Confirm your email</a
|
||||
>
|
||||
</p>
|
||||
@@ -170,18 +171,16 @@ Create a new file at `app/auth/confirm/route.ts` and populate with the following
|
||||
|
||||
```ts app/auth/confirm/route.ts
|
||||
import { type EmailOtpType } from '@supabase/supabase-js'
|
||||
import { cookies } from 'next/headers'
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
// The client you created from the Server-Side Auth instructions
|
||||
import { type NextRequest } from 'next/server'
|
||||
|
||||
import { createClient } from '@/utils/supabase/server'
|
||||
import { redirect } from 'next/navigation'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const token_hash = searchParams.get('token_hash')
|
||||
const type = searchParams.get('type') as EmailOtpType | null
|
||||
const next = searchParams.get('next') ?? '/'
|
||||
const redirectTo = request.nextUrl.clone()
|
||||
redirectTo.pathname = next
|
||||
|
||||
if (token_hash && type) {
|
||||
const supabase = await createClient()
|
||||
@@ -191,13 +190,13 @@ export async function GET(request: NextRequest) {
|
||||
token_hash,
|
||||
})
|
||||
if (!error) {
|
||||
return NextResponse.redirect(redirectTo)
|
||||
// redirect user to specified redirect URL or root of app
|
||||
redirect(next)
|
||||
}
|
||||
}
|
||||
|
||||
// return the user to an error page with some instructions
|
||||
redirectTo.pathname = '/auth/auth-code-error'
|
||||
return NextResponse.redirect(redirectTo)
|
||||
// redirect the user to an error page with some instructions
|
||||
redirect('/auth/auth-code-error')
|
||||
}
|
||||
```
|
||||
|
||||
@@ -381,11 +380,18 @@ app.get("/auth/confirm", async function (req, res) {
|
||||
|
||||
To sign up the user, call [signUp()](/docs/reference/javascript/auth-signup) with their email address and password:
|
||||
|
||||
You can optionally specify a URL to redirect to after the user clicks the confirmation link. This URL must be configured as a [Redirect URL](/docs/guides/auth/redirect-urls), which you can do in the [dashboard](/dashboard/project/_/auth/url-configuration) for hosted projects, or in the [configuration file](/docs/guides/cli/config#auth.additional_redirect_urls) for self-hosted projects.
|
||||
|
||||
If you don't specify a redirect URL, the user is automatically redirected to your site URL. This defaults to `localhost:3000`, but you can also configure this.
|
||||
|
||||
```js
|
||||
async function signUpNewUser() {
|
||||
const { data, error } = await supabase.auth.signUp({
|
||||
email: 'example@email.com',
|
||||
password: 'example-password',
|
||||
options: {
|
||||
emailRedirectTo: 'https://example.com/welcome',
|
||||
},
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useQueryClient } from '@tanstack/react-query'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import { ChevronRight } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { toast } from 'sonner'
|
||||
@@ -24,8 +25,10 @@ import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-que
|
||||
import { useProjectAddonUpdateMutation } from 'data/subscriptions/project-addon-update-mutation'
|
||||
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
|
||||
import { AddonVariantId } from 'data/subscriptions/types'
|
||||
import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query'
|
||||
import { useCheckPermissions, usePermissionsLoaded } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
import { GB, PROJECT_STATUS } from 'lib/constants'
|
||||
import {
|
||||
Button,
|
||||
@@ -56,7 +59,6 @@ import {
|
||||
} from './ui/DiskManagement.constants'
|
||||
import { NoticeBar } from './ui/NoticeBar'
|
||||
import { SpendCapDisabledSection } from './ui/SpendCapDisabledSection'
|
||||
import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query'
|
||||
|
||||
export function DiskManagementForm() {
|
||||
const {
|
||||
@@ -67,6 +69,8 @@ export function DiskManagementForm() {
|
||||
const org = useSelectedOrganization()
|
||||
const { ref: projectRef } = useParams()
|
||||
const queryClient = useQueryClient()
|
||||
const router = useRouter()
|
||||
const diskAndComputeForm = useFlag('diskAndComputeForm')
|
||||
|
||||
const { data: resourceWarnings } = useResourceWarningsQuery()
|
||||
const projectResourceWarnings = (resourceWarnings ?? [])?.find(
|
||||
@@ -186,14 +190,6 @@ export function DiskManagementForm() {
|
||||
isFlyArchitecture
|
||||
|
||||
const disableComputeInputs = isPlanUpgradeRequired
|
||||
|
||||
useEffect(() => {
|
||||
// Initialize field values properly when data has been loaded, preserving any user changes
|
||||
if (isSuccess) {
|
||||
form.reset(defaultValues, {})
|
||||
}
|
||||
}, [isSuccess])
|
||||
|
||||
const isDirty = !!Object.keys(form.formState.dirtyFields).length
|
||||
const isProjectResizing = project?.status === PROJECT_STATUS.RESIZING
|
||||
const isProjectRequestingDiskChanges = isRequestingChanges && !isProjectResizing
|
||||
@@ -262,6 +258,20 @@ export function DiskManagementForm() {
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Initialize field values properly when data has been loaded, preserving any user changes
|
||||
if (isSuccess) {
|
||||
form.reset(defaultValues, {})
|
||||
}
|
||||
}, [isSuccess])
|
||||
|
||||
// Redirect logic incase disk and compute feature is not live yet
|
||||
useEffect(() => {
|
||||
if (diskAndComputeForm !== undefined && !diskAndComputeForm && projectRef) {
|
||||
router.push(`/project/${projectRef}/settings/addons?panel=computeInstance`)
|
||||
}
|
||||
}, [diskAndComputeForm, projectRef, router])
|
||||
|
||||
return (
|
||||
<Form_Shadcn_ {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-8">
|
||||
|
||||
+9
-14
@@ -54,9 +54,6 @@ const ExitSurveyModal = ({ visible, subscription, projects, onClose }: ExitSurve
|
||||
|
||||
const hasProjectsWithComputeDowngrade = projectsWithComputeDowngrade.length > 0
|
||||
|
||||
const willPlanDowngradeHappenImmediately =
|
||||
subscription?.billing_via_partner === false || subscription?.billing_partner !== 'fly'
|
||||
|
||||
function reducer(state: any, action: any) {
|
||||
if (includes(state, action.target.value)) {
|
||||
return without(state, action.target.value)
|
||||
@@ -105,11 +102,9 @@ const ExitSurveyModal = ({ visible, subscription, projects, onClose }: ExitSurve
|
||||
// [Joshen] In this case we don't raise any errors if the exit survey fails to send since it shouldn't block the user
|
||||
} finally {
|
||||
toast.success(
|
||||
willPlanDowngradeHappenImmediately
|
||||
? hasProjectsWithComputeDowngrade
|
||||
? 'Successfully downgraded organization to the Free Plan. Your projects are currently restarting to update their compute instances.'
|
||||
: 'Successfully downgraded organization to the Free Plan'
|
||||
: 'Your organization is scheduled for the downgrade at the end of your current billing cycle',
|
||||
hasProjectsWithComputeDowngrade
|
||||
? 'Successfully downgraded organization to the Free Plan. Your projects are currently restarting to update their compute instances.'
|
||||
: 'Successfully downgraded organization to the Free Plan',
|
||||
{ duration: hasProjectsWithComputeDowngrade ? 8000 : 4000 }
|
||||
)
|
||||
onClose(true)
|
||||
@@ -199,7 +194,7 @@ const ExitSurveyModal = ({ visible, subscription, projects, onClose }: ExitSurve
|
||||
<Alert
|
||||
withIcon
|
||||
variant="warning"
|
||||
title={`${projectsWithComputeDowngrade.length} of your projects will be restarted ${willPlanDowngradeHappenImmediately ? 'upon clicking confirm' : 'once the downgrade takes effect at the end of your current billing cycle'}`}
|
||||
title={`${projectsWithComputeDowngrade.length} of your projects will be restarted upon clicking confirm,`}
|
||||
>
|
||||
This is due to changes in compute instances from the downgrade. Affected projects
|
||||
include {projectsWithComputeDowngrade.map((project) => project.name).join(', ')}.
|
||||
@@ -209,11 +204,11 @@ const ExitSurveyModal = ({ visible, subscription, projects, onClose }: ExitSurve
|
||||
</Modal.Content>
|
||||
|
||||
<div className="flex items-center justify-between border-t px-4 py-4">
|
||||
{willPlanDowngradeHappenImmediately && (
|
||||
<p className="text-xs text-foreground-lighter">
|
||||
The unused amount for the remaining of your billing cycle will be refunded as credits
|
||||
</p>
|
||||
)}
|
||||
<p className="text-xs text-foreground-lighter">
|
||||
The unused amount for the remaining time of your billing cycle will be refunded as
|
||||
credits
|
||||
</p>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button type="default" onClick={() => onClose()}>
|
||||
Cancel
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import * as Tooltip from '@radix-ui/react-tooltip'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { ExternalLink, Info } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import { ExternalLink, Info } from 'lucide-react'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import {
|
||||
@@ -32,7 +32,6 @@ import {
|
||||
AlertTitle_Shadcn_,
|
||||
Badge,
|
||||
Button,
|
||||
CriticalIcon,
|
||||
Modal,
|
||||
Radio,
|
||||
SidePanel,
|
||||
@@ -45,17 +44,14 @@ const ComputeInstanceSidePanel = () => {
|
||||
const { ref: projectRef } = useParams()
|
||||
const { project: selectedProject } = useProjectContext()
|
||||
const organization = useSelectedOrganization()
|
||||
|
||||
const computeSizeChangesDisabled = useFlag('disableComputeSizeChanges')
|
||||
const diskAndComputeForm = useFlag('diskAndComputeForm')
|
||||
const [showConfirmationModal, setShowConfirmationModal] = useState(false)
|
||||
|
||||
const canUpdateCompute = useCheckPermissions(
|
||||
PermissionAction.BILLING_WRITE,
|
||||
'stripe.subscriptions'
|
||||
)
|
||||
|
||||
const isProjectActive = useIsProjectActive()
|
||||
|
||||
const { panel, setPanel, closePanel } = useAddonsPagePanel()
|
||||
const visible = panel === 'computeInstance'
|
||||
|
||||
@@ -176,16 +172,6 @@ const ComputeInstanceSidePanel = () => {
|
||||
hasReadReplicas &&
|
||||
(isDowngradingToBelowSmall || hasMoreThanTwoReplicasForXLAndAbove)
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
if (subscriptionCompute !== undefined) {
|
||||
setSelectedOption(subscriptionCompute.variant.identifier)
|
||||
} else {
|
||||
setSelectedOption(defaultInstanceSize)
|
||||
}
|
||||
}
|
||||
}, [visible, isLoading])
|
||||
|
||||
const onConfirmUpdateComputeInstance = async () => {
|
||||
if (!projectRef) return console.error('Project ref is required')
|
||||
if (!projectId) return console.error('Project ID is required')
|
||||
@@ -210,6 +196,22 @@ const ComputeInstanceSidePanel = () => {
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
if (subscriptionCompute !== undefined) {
|
||||
setSelectedOption(subscriptionCompute.variant.identifier)
|
||||
} else {
|
||||
setSelectedOption(defaultInstanceSize)
|
||||
}
|
||||
}
|
||||
}, [visible, isLoading])
|
||||
|
||||
useEffect(() => {
|
||||
if (visible && diskAndComputeForm) {
|
||||
router.push(`/project/${projectRef}/settings/compute-and-disk`)
|
||||
}
|
||||
}, [visible, diskAndComputeForm, router, projectRef])
|
||||
|
||||
return (
|
||||
<>
|
||||
<SidePanel
|
||||
|
||||
Reference in new issue
Block a user