mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
This reverts commit 3e2f8cf51b.
Co-authored-by: Jonathan Summers-Muir <MildTomato@users.noreply.github.com>
This commit is contained in:
1 parent
4647d50380
commit
cd4b057ef5
4 files changed
+93
-183
No files matched your search
@@ -1,77 +1,41 @@
|
||||
import { noop } from 'lodash'
|
||||
import { Globe } from 'lucide-react'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { EmptyListState } from 'components/ui/States'
|
||||
import { Checkbox_Shadcn_ } from 'ui'
|
||||
import { ValueContainer } from './ValueContainer'
|
||||
import ValueContainer from './ValueContainer'
|
||||
import { Globe, Trash } from 'lucide-react'
|
||||
|
||||
interface RedirectUrlListProps {
|
||||
allowList: string[]
|
||||
URI_ALLOW_LIST_ARRAY: string[]
|
||||
canUpdate: boolean
|
||||
selectedUrls: string[]
|
||||
onSelectUrl: (urls: string[]) => void
|
||||
onSelectUrlToDelete: (url: string) => void
|
||||
}
|
||||
|
||||
export const RedirectUrlList = ({
|
||||
allowList,
|
||||
selectedUrls,
|
||||
onSelectUrl = noop,
|
||||
const RedirectUrlList = ({
|
||||
URI_ALLOW_LIST_ARRAY,
|
||||
canUpdate,
|
||||
onSelectUrlToDelete = noop,
|
||||
}: RedirectUrlListProps) => {
|
||||
// [Joshen] One for next time: maybe shift this into a reusable logic since it
|
||||
// seems like we can use this in multiple places for future
|
||||
const onClickUrl = (event: any, url: string) => {
|
||||
if (event.shiftKey) {
|
||||
const urlIdx = allowList.indexOf(url)
|
||||
const idxLatest = allowList.indexOf(selectedUrls[selectedUrls.length - 1])
|
||||
|
||||
const newSelectedUrls =
|
||||
urlIdx > idxLatest
|
||||
? allowList.slice(idxLatest + 1, urlIdx + 1)
|
||||
: allowList.slice(urlIdx, idxLatest)
|
||||
|
||||
const urlsNotSelectedYet = newSelectedUrls.filter((x) => !selectedUrls.includes(x))
|
||||
|
||||
if (urlsNotSelectedYet.length > 0) {
|
||||
onSelectUrl([
|
||||
...selectedUrls,
|
||||
...(urlIdx > idxLatest ? newSelectedUrls : newSelectedUrls.reverse()),
|
||||
])
|
||||
} else {
|
||||
const urlsToRemove = newSelectedUrls.concat([selectedUrls[selectedUrls.length - 1]])
|
||||
onSelectUrl(selectedUrls.filter((x) => !urlsToRemove.includes(x)))
|
||||
}
|
||||
} else {
|
||||
const isSelected = selectedUrls.includes(url)
|
||||
const newSelectedUrls = isSelected
|
||||
? selectedUrls.filter((selectedUrl) => selectedUrl !== url)
|
||||
: [...selectedUrls, url]
|
||||
onSelectUrl(newSelectedUrls)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="-space-y-px">
|
||||
{allowList.length > 0 ? (
|
||||
<>
|
||||
{allowList.map((url) => {
|
||||
const isSelected = selectedUrls.includes(url)
|
||||
return (
|
||||
<ValueContainer key={url} isSelected={isSelected} onClick={(e) => onClickUrl(e, url)}>
|
||||
<div className={`flex items-center gap-4 font-mono group w-full`}>
|
||||
<div className="w-5 h-5 flex items-center justify-center">
|
||||
<span className="text-foreground-lighter">
|
||||
<Globe strokeWidth={2} size={14} />
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-sm flex-grow">{url}</span>
|
||||
<div className="flex-shrink-0">
|
||||
<Checkbox_Shadcn_ checked={isSelected} onChange={(e) => onClickUrl(e, url)} />
|
||||
</div>
|
||||
</div>
|
||||
</ValueContainer>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
{URI_ALLOW_LIST_ARRAY.length > 0 ? (
|
||||
URI_ALLOW_LIST_ARRAY.map((url) => {
|
||||
return (
|
||||
<ValueContainer key={url}>
|
||||
<div className="flex items-center gap-4 font-mono">
|
||||
<span className="text-foreground-lighter">
|
||||
<Globe strokeWidth={2} size={14} />
|
||||
</span>
|
||||
<span className="text-sm">{url}</span>
|
||||
</div>
|
||||
{canUpdate && (
|
||||
<Button type="default" icon={<Trash />} onClick={() => onSelectUrlToDelete(url)}>
|
||||
Remove
|
||||
</Button>
|
||||
)}
|
||||
</ValueContainer>
|
||||
)
|
||||
})
|
||||
) : (
|
||||
<div
|
||||
className={[
|
||||
@@ -88,3 +52,5 @@ export const RedirectUrlList = ({
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RedirectUrlList
|
||||
@@ -1,12 +1,11 @@
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { ExternalLink, Trash } from 'lucide-react'
|
||||
import { useParams } from 'common'
|
||||
import { ExternalLink } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import { object, string } from 'yup'
|
||||
|
||||
import { Label } from '@ui/components/shadcn/ui/label'
|
||||
import { useParams } from 'common'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { FormHeader } from 'components/ui/Forms/FormHeader'
|
||||
import { HorizontalShimmerWithIcon } from 'components/ui/Shimmers/Shimmers'
|
||||
@@ -20,17 +19,15 @@ import {
|
||||
Button,
|
||||
DialogSectionSeparator,
|
||||
Form,
|
||||
Input,
|
||||
Modal,
|
||||
WarningIcon,
|
||||
} from 'ui'
|
||||
import { Input } from 'ui-patterns/DataInputs/Input'
|
||||
import { urlRegex } from '../Auth.constants'
|
||||
import { RedirectUrlList } from './RedirectUrlList'
|
||||
import { ValueContainer } from './ValueContainer'
|
||||
import RedirectUrlList from './RedirectUrlList'
|
||||
import ValueContainer from './ValueContainer'
|
||||
|
||||
const MAX_URLS_LENGTH = 2 * 1024
|
||||
|
||||
export const RedirectUrls = () => {
|
||||
const RedirectUrls = () => {
|
||||
const { ref: projectRef } = useParams()
|
||||
const {
|
||||
data: authConfig,
|
||||
@@ -48,8 +45,7 @@ export const RedirectUrls = () => {
|
||||
}, [authConfig?.URI_ALLOW_LIST])
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
const [openRemoveSelected, setOpenRemoveSelected] = useState(false)
|
||||
const [selectedUrls, setSelectedUrls] = useState<string[]>([])
|
||||
const [selectedUrlToDelete, setSelectedUrlToDelete] = useState<string>()
|
||||
|
||||
const canUpdateConfig = useCheckPermissions(PermissionAction.UPDATE, 'custom_config_gotrue')
|
||||
|
||||
@@ -68,7 +64,7 @@ export const RedirectUrls = () => {
|
||||
|
||||
const payloadString = payload.toString()
|
||||
|
||||
if (payloadString.length > MAX_URLS_LENGTH) {
|
||||
if (payloadString.length > 2 * 1024) {
|
||||
return toast.error('Too many redirect URLs, please remove some or try to use wildcards')
|
||||
}
|
||||
|
||||
@@ -86,25 +82,21 @@ export const RedirectUrls = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const onConfirmDeleteUrl = async (urls?: string[]) => {
|
||||
if (!urls || urls.length === 0) return
|
||||
const onConfirmDeleteUrl = async (url?: string) => {
|
||||
if (!url) return
|
||||
|
||||
// Remove selectedUrl from array and update
|
||||
const payload = URI_ALLOW_LIST_ARRAY.filter((url: string) => !selectedUrls.includes(url))
|
||||
const payloadString = payload.join(',')
|
||||
if (payloadString.length > MAX_URLS_LENGTH) {
|
||||
return toast.error('Too many redirect URLs, please remove some or try to use wildcards')
|
||||
}
|
||||
const payload = URI_ALLOW_LIST_ARRAY.filter((e: string) => e !== url)
|
||||
|
||||
updateAuthConfig(
|
||||
{ projectRef: projectRef!, config: { URI_ALLOW_LIST: payloadString } },
|
||||
{ projectRef: projectRef!, config: { URI_ALLOW_LIST: payload.toString() } },
|
||||
{
|
||||
onError: (error) => {
|
||||
toast.error(`Failed to remove URL(s): ${error?.message}`)
|
||||
toast.error(`Failed to remove URL: ${error?.message}`)
|
||||
},
|
||||
onSuccess: () => {
|
||||
setSelectedUrls([])
|
||||
setOpenRemoveSelected(false)
|
||||
toast.success('Successfully removed URL(s)')
|
||||
setSelectedUrlToDelete(undefined)
|
||||
toast.success('Successfully removed URL')
|
||||
},
|
||||
}
|
||||
)
|
||||
@@ -118,51 +110,27 @@ export const RedirectUrls = () => {
|
||||
description={`URLs that auth providers are permitted to redirect to post authentication. Wildcards are allowed, for example, https://*.domain.com`}
|
||||
/>
|
||||
<div className="flex items-center gap-2 mb-6 ml-12">
|
||||
{selectedUrls.length > 0 ? (
|
||||
<>
|
||||
<Button type="default" onClick={() => setSelectedUrls([])}>
|
||||
Clear selection
|
||||
</Button>
|
||||
<ButtonTooltip
|
||||
type="default"
|
||||
disabled={!canUpdateConfig}
|
||||
tooltip={{
|
||||
content: {
|
||||
side: 'bottom',
|
||||
text: 'You need additional permissions to remove redirect URLs',
|
||||
},
|
||||
}}
|
||||
icon={<Trash />}
|
||||
onClick={() => (selectedUrls.length > 0 ? setOpenRemoveSelected(true) : null)}
|
||||
>
|
||||
Remove ({selectedUrls.length})
|
||||
</ButtonTooltip>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Button asChild type="default" icon={<ExternalLink />}>
|
||||
<Link
|
||||
href="https://supabase.com/docs/guides/auth/concepts/redirect-urls"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Documentation
|
||||
</Link>
|
||||
</Button>
|
||||
<ButtonTooltip
|
||||
disabled={!canUpdateConfig}
|
||||
onClick={() => setOpen(true)}
|
||||
tooltip={{
|
||||
content: {
|
||||
side: 'bottom',
|
||||
text: 'You need additional permissions to update redirect URLs',
|
||||
},
|
||||
}}
|
||||
>
|
||||
Add URL
|
||||
</ButtonTooltip>
|
||||
</>
|
||||
)}
|
||||
<Button asChild type="default" icon={<ExternalLink />}>
|
||||
<Link
|
||||
href="https://supabase.com/docs/guides/auth/concepts/redirect-urls"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Documentation
|
||||
</Link>
|
||||
</Button>
|
||||
<ButtonTooltip
|
||||
disabled={!canUpdateConfig}
|
||||
onClick={() => setOpen(true)}
|
||||
tooltip={{
|
||||
content: {
|
||||
side: 'bottom',
|
||||
text: 'You need additional permissions to update redirect URLs',
|
||||
},
|
||||
}}
|
||||
>
|
||||
Add URL
|
||||
</ButtonTooltip>
|
||||
</div>
|
||||
</div>
|
||||
{isLoading && (
|
||||
@@ -184,10 +152,9 @@ export const RedirectUrls = () => {
|
||||
)}
|
||||
{isSuccess && (
|
||||
<RedirectUrlList
|
||||
allowList={URI_ALLOW_LIST_ARRAY}
|
||||
selectedUrls={selectedUrls}
|
||||
onSelectUrl={setSelectedUrls}
|
||||
URI_ALLOW_LIST_ARRAY={URI_ALLOW_LIST_ARRAY}
|
||||
canUpdate={canUpdateConfig}
|
||||
onSelectUrlToDelete={setSelectedUrlToDelete}
|
||||
/>
|
||||
)}
|
||||
<Modal
|
||||
@@ -208,9 +175,8 @@ export const RedirectUrls = () => {
|
||||
{() => {
|
||||
return (
|
||||
<>
|
||||
<Modal.Content className="flex flex-col gap-y-2">
|
||||
<Label htmlFor="url">URL</Label>
|
||||
<Input id="url" name="url" placeholder="https://mydomain.com" />
|
||||
<Modal.Content>
|
||||
<Input id="url" name="url" label="URL" placeholder="https://mydomain.com" />
|
||||
</Modal.Content>
|
||||
<DialogSectionSeparator />
|
||||
<Modal.Content>
|
||||
@@ -232,27 +198,18 @@ export const RedirectUrls = () => {
|
||||
</Modal>
|
||||
<Modal
|
||||
hideFooter
|
||||
size="large"
|
||||
visible={openRemoveSelected}
|
||||
header="Remove URLs"
|
||||
onCancel={() => {
|
||||
setSelectedUrls([])
|
||||
setOpenRemoveSelected(false)
|
||||
}}
|
||||
size="small"
|
||||
visible={selectedUrlToDelete !== undefined}
|
||||
header="Remove URL"
|
||||
onCancel={() => setSelectedUrlToDelete(undefined)}
|
||||
>
|
||||
<Modal.Content>
|
||||
<p className="mb-2 text-sm text-foreground-light">
|
||||
Are you sure you want to remove the following URLs?
|
||||
Are you sure you want to remove{' '}
|
||||
<span className="text-foreground">{selectedUrlToDelete}</span>?
|
||||
</p>
|
||||
<ul className="list-disc pl-4 mb-2">
|
||||
{selectedUrls.map((url, index) => (
|
||||
<li key={index} className="text-foreground-light text-sm">
|
||||
{url}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-foreground-light text-sm">
|
||||
These URLs will no longer work with your authentication configuration.
|
||||
This URL will no longer work with your authentication configuration.
|
||||
</p>
|
||||
</Modal.Content>
|
||||
<Modal.Separator />
|
||||
@@ -261,10 +218,7 @@ export const RedirectUrls = () => {
|
||||
block
|
||||
type="default"
|
||||
size="medium"
|
||||
onClick={() => {
|
||||
setSelectedUrls([])
|
||||
setOpenRemoveSelected(false)
|
||||
}}
|
||||
onClick={() => setSelectedUrlToDelete(undefined)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
@@ -273,7 +227,7 @@ export const RedirectUrls = () => {
|
||||
size="medium"
|
||||
type="warning"
|
||||
loading={isUpdatingConfig}
|
||||
onClick={() => onConfirmDeleteUrl(selectedUrls)}
|
||||
onClick={() => onConfirmDeleteUrl(selectedUrlToDelete)}
|
||||
>
|
||||
{isUpdatingConfig ? 'Removing...' : 'Remove URL'}
|
||||
</Button>
|
||||
@@ -282,3 +236,5 @@ export const RedirectUrls = () => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default RedirectUrls
|
||||
@@ -1,29 +1,17 @@
|
||||
import { PropsWithChildren, MouseEvent } from 'react'
|
||||
import { PropsWithChildren } from 'react'
|
||||
|
||||
import { cn } from 'ui'
|
||||
|
||||
interface ValueContainerProps {
|
||||
isSelected?: boolean
|
||||
onClick?: (event: MouseEvent<HTMLElement>) => void
|
||||
}
|
||||
|
||||
export const ValueContainer = ({
|
||||
children,
|
||||
isSelected = false,
|
||||
onClick,
|
||||
}: PropsWithChildren<ValueContainerProps>) => (
|
||||
const ValueContainer = ({ children }: PropsWithChildren<{}>) => (
|
||||
<div
|
||||
className={cn(
|
||||
'bg-surface-100 hover:bg-surface-200 border-default text-foreground flex items-center',
|
||||
'transition justify-between gap-2 border px-6 py-4 text-sm',
|
||||
'first:rounded-tr first:rounded-tl last:rounded-br last:rounded-bl',
|
||||
isSelected ? 'bg-surface-300' : '',
|
||||
onClick ? 'cursor-pointer' : ''
|
||||
)}
|
||||
onClick={(e) => {
|
||||
if (onClick) onClick(e)
|
||||
}}
|
||||
className="
|
||||
bg-surface-100 border-default text-foreground flex items-center
|
||||
justify-between gap-2
|
||||
border px-6
|
||||
py-4 text-sm
|
||||
first:rounded-tr first:rounded-tl last:rounded-br last:rounded-bl
|
||||
"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
|
||||
export default ValueContainer
|
||||
@@ -1,6 +1,6 @@
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
|
||||
import { RedirectUrls } from 'components/interfaces/Auth/RedirectUrls/RedirectUrls'
|
||||
import RedirectUrls from 'components/interfaces/Auth/RedirectUrls/RedirectUrls'
|
||||
import SiteUrl from 'components/interfaces/Auth/SiteUrl/SiteUrl'
|
||||
import AuthLayout from 'components/layouts/AuthLayout/AuthLayout'
|
||||
import { FormsContainer } from 'components/ui/Forms/FormsContainer'
|
||||
|
||||
Reference in new issue
Block a user