Files
supabase/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx
T
b91532f6c7 Migrate to styling tokens (#18314)
* migrate some www components to tokens

* consolidate InteractiveShimmerCard to Panel component

* update tokens in blog

* update tokens in careers page

* update tokens in customers section

* update tokens in open-source section

* update tokens in Realtime page

* update tokens in Storage and Vector

* update tokens in SplitCodeBlockCarousel

* update tokens in PGCharts

* remove unused css files

* update tokens in Card

* update tokens in Pricing page

* clean up priving page imports

* remove hardcoded theme vars

* migrate first half of defaultTheme.ts to tokens

* migrate second half of defaultTheme.ts to tokens

* improve inputs

* add foreground to text-light and text-lighter

* add foreground to text-light and text-lighter

* migrate docs components with styling tokens

* migrate docs components with styling tokens

* fix broken Repos component

* fix broken classes in blog

* update tokens on Button and other components

* update tokens on IconPanel

* update studio main layout base styling tokens

* update tokens across studio, docs and www

* update tokens across studio, docs and www

* update ui/Panel to styling tokens

* update ExampleProject and TableEditorMenu tokens

* www vector page tokens

* update studio UI tokens

* update other studio UI tokens

* update more studio UI tokens

* change tokens here, change tokens there

* finish updating colors with tokens variables

* add gui sandbox for theme experimentation

* use common package for www, docs and studio and fix Command K tokens

* provide light mode default tokens options

* fix conflict leftover

* update loading line

* fix className typo

* fix prettier

* update themeSandbox preset default values

* fix text-background0

* prettier

* update warningBanner with warning color

* switch all border-border with border-default

* improve border-secondary and foreground-muted in light mode

* force ring color on toggle

* fix button bg color and border-muted light token

* fix input bg color

* fix dark button hover

* fix homepage product card

* fix code-hike table header colors

* button dark border

* remove tabIndex leftover from homepage main ctas

---------

Co-authored-by: Kevin Grüneberg <k.grueneberg1994@gmail.com>
Co-authored-by: Jonathan Summers-Muir <MildTomato@users.noreply.github.com>
2023-11-07 03:40:53 +00:00

241 lines
9.5 KiB
TypeScript

import * as Tooltip from '@radix-ui/react-tooltip'
import { Address4 } from 'ip-address'
import { useState } from 'react'
import { Button, Form, IconHelpCircle, Input, Modal } from 'ui'
import { useParams } from 'common/hooks'
import InformationBox from 'components/ui/InformationBox'
import { useNetworkRestrictionsApplyMutation } from 'data/network-restrictions/network-retrictions-apply-mutation'
import { useStore } from 'hooks'
import { checkIfPrivate, getAddressEndRange } from './NetworkRestrictions.utils'
interface AddRestrictionModalProps {
restrictedIps: string[]
visible: boolean
hasOverachingRestriction: boolean
onClose: () => void
}
const AddRestrictionModal = ({
restrictedIps,
visible,
hasOverachingRestriction,
onClose,
}: AddRestrictionModalProps) => {
const formId = 'add-restriction-form'
const { ui } = useStore()
const { ref } = useParams()
const { mutate: applyNetworkRestrictions, isLoading: isApplying } =
useNetworkRestrictionsApplyMutation({ onSuccess: () => onClose() })
const validate = (values: any) => {
const errors: any = {}
const { ipAddress, cidrBlockSize } = values
// Validate CIDR block size
const isOutOfCidrSizeRange = cidrBlockSize < 0 || cidrBlockSize > 32
if (cidrBlockSize.length === 0 || isOutOfCidrSizeRange) {
errors.cidrBlockSize = 'Size has to be between 0 to 32'
}
// Validate IP address
const isValid = Address4.isValid(ipAddress)
if (!isValid) {
errors.ipAddress = 'Please enter a valid IP address'
return errors
}
const isPrivate = checkIfPrivate(ipAddress)
if (isPrivate) errors.ipAddress = 'Private IP addresses are not supported'
return errors
}
const onSubmit = async (values: any) => {
if (!ref) return console.error('Project ref is required')
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]
applyNetworkRestrictions({ projectRef: ref, dbAllowedCidrs })
}
return (
<Modal
closable
hideFooter
size="medium"
visible={visible}
onCancel={onClose}
header="Add a new restriction"
>
<Form
validateOnBlur
id={formId}
className="!border-t-0"
initialValues={{ ipAddress: '', cidrBlockSize: 32 }}
validate={validate}
onSubmit={onSubmit}
>
{({ values, resetForm }: 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
// [Alaister] although this "technically" is breaking the rules of React hooks
// it won't error because the hooks are always rendered in the same order
// eslint-disable-next-line react-hooks/rules-of-hooks
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()
if (geoplugin_request) {
const updatedValues = { ...values, ipAddress: geoplugin_request, cidrBlockSize: 32 }
resetForm({ initialValues: updatedValues, values: updatedValues })
} else {
ui.setNotification({
category: 'error',
duration: 4000,
message: 'Failed to retrieve client IP address, please enter address manually',
})
}
setIsFetchingAddress(false)
}
return (
<>
<Modal.Content>
<div className="py-6 space-y-4">
<p className="text-sm text-foreground-light">
This will add an IP address range to a list of allowed ranges that can access
your database. Only IPv4 addresses are supported at the moment.
</p>
<InformationBox
title="Note: Restrictions only apply to direct connections to your database and PgBouncer"
description="They do not currently apply to Supavisor and to APIs offered over HTTPS, such as PostgREST, Storage, or Authentication"
/>
<div className="flex space-x-4">
<div className="w-[55%]">
<Input
label="IPv4 address"
id="ipAddress"
name="ipAddress"
placeholder="0.0.0.0"
className=""
/>
</div>
<div className="flex-grow">
<Input
label={
<div className="flex items-center space-x-2">
<p>CIDR Block Size</p>
<Tooltip.Root delayDuration={0}>
<Tooltip.Trigger>
<IconHelpCircle size="tiny" strokeWidth={2} />
</Tooltip.Trigger>
<Tooltip.Portal>
<Tooltip.Content side="bottom">
<Tooltip.Arrow className="radix-tooltip-arrow" />
<div
className={[
'rounded bg-alternative py-1 px-2 leading-none shadow',
'border border-background w-[300px]',
].join(' ')}
>
<span className="text-xs text-foreground">
Classless inter-domain routing (CIDR) notation is the notation
used to identify networks and hosts in the networks. The block
size tells us how many bits we need to take for the network
prefix, and is a value between 0 to 32.
</span>
</div>
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
</div>
}
id="cidrBlockSize"
name="cidrBlockSize"
type="number"
placeholder="32"
min={0}
max={32}
/>
</div>
</div>
<Button
type="default"
loading={isFetchingAddress}
disabled={isFetchingAddress}
onClick={() => getClientIpAddress()}
>
Use my IP address
</Button>
</div>
</Modal.Content>
<Modal.Separator />
{isValidCIDR ? (
<Modal.Content>
<div className="space-y-1 pt-2 pb-4">
<p className="text-sm">
The address range{' '}
<code className="text-xs">
{values.ipAddress}/{values.cidrBlockSize}
</code>{' '}
will be restricted
</p>
<p className="text-sm text-foreground-light">
Selected address space: <code className="text-xs">{addressRange.start}</code>{' '}
to <code className="text-xs">{addressRange.end}</code>{' '}
</p>
<p className="text-sm text-foreground-light">
Number of addresses: {availableAddresses}
</p>
</div>
</Modal.Content>
) : (
<Modal.Content>
<div className="pt-2 pb-4">
<div className="h-[68px] flex items-center">
<p className="text-sm text-foreground-light">
A summary of your restriction will be shown here after entering a valid IP
address and CIDR block size
</p>
</div>
</div>
</Modal.Content>
)}
<div className="flex items-center justify-end px-6 py-4 border-t space-x-2">
<Button type="default" disabled={isApplying} onClick={() => onClose()}>
Cancel
</Button>
<Button htmlType="submit" loading={isApplying} disabled={isApplying}>
Save restriction
</Button>
</div>
</>
)
}}
</Form>
</Modal>
)
}
export default AddRestrictionModal