Merge pull request #4240 from supabase/master

Prod deploy for studio
This commit is contained in:
Joshen Lim authored and GitHub committed 2021-12-02 15:43:24 +08:00
commit 8e321c486d
39 files changed
+5791 -4952

No files matched your search

+2
View File
@@ -37,7 +37,9 @@ Project settings are managed outside of the Dashboard. If you use docker-compose
- [MildTomato](https://github.com/mildtomato), [phamhieu](https://github.com/phamhieu), [joshenlim](https://github.com/joshenlim)
### Developer Quickstart
```bash
# You'll need to be on Node v14
# in /studio
npm i # install deps
@@ -4,7 +4,7 @@ import { observer } from 'mobx-react-lite'
import { API_URL, IS_PLATFORM } from 'lib/constants'
import { useStore } from 'hooks'
import WithSidebar from 'components/layouts/WithSidebar'
import WithSidebar from './WithSidebar'
/**
* layout for dashboard homepage, account and org settings
@@ -105,13 +105,7 @@ const AccountLayout = ({ children, title, breadcrumbs }: any) => {
style={{ maxHeight: '100vh' }}
className="w-full flex flex-col flex-1 overflow-y-auto"
>
{/* @ts-ignore */}
<WithSidebar
breadcrumbs={breadcrumbs}
links={linksWithHeaders}
title={title}
hideIconSidebar
>
<WithSidebar title={title} breadcrumbs={breadcrumbs} links={linksWithHeaders}>
{children}
</WithSidebar>
</main>
@@ -1,25 +1,22 @@
import Link from 'next/link'
// import IconSidebar from 'components/Layouts/IconSidebar'
import { CSSProperties, FC, ReactNode } from 'react'
import { Menu, Typography, IconArrowUpRight, Badge } from '@supabase/ui'
import LayoutHeader from './ProjectLayout/LayoutHeader'
import LayoutHeader from '../ProjectLayout/LayoutHeader'
/**
* layout for all pages under /project/ route
*
* @param {String} title
* @param {JSX.Element|JSX.Element[]} header
* @param {JSX.Element|JSX.Element[]} children
* @param {CSSProperties} sidebarStyle
* @param {Array<Object>} icons 1st level group links
* @param {Array<Object>} links 2nd level links related to group link
* @param {Array<Object>} subitems subitems link to different sections on the same page
* @param {Number} subitemsParentKey index of subitems parent link
* @param {boolean} hideIconSidebar Hides the left-most icon bar
* @param {JSX.Element|JSX.Element[]} customSidebarContent For injecting custom JSX into the sidebar
* @param {string} projectRef Project ref string used in url
*/
interface Props {
title: string
breadcrumbs: any[]
links: any[]
header?: ReactNode
subitems?: any[]
subitemsParentKey?: number
sidebarStyle?: CSSProperties
hideSidebar?: boolean
customSidebarContent?: ReactNode
children: ReactNode
}
const WithSidebar = ({
const WithSidebar: FC<Props> = ({
title,
header,
breadcrumbs = [],
@@ -28,18 +25,14 @@ const WithSidebar = ({
links,
subitems,
subitemsParentKey,
icons = [],
hideIconSidebar = false,
hideSidebar = false,
customSidebarContent,
projectRef,
}) => {
const noContent = !links && !customSidebarContent
const linksHaveHeaders = links && links[0].heading
return (
<div className={`flex `}>
{/* {!hideIconSidebar && <IconSidebar links={icons} projectRef={projectRef} key="IconSidebar" />} */}
{!hideSidebar && !noContent && (
<div
id="with-sidebar"
@@ -89,8 +82,8 @@ const WithSidebar = ({
}
export default WithSidebar
const LinksWithHeaders = ({ links, subitems, subitemsParentKey }) => {
return links.map((x) => (
const LinksWithHeaders: FC<any> = ({ links, subitems, subitemsParentKey }) => {
return links.map((x: any) => (
<div key={x.heading} className="py-5 border-b dark:border-dark px-3">
<Menu.Group title={x.heading} />
{x.versionLabel && (
@@ -108,13 +101,14 @@ const LinksWithHeaders = ({ links, subitems, subitemsParentKey }) => {
</div>
))
}
const LinksWithoutHeaders = ({ links, subitems, subitemsParentKey }) => {
const LinksWithoutHeaders: FC<any> = ({ links, subitems, subitemsParentKey }) => {
return (
<ul className="dash-product-menu space-y-1">
{links.map((x, i) => {
{links.map((x: any, i: number) => {
// disable active state for link with subitems
const isActive = x.isActive && !subitems
let render = (
let render: any = (
<SidebarItem
key={`${x.key || x.as}-${i}-sidebarItem`}
id={`${x.key || x.as}-${i}`}
@@ -127,8 +121,9 @@ const LinksWithoutHeaders = ({ links, subitems, subitemsParentKey }) => {
external={x.external || false}
/>
)
if (subitems && x.subitemsKey === subitemsParentKey) {
const subItemsRender = subitems.map((y, i) => (
const subItemsRender = subitems.map((y: any, i: number) => (
<SidebarItem
key={`${y.key || y.as}-${i}-sidebarItem`}
id={`${y.key || y.as}-${i}`}
@@ -141,24 +136,24 @@ const LinksWithoutHeaders = ({ links, subitems, subitemsParentKey }) => {
))
render = [render, ...subItemsRender]
}
return render
})}
</ul>
)
}
const SidebarItem = ({ id, label, as, href, slug, isActive, isSubitem, onClick, external }) => {
const SidebarItem: FC<any> = ({ id, label, as, href, isActive, isSubitem, onClick, external }) => {
return (
<Link href={href || ''} as={as || ''}>
<a className="block" target={external ? '_blank' : '_self'}>
<Menu.Item
rounded
key={id}
style={{
marginLeft: isSubitem && '.5rem',
}}
active={isActive ? true : false}
key={id}
id={slug}
onClick={onClick || (() => {})}
icon={external && <IconArrowUpRight size={'tiny'} />}
>
@@ -1,9 +1,9 @@
import React from 'react'
import { FC } from 'react'
import Link from 'next/link'
import BaseLayout from 'components/layouts'
import { IconChevronRight, Typography } from '@supabase/ui'
const WizardLayout = ({ organization, project, children }) => {
const WizardLayout: FC<any> = ({ organization, project, children }) => {
return (
<BaseLayout hideHeader hideIconBar>
<div className="flex flex-col h-full w-full">
@@ -19,7 +19,7 @@ const WizardLayout = ({ organization, project, children }) => {
}
export default WizardLayout
const Header = ({ organization, project }) => {
const Header: FC<any> = ({ organization, project }) => {
let stepNumber = organization ? 1 : project ? 2 : 0
return (
<div className="p-3 border-b dark:border-dark">
@@ -50,13 +50,13 @@ const Header = ({ organization, project }) => {
<Typography.Text type="secondary">
<IconChevronRight size="small" />
</Typography.Text>
<Typography.Text small type={stepNumber < 1 && 'secondary'}>
<Typography.Text small type={stepNumber < 1 ? 'secondary' : 'default'}>
<a>{project ? project.name : 'Create a new project'}</a>
</Typography.Text>
<Typography.Text type="secondary">
<IconChevronRight size="small" />
</Typography.Text>
<Typography.Text small type={stepNumber < 2 && 'secondary'}>
<Typography.Text small type={stepNumber < 2 ? 'secondary' : 'default'}>
<a>{project ? project.name : 'Extend your database'}</a>
</Typography.Text>
</div>
+7
View File
@@ -7,6 +7,10 @@ import DocsLayout from './DocsLayout/DocsLayout'
import SettingsLayout from './SettingsLayout/SettingsLayout'
import StorageLayout from './StorageLayout/StorageLayout'
import AccountLayout from './AccountLayout/AccountLayout'
import WizardLayout from './WizardLayout'
import VercelIntegrationLayout from './VercelIntegrationLayout'
export {
AuthLayout,
DatabaseLayout,
@@ -15,5 +19,8 @@ export {
SQLEditorLayout,
SettingsLayout,
StorageLayout,
AccountLayout,
WizardLayout,
VercelIntegrationLayout,
}
export default ProjectLayout
@@ -1,8 +1,6 @@
import * as React from 'react'
import { Typography, IconAlertCircle } from '@supabase/ui'
export default function AlphaPreview() {
const AlphaPreview = () => {
return (
<div className="block w-full bg-yellow-500 bg-opacity-5 p-3 border border-yellow-500 border-opacity-50 rounded">
<div className="flex space-x-3">
@@ -21,3 +19,5 @@ export default function AlphaPreview() {
</div>
)
}
export default AlphaPreview
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react'
import { FC, useEffect, useState } from 'react'
import { Button, Dropdown, IconChevronDown } from '@supabase/ui'
import dayjs from 'dayjs'
import { DATE_FORMAT } from 'lib/constants'
@@ -11,7 +11,13 @@ import { DATE_FORMAT } from 'lib/constants'
* Otherwise there won't be any data shown on the graphs
*/
function DateRangePicker({ onChange, value, options, loading, currentBillingPeriodStart }) {
const DateRangePicker: FC<any> = ({
onChange,
value,
options,
loading,
currentBillingPeriodStart,
}) => {
const [timePeriod, setTimePeriod] = useState(value)
useEffect(() => {
@@ -23,7 +29,7 @@ function DateRangePicker({ onChange, value, options, loading, currentBillingPeri
const today = dayjs().format(DATE_FORMAT)
function handleChange(x) {
function handleChange(x: any) {
setTimePeriod(x)
switch (x) {
@@ -164,7 +170,7 @@ function DateRangePicker({ onChange, value, options, loading, currentBillingPeri
overlay={
<>
<Dropdown.RadioGroup value={timePeriod} onChange={(x) => handleChange(x)}>
{options.map((option) => {
{options.map((option: any) => {
return (
<Dropdown.Radio value={option.key} key={option.key}>
{option.label}
@@ -176,7 +182,7 @@ function DateRangePicker({ onChange, value, options, loading, currentBillingPeri
}
>
<Button as="span" type="default" iconRight={<IconChevronDown />}>
{timePeriod && options.find((x) => x.key == timePeriod).label}
{timePeriod && options.find((x: any) => x.key == timePeriod).label}
</Button>
</Dropdown>
</>
@@ -1,4 +1,5 @@
import useSWR from 'swr'
import { FC } from 'react'
import { useRouter } from 'next/router'
import { get } from 'lib/common/fetch'
import { IconAlertCircle, Input, Loading, Typography } from '@supabase/ui'
@@ -8,11 +9,11 @@ import Panel from './Panel'
const { Text, Title } = Typography
export function DisplayApiSettings() {
export const DisplayApiSettings = () => {
const router = useRouter()
const { ref } = router.query
const { data, error } = useSWR(`${API_URL}/props/project/${ref}/settings`, get)
const { data, error }: any = useSWR(`${API_URL}/props/project/${ref}/settings`, get)
if (!data)
return (
@@ -27,6 +28,7 @@ export function DisplayApiSettings() {
</div>
) : (
<div className="py-4">
{/* @ts-ignore */}
<Loading active={true} />
</div>
)}
@@ -38,15 +40,16 @@ export function DisplayApiSettings() {
// Get the API service
const API_SERVICE_ID = 1
let apiService = services.find((x) => x.app.id == API_SERVICE_ID)
let apiService = services.find((x: any) => x.app.id == API_SERVICE_ID)
let apiKeys = apiService.service_api_keys
return (
<ApiContentWrapper>
{!data ? (
// @ts-ignore
<Loading active={true} />
) : (
apiKeys.map((x, i) => (
apiKeys.map((x: any, i: number) => (
<Panel.Content
key={x.api_key}
className={
@@ -56,9 +59,10 @@ export function DisplayApiSettings() {
>
<Input
layout="horizontal"
// @ts-ignore
label={
<>
{x.tags?.split(',').map((x, i) => (
{x.tags?.split(',').map((x: any, i: number) => (
<code key={`${x}${i}`} className="text-xs bg-gray-500 text-white px-2">
{x}
</code>
@@ -91,11 +95,11 @@ export function DisplayApiSettings() {
)
}
export function DisplayConfigSettings() {
export const DisplayConfigSettings = () => {
const router = useRouter()
const { ref } = router.query
const { data, error } = useSWR(`${API_URL}/props/project/${ref}/settings`, get)
const { data, error }: any = useSWR(`${API_URL}/props/project/${ref}/settings`, get)
if (!data)
return (
@@ -112,6 +116,7 @@ export function DisplayConfigSettings() {
</div>
) : (
<div className="py-4">
{/* @ts-ignore */}
<Loading active={true} />
</div>
)}
@@ -124,14 +129,14 @@ export function DisplayConfigSettings() {
// Get the API service
const API_SERVICE_ID = 1
let jwtSecret = project.jwt_secret ?? ''
let apiService = services.find((x) => x.app.id == API_SERVICE_ID)
let apiService = services.find((x: any) => x.app.id == API_SERVICE_ID)
let apiConfig = apiService.app_config
return (
<section>
<Panel
title={
<Typography.Title level={6} className="mb-0">
<Typography.Title level={5} className="mb-0">
Project Configuration
</Typography.Title>
}
@@ -141,9 +146,7 @@ export function DisplayConfigSettings() {
label="URL"
readOnly
copy
readOnly
disabled
copy
className="input-mono"
value={`https://${apiConfig.endpoint}`}
descriptionText="A RESTful endpoint for querying and managing your database."
@@ -168,7 +171,7 @@ export function DisplayConfigSettings() {
)
}
const ApiContentWrapper = ({ children }) => {
const ApiContentWrapper: FC<any> = ({ children }) => {
return (
<Panel
title={
@@ -187,7 +190,7 @@ const ApiContentWrapper = ({ children }) => {
)
}
const ConfigContentWrapper = ({ children }) => {
const ConfigContentWrapper: FC<any> = ({ children }) => {
return (
<Panel
title={
@@ -1,7 +0,0 @@
export default function FiveOhFive({ error }) {
return (
<div className="flex h-screen">
<img className="m-auto max-w-lg" src="/img/eastwood-fatal-error.svg" alt="loading" />
</div>
)
}
@@ -1,14 +1,13 @@
import { IconGlobe, IconTool, Typography } from '@supabase/ui'
import { FC } from 'react'
import { Typography } from '@supabase/ui'
const { Text } = Typography
const EmptyPanel = ({ icon, text }) => {
const EmptyPanel: FC<any> = ({ icon, text }) => {
return (
<div className="flex items-center justify-center flex-row py-4 space-x-2">
<div className="relative bg-bg-secondary-light w-12 h-12 rounded-full flex items-center justify-center text-gray-300">
{icon}
</div>
<Text type="secondary">{text}</Text>
<Typography.Text type="secondary">{text}</Typography.Text>
</div>
)
}
@@ -1,4 +1,4 @@
import React from 'react'
import { FC } from 'react'
/**
* Render keyMap
@@ -6,18 +6,18 @@ import React from 'react'
* @param {String} keyMap ex: Command+Enter ; separate key using +
*/
const customKeyChars = {
const customKeyChars: any = {
command: '⌘',
control: '⌃',
option: '⌥',
enter: '↵',
}
export default function KeyMap({ keyMap }) {
const KeyMap: FC<any> = ({ keyMap }) => {
if (!keyMap) return null
const keys = keyMap.split('+')
const keymapRender = keys.map((key) => {
const keymapRender = keys.map((key: any) => {
let keyChar = key
if (key.toLowerCase() in customKeyChars) keyChar = customKeyChars[key.toLowerCase()]
return (
@@ -28,3 +28,5 @@ export default function KeyMap({ keyMap }) {
})
return <p className="inline-flex space-x-1.5">{keymapRender}</p>
}
export default KeyMap
@@ -1,69 +0,0 @@
import { useState, useEffect } from 'react'
import { Button, Typography } from '@supabase/ui'
import { API_URL } from 'lib/constants'
import { get } from 'lib/common/fetch'
import Panel from './Panel'
export default function LogPanel({ title, projectRef, app }) {
const [logs, setLogs] = useState([])
const [lines, setLines] = useState(10)
const [isLoading, setIsLoading] = useState(false)
const refreshLogs = async () => {
setIsLoading(true)
try {
const data = await get(`${API_URL}/projects/${projectRef}/logs?app=${app}`)
if (data.error) throw data.error
const cleansed = data.logs
.split('\n')
.filter((x) => x.trim() != '')
.reverse()
setLogs(cleansed)
} catch (error) {
console.error('Refresh logs error:', error)
} finally {
setIsLoading(false)
}
}
useEffect(() => {
refreshLogs()
}, [])
return (
<Panel
title={
<div className="flex items-center justify-between w-full">
<Typography.Title level={5} className="flex-1 block">
{title}
</Typography.Title>
<div className="flex-1 text-right">
<Button
type="outline"
className="hover:border-gray-400"
disabled={isLoading}
loading={isLoading}
onClick={() => refreshLogs()}
>
Refresh
</Button>
</div>
</div>
}
>
<div className="max-w-full w-full rounded overflow-hidden">
<div className="relative max-w-full overflow-x-auto">
{logs.map((x, i) => (
<div
key={x}
className={`block whitespace-nowrap px-4 py-3 font-mono border-t dark:border-dark`}
>
<Typography.Text>{x}</Typography.Text>
</div>
))}
</div>
</div>
</Panel>
)
}
@@ -1,7 +1,7 @@
import { Loading } from '@supabase/ui'
import React from 'react'
function Panel(props) {
function Panel(props: any) {
let headerClasses = []
if (!props.hideHeaderStyling) {
@@ -25,7 +25,7 @@ function Panel(props) {
rounded mb-8 ${props.className}`}
>
{props.title && (
<div className={headerClasses}>
<div className={headerClasses.join(' ')}>
<div className="px-6 py-4 flex items-center">{props.title}</div>
</div>
)}
@@ -44,7 +44,7 @@ function Panel(props) {
)
}
function Content(props) {
function Content(props: any) {
let classes = ['px-6 py-4']
if (props.className) classes.push(props.className)
return <div className={classes.join(' ')}>{props.children}</div>
@@ -1,21 +1,22 @@
import React, { useState, useEffect } from 'react'
import React, { FC, useState, useEffect } from 'react'
import { useRouter } from 'next/router'
import { observer } from 'mobx-react-lite'
import { toJS } from 'mobx'
import { Input, Modal } from '@supabase/ui'
import { useStore } from 'hooks'
import { useProjectContentStore } from 'stores/projectContentStore'
import { toast } from 'react-hot-toast'
const EditModal = () => {
const EditModal: FC<any> = () => {
const router = useRouter()
const { id, ref } = router.query
const [visible, setVisible] = useState(false)
const [loading, setLoading] = useState(false)
const [name, setName] = useState(null)
const [description, setDescription] = useState(null)
const { ui } = useStore()
const [visible, setVisible] = useState<boolean>(false)
const [loading, setLoading] = useState<boolean>(false)
const [name, setName] = useState<string>()
const [description, setDescription] = useState<string>()
const contentStore = useProjectContentStore(ref)
@@ -55,9 +56,8 @@ const EditModal = () => {
await contentStore.load()
setVisible(!visible)
return data
} catch (error) {
console.error(error)
toast.error(error.message)
} catch (error: any) {
ui.setNotification({ category: 'error', message: error.message })
setLoading(false)
}
}
@@ -0,0 +1,19 @@
export const NEW_REPORT_SKELETON = {
name: 'new report',
description: '',
type: 'report',
visibility: 'project',
content: {
schema_version: 1,
period_start: {
time_period: '7d',
date: '',
},
period_end: {
time_period: 'today',
date: '',
},
interval: '1d',
layout: [],
},
}
@@ -1,5 +1,5 @@
import { useProjectContentStore } from 'stores/projectContentStore'
import { NEW_REPORT_SKELETON } from 'lib/constants'
import { NEW_REPORT_SKELETON } from './Reports.constants'
import toast from 'react-hot-toast'
function handleErr(error: any) {
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import React, { useEffect, useState, useRef } from 'react'
import { FC, useEffect, useState, useRef } from 'react'
import Highlight, { defaultProps } from 'prism-react-renderer'
import defaultTheme from 'prism-react-renderer/themes/palenight'
import Clipboard from 'clipboard'
@@ -18,11 +18,11 @@ const prism = {
plugins: ['line-numbers', 'show-language'],
}
const SimpleCodeBlock = ({ children, className: languageClassName, metastring }) => {
const SimpleCodeBlock: FC<any> = ({ children, className: languageClassName, metastring }) => {
const [showCopied, setShowCopied] = useState(false)
const target = useRef(null)
const button = useRef(null)
let highlightLines = []
let highlightLines: any = []
if (metastring && highlightLinesRangeRegex.test(metastring)) {
const highlightLinesRange = metastring.match(highlightLinesRangeRegex)[1]
@@ -36,10 +36,13 @@ const SimpleCodeBlock = ({ children, className: languageClassName, metastring })
}, [showCopied])
useEffect(() => {
let clipboard
let clipboard: any
// @ts-ignore
if (button?.current?.button) {
// @ts-ignore
clipboard = new Clipboard(button.current.button, {
// @ts-ignore
target: () => target.current,
})
}
@@ -57,15 +60,14 @@ const SimpleCodeBlock = ({ children, className: languageClassName, metastring })
language = prism.defaultLanguage
}
const handleCopyCode = (code) => {
navigator.clipboard.writeText(code).then(() => setShowCopied(true))
const handleCopyCode = (code: any) => {
navigator?.clipboard?.writeText(code).then(() => setShowCopied(true))
}
return (
<Highlight
className=""
{...defaultProps}
theme={prism.theme || defaultTheme}
theme={(prism as any).theme || defaultTheme}
code={children.trim()}
language={language}
>
@@ -1,9 +1,9 @@
import Editor, { useMonaco } from '@monaco-editor/react'
import { useEffect, useRef } from 'react'
import { FC, useEffect, useRef } from 'react'
import { Typography } from '@supabase/ui'
import { useStore } from 'hooks'
const SqlEditor = ({
const SqlEditor: FC<any> = ({
queryId,
language = 'pgsql',
defaultValue = '',
@@ -13,7 +13,7 @@ const SqlEditor = ({
}) => {
const monaco = useMonaco()
const { meta } = useStore()
const editorRef = useRef()
const editorRef = useRef<any>()
useEffect(() => {
if (monaco) {
@@ -21,6 +21,7 @@ const SqlEditor = ({
monaco.editor.defineTheme('supabase', {
base: 'vs-dark',
inherit: true,
colors: {},
rules: [
{ token: '', background: '4a5568' },
{ token: 'string.sql', foreground: '24b47e' },
@@ -31,7 +32,7 @@ const SqlEditor = ({
// Enable pgsql format
const formatprovider = monaco.languages.registerDocumentFormattingEditProvider('pgsql', {
async provideDocumentFormattingEdits(model) {
async provideDocumentFormattingEdits(model: any) {
const value = model.getValue()
const formatted = await formatPgsql(value)
return [
@@ -52,7 +53,7 @@ const SqlEditor = ({
useEffect(() => {
if (editorRef.current) {
// add margin above first line
editorRef.current?.changeViewZones((accessor) => {
editorRef.current?.changeViewZones((accessor: any) => {
accessor.addZone({
afterLineNumber: 0,
heightInPx: 4,
@@ -62,7 +63,7 @@ const SqlEditor = ({
}
}, [queryId])
async function formatPgsql(value) {
async function formatPgsql(value: any) {
try {
const formatted = await meta.formatQuery(value)
if (formatted.error) throw formatted.error
@@ -73,11 +74,11 @@ const SqlEditor = ({
}
}
const onMount = (editor, monaco) => {
const onMount = (editor: any, monaco: any) => {
editorRef.current = editor
// Add margin above first line
editor.changeViewZones((accessor) => {
editor.changeViewZones((accessor: any) => {
accessor.addZone({
afterLineNumber: 0,
heightInPx: 4,
@@ -1,7 +1,8 @@
import { useRef, useEffect } from 'react'
import KeyMap from './KeyMap'
import { renderToString } from 'react-dom/server'
import { makeRandomString } from 'lib/helpers'
import KeyMap from './KeyMap'
/**
* Hook for listening to hover action on the target element and show tooltip.
@@ -17,13 +18,13 @@ import { makeRandomString } from 'lib/helpers'
*/
export default function TooltipListener({
title,
title = '',
position = 'bottom',
keyMap = null,
className = '',
nowrap,
nowrap = false,
}) {
const ref = useRef(null)
const ref = useRef<any>(null)
const tooltipId = `tooltip-${makeRandomString(5)}`
const tooltip = document.createElement('div')
tooltip.id = tooltipId
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react'
import React, { FC, useEffect, useState } from 'react'
import useSWR from 'swr'
import { Loading, Typography } from '@supabase/ui'
import { toast } from 'react-hot-toast'
@@ -32,10 +32,11 @@ const usageLimits = {
title: 'Database space',
tiers: {
free: {
description: (stats) => `${(((stats?.dbSize || 0) / (500 * MB)) * 100).toFixed(2)} %`,
render: (stats) => {
let bytes = stats?.dbSize ? new Number(stats?.dbSize) : 0
let usage = {
description: (stats: any) =>
`${(((stats?.dbSize || 0) / (500 * MB)) * 100).toFixed(2)} %`,
render: (stats: any) => {
const bytes: any = stats?.dbSize ? new Number(stats?.dbSize) : 0
const usage = {
gb: bytes / GB || 0,
mb: bytes / MB || 0,
}
@@ -52,10 +53,11 @@ const usageLimits = {
},
},
pro: {
description: (stats) => `${(((stats?.dbSize || 0) / (8000 * MB)) * 100).toFixed(2)} %`,
render: (stats) => {
let bytes = stats?.dbSize ? new Number(stats?.dbSize) : 0
let usage = {
description: (stats: any) =>
`${(((stats?.dbSize || 0) / (8000 * MB)) * 100).toFixed(2)} %`,
render: (stats: any) => {
const bytes: any = stats?.dbSize ? new Number(stats?.dbSize) : 0
const usage = {
gb: bytes / GB || 0,
mb: bytes / MB || 0,
}
@@ -83,27 +85,28 @@ const usageLimits = {
title: 'Users',
tiers: {
free: {
description: (stats) => `${(((stats?.authUsers || 0) / 10000) * 100).toFixed(2)}%`,
render: (stats) => (
description: (stats: any) => `${(((stats?.authUsers || 0) / 10000) * 100).toFixed(2)}%`,
render: (stats: any) => (
<SparkBar
value={stats?.authUsers || 0}
max={10000}
type={'horizontal'}
barClass={'bg-green-500'}
labelBottom={stats?.authUsers ? new Number(stats?.authUsers).toLocaleString() : 0}
labelBottom={stats?.authUsers ? new Number(stats?.authUsers).toLocaleString() : '0'}
labelTop={`${new Number(10000).toLocaleString()}`}
/>
),
},
pro: {
description: (stats) => `${(((stats?.authUsers || 0) / 100000) * 100).toFixed(2)}%`,
render: (stats) => (
description: (stats: any) =>
`${(((stats?.authUsers || 0) / 100000) * 100).toFixed(2)}%`,
render: (stats: any) => (
<SparkBar
value={stats?.authUsers || 0}
max={100000}
type={'horizontal'}
barClass={'bg-green-500'}
labelBottom={stats?.authUsers ? new Number(stats?.authUsers).toLocaleString() : 0}
labelBottom={stats?.authUsers ? new Number(stats?.authUsers).toLocaleString() : '0'}
labelTop={`${new Number(100000).toLocaleString()}`}
/>
),
@@ -133,8 +136,9 @@ const usageLimits = {
title: 'Storage space',
tiers: {
free: {
description: (stats) => `${(((stats?.bucketSize || 0) / (1 * GB)) * 100).toFixed(2)}%`,
render: (stats) => (
description: (stats: any) =>
`${(((stats?.bucketSize || 0) / (1 * GB)) * 100).toFixed(2)}%`,
render: (stats: any) => (
<SparkBar
value={stats?.bucketSize / (1024 * 1024) || 0}
max={1 * 1024}
@@ -143,16 +147,16 @@ const usageLimits = {
labelBottom={
stats?.bucketSize
? `${new Number(stats?.bucketSize / (1024 * 1024)).toLocaleString()} MB`
: 0
: '0'
}
labelTop={`${new Number(1 * 1024).toLocaleString()} MB`}
/>
),
},
pro: {
description: (stats) =>
description: (stats: any) =>
`${(((stats?.bucketSize || 0) / (100 * GB)) * 100).toFixed(2)}%`,
render: (stats) => (
render: (stats: any) => (
<SparkBar
value={stats?.bucketSize / (1024 * 1024) || 0}
max={100 * 1024}
@@ -161,7 +165,7 @@ const usageLimits = {
labelBottom={
stats?.bucketSize
? `${new Number(stats?.bucketSize / (1024 * 1024)).toLocaleString()} MB`
: 0
: '0'
}
labelTop={`${new Number(100).toLocaleString()} GB`}
/>
@@ -186,12 +190,12 @@ const usageLimits = {
},
}
export default function ProjectUsage({ projectRef, subscription_id }) {
const ProjectUsage: FC<any> = ({ projectRef, subscription_id }) => {
const { data: stats, error: usageError } = useSWR(`${API_URL}/projects/${projectRef}/usage`, get)
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
const [subscription, setSubscription] = useState(null)
const [loading, setLoading] = useState<boolean>(false)
const [error, setError] = useState<any>(null)
const [subscription, setSubscription] = useState<any>(null)
useEffect(() => {
let cancel = false
@@ -208,7 +212,7 @@ export default function ProjectUsage({ projectRef, subscription_id }) {
})
if (!cancel) setSubscription(data)
if (error) throw error
} catch (error) {
} catch (error: any) {
toast.error(error.message)
if (!cancel) setError(error)
} finally {
@@ -269,6 +273,7 @@ export default function ProjectUsage({ projectRef, subscription_id }) {
{feature.tiers[tier]?.description(stats)}
</td>
<td className="px-6 py-3 text-sm text-typography-body-light dark:text-typography-body-dark ">
{/* @ts-ignore */}
{feature.tiers[tier]?.render ? feature.tiers[tier].render(stats) : null}
</td>
</tr>
@@ -283,12 +288,14 @@ export default function ProjectUsage({ projectRef, subscription_id }) {
)
}
export function ProjectUsageMinimal({ projectRef, subscription_id, filter }) {
export default ProjectUsage
export const ProjectUsageMinimal: FC<any> = ({ projectRef, subscription_id, filter }) => {
const { data: stats, error: usageError } = useSWR(`${API_URL}/projects/${projectRef}/usage`, get)
const [loading, setLoading] = useState(false)
const [error, setError] = useState(null)
const [subscription, setSubscription] = useState(null)
const [loading, setLoading] = useState<boolean>(false)
const [error, setError] = useState<any>(null)
const [subscription, setSubscription] = useState<any>(null)
useEffect(() => {
let cancel = false
@@ -305,7 +312,7 @@ export function ProjectUsageMinimal({ projectRef, subscription_id, filter }) {
})
if (!cancel) setSubscription(data)
if (error) throw error
} catch (error) {
} catch (error: any) {
if (!cancel) setError(error)
} finally {
if (!cancel) setLoading(false)
@@ -338,6 +345,7 @@ export function ProjectUsageMinimal({ projectRef, subscription_id, filter }) {
</Typography.Text>
) : null}
<div className="text-sm text-typography-body-light dark:text-typography-body-dark">
{/* @ts-ignore */}
{feature.tiers[tier]?.render ? feature.tiers[tier].render(stats) : null}
</div>
</div>
-3
View File
@@ -3,6 +3,3 @@ export * from './delete'
export * from './get'
export * from './patch'
export * from './post'
// remove this as soon we can
export * as swr from './swr'
-5
View File
@@ -1,5 +0,0 @@
export default async function Fetch(...args) {
const res = await fetch(...args)
const body = await res.json()
return body
}
-802
View File
@@ -1,802 +0,0 @@
import { IconArchive, IconDatabase, IconHeart, IconKey } from '@supabase/ui'
import { concat, sortBy } from 'lodash'
// Ideally we'd split this into smaller, more scoped and easily readable files
// And shift them under common/constants/xxx.ts
export const IS_PLATFORM = process.env.NEXT_PUBLIC_IS_PLATFORM === 'true'
export const API_URL = IS_PLATFORM ? process.env.NEXT_PUBLIC_API_URL : '/api'
export const PG_META_URL = IS_PLATFORM
? process.env.PLATFORM_PG_META_URL
: process.env.STUDIO_PG_META_URL
export const POSTGRES_SUPER_ADMIN = 'supabase_admin'
export const POSTGRES_AUTH_ADMIN = 'supabase_auth_admin'
export const POSTGRES_STORAGE_ADMIN = 'supabase_storage_admin'
export const POSTGRES_POSTGREST = 'authenticator'
export const POSTGRES_PGBOUNCER = 'pgbouncer'
export const DATE_FORMAT = 'YYYY-MM-DDTHH:mm:ssZ'
export const QUEUES = {
// All infra tasks
INFRA: {
id: 'infra',
tasks: {
CREATE: 'create', // initialises a new server
DELETE: 'delete', // destroys a server
UPDATE: 'update', // updates the server's config
REPLACE: 'replace', // replace the server
},
},
DB: {
id: 'database',
tasks: {
CREATE: 'create', // initialises a new server
DELETE: 'delete', // destroys a server
UPDATE: 'update', // updates the server's config
RESTORE: 'restore', // restore a db with a given backup file
},
},
DUMP: {
id: 'dump',
tasks: {
BULK_PREPARE: 'bulk_prepare',
DUMP: 'dump',
PREPARE: 'prepare',
},
},
RESTORE: {
id: 'restore',
tasks: {
PREPARE: 'prepare',
RESTORE: 'restore',
},
},
EVENTS: {
id: 'events',
tasks: {
organization_created: 'organization.created',
organization_updated: 'organization.updated',
organization_deleted: 'organization.deleted',
project_created: 'project.created',
project_updated: 'project.updated',
project_deleted: 'project.deleted',
},
},
}
// alias regions remain as the starting point for project creation
// they are immediately translated to their respective cloud regions
// and are afterward never referred to
export const REGIONS = {
WEST_US: 'West US (North California)',
EAST_US: 'East US (North Virginia)',
CENTRAL_CANADA: 'Canada (Central)',
WEST_EU: 'West EU (Ireland)',
WEST_EU_2: 'West EU (London)',
// 'North EU': 'North EU',
CENTRAL_EU: 'Central EU (Frankfurt)',
SOUTH_ASIA: 'South Asia (Mumbai)',
SOUTHEAST_ASIA: 'Southeast Asia (Singapore)',
NORTHEAST_ASIA: 'Northeast Asia (Tokyo)',
NORTHEAST_ASIA_2: 'Northeast Asia (Seoul)',
OCEANIA: 'Oceania (Sydney)',
SOUTH_AMERICA: 'South America (São Paulo)',
// SOUTH_AFRICA: 'South Africa (Cape Town)',
}
export const REGIONS_DEFAULT = REGIONS.EAST_US
export const PROVIDERS = {
// Digital Ocean
DO: {
id: 'DO',
name: 'digitalocean',
DEFAULT_SSH_KEY: '26347828',
regions: {
EAST_US: 'nyc1', // New York City, United States
// 'North EU': 'ams3', //Amsterdam, the Netherlands
WEST_US: 'sfo2', // San Francisco, United States
SOUTHEAST_ASIA: 'sgp1', // Singapore
WEST_US: 'lon1', // London
CENTRAL_EU: 'fra1', // Frankfurt
CENTRAL_CANADA: 'tor1', // Toronto
SOUTH_ASIA: 'blr1', // Bangalore
},
sizes: {
512: '512mb', // Don't use - https://developers.digitalocean.com/documentation/changelog/api-v2/new-size-slugs-for-droplet-plan-changes/
's-1vcpu-1gb': {
id: 's-1vcpu-1gb',
description: '',
},
},
},
AWS: {
id: 'AWS',
name: 'aws',
DEFAULT_SSH_KEY: 'supabase-app-instance',
regions: {
EAST_US: 'us-east-1', // North Virginia
// 'North EU': 'eu-north-1', // Stockholm
WEST_US: 'us-west-1', // North California
SOUTHEAST_ASIA: 'ap-southeast-1', // Singapore
NORTHEAST_ASIA: 'ap-northeast-1', // Tokyo
NORTHEAST_ASIA_2: 'ap-northeast-2', //Seoul
OCEANIA: 'ap-southeast-2', // Sydney
WEST_EU: 'eu-west-1', // Ireland
WEST_EU_2: 'eu-west-2', // London
CENTRAL_EU: 'eu-central-1', // Frankfurt
CENTRAL_CANADA: 'ca-central-1', // Central Canada
SOUTH_ASIA: 'ap-south-1', // Mumbai
SOUTH_AMERICA: 'sa-east-1', // Sao Paulo
// SOUTH_AFRICA: 'af-south-1', // Cape Town
},
sizes: {
nano: {
x86_64: 't3.nano',
arm64: 't4g.nano',
description: '512mb RAM',
},
micro: {
x86_64: 't3.micro',
arm64: 't4g.micro',
description: '1gb RAM',
},
medium: {
x86_64: 'm5a.large',
arm64: 'm6g.medium',
description: '4gb RAM',
},
},
},
}
// @todo ini update for prod
export const AWS_SUPPORTED_AZ = {
EAST_US: ['us-east-1a', 'us-east-1b', 'us-east-1c', 'us-east-1d', 'us-east-1e', 'us-east-1f'],
SOUTHEAST_ASIA: ['ap-southeast-1a', 'ap-southeast-1b', 'ap-southeast-1c'],
WEST_EU: ['eu-west-1a', 'eu-west-1b', 'eu-west-1c'],
}
export const SERVICE_STATUS = {
INACTIVE: 'INACTIVE',
ACTIVE_HEALTHY: 'ACTIVE_HEALTHY',
ACTIVE_UNHEALTHY: 'ACTIVE_UNHEALTHY',
COMING_UP: 'COMING_UP',
UNKNOWN: 'UNKNOWN',
GOING_DOWN: 'GOING_DOWN',
INIT_FAILED: 'INIT_FAILED',
REMOVED: 'REMOVED',
}
export const PROJECT_STATUS = {
INACTIVE: 'INACTIVE',
ACTIVE_HEALTHY: 'ACTIVE_HEALTHY',
ACTIVE_UNHEALTHY: 'ACTIVE_UNHEALTHY',
COMING_UP: 'COMING_UP',
UNKNOWN: 'UNKNOWN',
GOING_DOWN: 'GOING_DOWN',
INIT_FAILED: 'INIT_FAILED',
REMOVED: 'REMOVED',
RESTORING: 'RESTORING',
}
// Not used anywhere but referenced in a comment in interfaces.js (L12)
export const APPS = {
AUTO_API: {
id: 'AUTO_API',
name: 'Auto API',
},
}
export const SIDE_PANEL_KEYS = {
ROW: 'ROW',
TABLE: 'TABLE',
COLUMN: 'COLUMN',
}
export const FILTER_CONDITIONS = {
EQ: {
name: 'EQ',
tooltip: 'Equals to',
},
NEQ: {
name: 'NEQ',
tooltip: 'Not equals to',
},
GT: {
name: 'GT',
tooltip: 'Greater than',
},
LT: {
name: 'LT',
tooltip: 'Less than',
},
GTE: {
name: 'GTE',
tooltip: 'Greater than or equals to',
},
LTE: {
name: 'LTE',
tooltip: 'Less than or equals to',
},
LIKE: {
name: 'LIKE',
tooltip: 'Pattern matching with %',
},
ILIKE: {
name: 'ILIKE',
tooltip: 'Case sensitive matching',
},
IS: {
name: 'IS',
tooltip: 'Checks for exact quality',
},
IN: {
name: 'IN',
tooltip: 'Checks if value in list',
},
}
// Data types primarily for mapping icon in editor
export const NUMERICAL_TYPES = ['int2', 'int4', 'int8', 'float4', 'float8']
export const JSON_TYPES = ['json', 'jsonb']
export const TEXT_TYPES = ['text', 'varchar']
export const TIMESTAMP_TYPES = ['date', 'time', 'timestamp', 'timetz', 'timestamptz']
export const OTHER_DATA_TYPES = ['uuid', 'bool']
export const POSTGRES_DATA_TYPES = sortBy(
concat(NUMERICAL_TYPES, JSON_TYPES, TEXT_TYPES, TIMESTAMP_TYPES, OTHER_DATA_TYPES)
)
// Keyboard Shortcuts Related
export const SHORTCUT_KEYS = {
VIEW_ALL_SHORTCUTS: 'VIEW_ALL_SHORTCUTS',
TOGGLE_FILTER_MENU: 'TOGGLE_FILTER_MENU',
TOGGLE_SORT_MENU: 'TOGGLE_SORT_MENU',
CLOSE_SIDE_PANEL: 'CLOSE_SIDE_PANEL',
EDIT_CELL: 'EDIT_CELL',
COPY_CELL: 'COPY_CELL',
PASTE_CELL: 'PASTE_CELL',
CREATE_ROW: 'CREATE_ROW',
EDIT_ROW: 'EDIT_ROW',
DELETE_ROW: 'DELETE_ROW',
SCROLL_EDGES: 'SCROLL_EDGES',
}
export const GENERAL_SHORTCUTS = [
{
key: SHORTCUT_KEYS.VIEW_ALL_SHORTCUTS,
name: 'View all shortcuts',
keys: ['Shift+/'],
},
{
key: SHORTCUT_KEYS.CLOSE_SIDE_PANEL,
name: 'Close side panel',
keys: ['Esc'],
},
]
export const generateEditorShortcuts = (clientOS) => {
let metaKey = '⌘'
if (clientOS === 'windows') metaKey = 'Ctrl'
return [
{
key: SHORTCUT_KEYS.EDIT_CELL,
name: 'Edit current cell',
keys: ['Enter'],
},
{
key: SHORTCUT_KEYS.COPY_CELL,
name: 'Copy value of current cell',
keys: [`${metaKey}+C`],
},
{
key: SHORTCUT_KEYS.PASTE_CELL,
name: 'Paste value into current cell',
keys: [`${metaKey}+V`],
},
{
key: SHORTCUT_KEYS.CREATE_ROW,
name: 'Create a new row',
keys: ['Shift+Enter'],
},
{
key: SHORTCUT_KEYS.SCROLL_EDGES,
name: 'Scroll to edges of the table',
keys: [`${metaKey}+↑`, `${metaKey}+→`, `${metaKey}+↓`, `${metaKey}+←`],
},
]
}
export const EDITOR_STATUS = {
SAVING: 'SAVING',
READY: 'READY',
}
export const POLICY_MODAL_VIEWS = {
SELECTION: 'SELECTION',
TEMPLATES: 'TEMPLATES',
EDITOR: 'EDITOR',
REVIEW: 'REVIEW',
}
export const STRIPE_PRODUCT_IDS = {
PAYG: process?.env?.NEXT_PUBLIC_STRIPE_PAYG_TIER_ID,
PRO: process?.env?.NEXT_PUBLIC_STRIPE_PRO_TIER_ID,
FREE: process?.env?.NEXT_PUBLIC_STRIPE_FREE_TIER_ID,
}
export const TIER_NAMES = {
PAYG: 'Pay as you go',
PRO: 'Pro',
FREE: 'Free',
}
export const DEFAULT_PRODUCTS = [
{
// Free tier
price: process?.env?.NEXT_PUBLIC_STRIPE_PRODUCT_FREE_TIER_PRICE_FREE,
},
]
export const PGBOUNCER_CONFIG = {
pool_mode: 'transaction',
default_pool_size: 15,
ignore_startup_parameters: 'extra_float_digits',
}
export const DEFAULT_ORG_PROJECTS_LIMIT = 3
export const DEFAULT_MINIMUM_PASSWORD_STRENGTH = 4
const METRIC_CATEGORIES_ICON_SIZE = 16
export const METRIC_CATEGORIES = {
// API: {
// label: 'All API usage',
// icon: <IconActivity />,
// key: 'api',
// },
API_DATABASE: {
label: 'Database API',
icon: <IconDatabase size={METRIC_CATEGORIES_ICON_SIZE} />,
key: 'api_database',
},
API_AUTH: {
label: 'Auth API',
icon: <IconKey size={METRIC_CATEGORIES_ICON_SIZE} />,
key: 'api_auth',
},
API_STORAGE: {
label: 'Storage API',
icon: <IconArchive size={METRIC_CATEGORIES_ICON_SIZE} />,
key: 'api_storage',
},
// API_REALTIME: {
// label: 'Realtime API',
// icon: '',
// key: 'api_realtime',
// },
INSTANCE: {
label: 'Instance health',
icon: <IconHeart size={METRIC_CATEGORIES_ICON_SIZE} />,
key: 'instance',
},
// POSTGRES: {
// label: 'Postgres usage',
// icon: '',
// key: 'postgres',
// },
}
export const METRICS = [
{
key: 'cpu_usage',
label: 'CPU % usage',
provider: 'infra-monitoring',
category: METRIC_CATEGORIES.INSTANCE,
},
{
key: 'ram_usage',
label: 'Memory % usage',
provider: 'infra-monitoring',
category: METRIC_CATEGORIES.INSTANCE,
},
{
key: 'total_realtime_egress',
label: 'Realtime Connection Egress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
{
key: 'total_realtime_get_requests',
label: 'Realtime Connection Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_REALTIME,
},
{
key: 'total_realtime_ingress',
label: 'Realtime Connection Ingress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_REALTIME,
},
// {
// key: 'total_realtime_post_requests',
// label: 'total_realtime_post_requests',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API_REALTIME,
// },
{
key: 'total_realtime_requests',
label: 'Connection Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_REALTIME,
},
/**
* API
*/
{
key: 'total_rest_ingress',
label: 'API Ingress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_DATABASE,
},
{
key: 'total_rest_egress',
label: 'API Egress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_DATABASE,
},
{
key: 'total_rest_requests',
label: 'API Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_DATABASE,
},
{
key: 'total_rest_get_requests',
label: 'API GET Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_DATABASE,
},
{
key: 'total_rest_post_requests',
label: 'API POST Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_DATABASE,
},
{
key: 'total_rest_patch_requests',
label: 'API PATCH Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_DATABASE,
},
{
key: 'total_rest_delete_requests',
label: 'API DELETE Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_DATABASE,
},
{
key: 'total_rest_options_requests',
label: 'API OPTIONS Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_DATABASE,
},
/**
* Auth
*/
// {
// key: 'total_auth_users',
// label: 'Total Auth Users',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API_AUTH,
// },
{
key: 'total_auth_ingress',
label: 'Auth Ingress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_AUTH,
},
{
key: 'total_auth_egress',
label: 'Auth Egress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_AUTH,
},
{
key: 'total_auth_requests',
label: 'Auth Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_AUTH,
},
{
key: 'total_auth_get_requests',
label: 'Auth GET Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_AUTH,
},
{
key: 'total_auth_post_requests',
label: 'Auth POST Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_AUTH,
},
{
key: 'total_auth_patch_requests',
label: 'Auth PATCH requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_AUTH,
},
{
key: 'total_auth_options_requests',
label: 'Auth OPTIONS requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_AUTH,
},
/**
* Storage
*/
{
key: 'total_storage_ingress',
label: 'Storage Ingress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_STORAGE,
},
{
key: 'total_storage_egress',
label: 'Storage Egress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_STORAGE,
},
{
key: 'total_storage_requests',
label: 'Storage Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_STORAGE,
},
{
key: 'total_storage_get_requests',
label: 'Storage GET Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_STORAGE,
},
{
key: 'total_storage_post_requests',
label: 'Storage POST Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_STORAGE,
},
{
key: 'total_storage_delete_requests',
label: 'Storage DELETE Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_STORAGE,
},
{
key: 'total_storage_options_requests',
label: 'Storage OPTIONS Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_STORAGE,
},
{
key: 'total_auth_delete_requests',
label: 'Auth DELETE Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_AUTH,
},
{
key: 'total_egress',
label: 'All Egress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
// {
// key: 'total_auth_emails',
// name: 'total_auth_emails',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API,
// },
// {
// key: 'total_auth_texts',
// name: 'total_auth_texts',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API,
// },
{
key: 'total_get_requests',
label: 'All GET Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
// {
// key: 'total_db_size_bytes',
// name: 'total_db_size_bytes',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API,
// },
// {
// key: 'total_storage_size_bytes',
// name: 'total_storage_size_bytes',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API,
// },
// {
// key: 'total_realtime_delete_requests',
// label: 'total_realtime_delete_requests',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API_REALTIME,
// },
{
key: 'total_storage_patch_requests',
label: 'Storage PATCH Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_STORAGE,
},
{
key: 'total_requests',
label: 'All Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
// {
// key: 'total_realtime_options_requests',
// label: 'total_realtime_options_requests',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API_REALTIME,
// },
{
key: 'total_patch_requests',
label: 'All PATCH Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
{
key: 'total_post_requests',
label: 'All POST Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
{
key: 'total_ingress',
label: 'All Ingress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
// {
// key: 'total_realtime_patch_requests',
// label: 'total_realtime_patch_requests',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API_REALTIME,
// },
{
key: 'total_delete_requests',
label: 'All DELETE Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
{
key: 'total_options_requests',
label: 'All OPTIONS Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
// {
// key: 'total_db_egress_bytes',
// name: 'total_db_egress_bytes',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API,
// },
]
export const TIME_PERIODS_BILLING = [
{
key: 'currentBillingCycle',
label: 'Current billing cycle',
interval: '1d',
},
{
key: 'previousBillingCycle',
label: 'Previous billing cycle',
interval: '1d',
},
]
export const TIME_PERIODS_REPORTS = [
{
key: '7d',
label: 'Last 7 days',
interval: '1d',
},
{
key: '30d',
label: 'Last 30 days',
interval: '1d',
},
// {
// key: '60d',
// label: 'Last 60 days',
// interval: '1d',
// },
// {
// key: '120d',
// label: 'Last 120 days',
// interval: '1d',
// },
{
key: 'startMonth',
label: 'This month',
interval: '1d',
},
]
export const TIME_PERIODS_INFRA = [
{
key: '1h',
label: 'Last hour',
interval: '1m',
},
{
key: '3h',
label: 'Last 3 hours',
interval: '5m',
},
{
key: '1d',
label: 'Last 24 hours',
interval: '30m',
},
]
export const NEW_REPORT_SKELETON = {
name: 'new report',
description: '',
type: 'report',
visibility: 'project',
content: {
schema_version: 1,
period_start: {
time_period: '7d',
date: '',
},
period_end: {
time_period: 'today',
date: '',
},
interval: '1d',
layout: [],
},
}
export const PASSWORD_STRENGTH = {
0: 'This password is not acceptable.',
1: 'This password is not secure enough.',
2: 'This password is not secure enough.',
3: 'Not bad, but your password must be harder to guess.',
4: 'This password is strong.',
}
export const PASSWORD_STRENGTH_COLOR = {
0: 'bg-red-500',
1: 'bg-red-400',
2: 'bg-yellow-500',
3: 'bg-yellow-500',
4: 'bg-green-500',
}
export const PASSWORD_STRENGTH_PERCENTAGE = {
0: '10%',
1: '30%',
2: '50%',
3: '80%',
4: '100%',
}
+89
View File
@@ -0,0 +1,89 @@
import { sortBy, concat } from 'lodash'
export * from './infrastructure'
export * from './metrics'
export const IS_PLATFORM = process.env.NEXT_PUBLIC_IS_PLATFORM === 'true'
export const API_URL = IS_PLATFORM ? process.env.NEXT_PUBLIC_API_URL : '/api'
export const PG_META_URL = IS_PLATFORM
? process.env.PLATFORM_PG_META_URL
: process.env.STUDIO_PG_META_URL
export const DATE_FORMAT = 'YYYY-MM-DDTHH:mm:ssZ'
// Data types primarily for mapping icon in editor
export const NUMERICAL_TYPES = ['int2', 'int4', 'int8', 'float4', 'float8']
export const JSON_TYPES = ['json', 'jsonb']
export const TEXT_TYPES = ['text', 'varchar']
export const TIMESTAMP_TYPES = ['date', 'time', 'timestamp', 'timetz', 'timestamptz']
export const OTHER_DATA_TYPES = ['uuid', 'bool']
export const POSTGRES_DATA_TYPES = sortBy(
concat(NUMERICAL_TYPES, JSON_TYPES, TEXT_TYPES, TIMESTAMP_TYPES, OTHER_DATA_TYPES)
)
// Keyboard Shortcuts Related
export const SHORTCUT_KEYS = {
VIEW_ALL_SHORTCUTS: 'VIEW_ALL_SHORTCUTS',
TOGGLE_FILTER_MENU: 'TOGGLE_FILTER_MENU',
TOGGLE_SORT_MENU: 'TOGGLE_SORT_MENU',
CLOSE_SIDE_PANEL: 'CLOSE_SIDE_PANEL',
EDIT_CELL: 'EDIT_CELL',
COPY_CELL: 'COPY_CELL',
PASTE_CELL: 'PASTE_CELL',
CREATE_ROW: 'CREATE_ROW',
EDIT_ROW: 'EDIT_ROW',
DELETE_ROW: 'DELETE_ROW',
SCROLL_EDGES: 'SCROLL_EDGES',
}
export const GENERAL_SHORTCUTS = [
{
key: SHORTCUT_KEYS.VIEW_ALL_SHORTCUTS,
name: 'View all shortcuts',
keys: ['Shift+/'],
},
{
key: SHORTCUT_KEYS.CLOSE_SIDE_PANEL,
name: 'Close side panel',
keys: ['Esc'],
},
]
export const generateEditorShortcuts = (clientOS: string) => {
const metaKey = clientOS === 'windows' ? 'Ctrl' : '⌘'
return [
{
key: SHORTCUT_KEYS.EDIT_CELL,
name: 'Edit current cell',
keys: ['Enter'],
},
{
key: SHORTCUT_KEYS.COPY_CELL,
name: 'Copy value of current cell',
keys: [`${metaKey}+C`],
},
{
key: SHORTCUT_KEYS.PASTE_CELL,
name: 'Paste value into current cell',
keys: [`${metaKey}+V`],
},
{
key: SHORTCUT_KEYS.CREATE_ROW,
name: 'Create a new row',
keys: ['Shift+Enter'],
},
{
key: SHORTCUT_KEYS.SCROLL_EDGES,
name: 'Scroll to edges of the table',
keys: [`${metaKey}+↑`, `${metaKey}+→`, `${metaKey}+↓`, `${metaKey}+←`],
},
]
}
export const POLICY_MODAL_VIEWS = {
SELECTION: 'SELECTION',
TEMPLATES: 'TEMPLATES',
EDITOR: 'EDITOR',
REVIEW: 'REVIEW',
}
+124
View File
@@ -0,0 +1,124 @@
// Alias regions remain as the starting point for project creation
// they are immediately translated to their respective cloud regions
// and are afterward never referred to
export const REGIONS = {
WEST_US: 'West US (North California)',
EAST_US: 'East US (North Virginia)',
CENTRAL_CANADA: 'Canada (Central)',
WEST_EU: 'West EU (Ireland)',
WEST_EU_2: 'West EU (London)',
// 'North EU': 'North EU',
CENTRAL_EU: 'Central EU (Frankfurt)',
SOUTH_ASIA: 'South Asia (Mumbai)',
SOUTHEAST_ASIA: 'Southeast Asia (Singapore)',
NORTHEAST_ASIA: 'Northeast Asia (Tokyo)',
NORTHEAST_ASIA_2: 'Northeast Asia (Seoul)',
OCEANIA: 'Oceania (Sydney)',
SOUTH_AMERICA: 'South America (São Paulo)',
// SOUTH_AFRICA: 'South Africa (Cape Town)',
}
export const REGIONS_DEFAULT = REGIONS.EAST_US
export const PROVIDERS = {
AWS: {
id: 'AWS',
name: 'aws',
DEFAULT_SSH_KEY: 'supabase-app-instance',
regions: {
EAST_US: 'us-east-1', // North Virginia
// 'North EU': 'eu-north-1', // Stockholm
WEST_US: 'us-west-1', // North California
SOUTHEAST_ASIA: 'ap-southeast-1', // Singapore
NORTHEAST_ASIA: 'ap-northeast-1', // Tokyo
NORTHEAST_ASIA_2: 'ap-northeast-2', //Seoul
OCEANIA: 'ap-southeast-2', // Sydney
WEST_EU: 'eu-west-1', // Ireland
WEST_EU_2: 'eu-west-2', // London
CENTRAL_EU: 'eu-central-1', // Frankfurt
CENTRAL_CANADA: 'ca-central-1', // Central Canada
SOUTH_ASIA: 'ap-south-1', // Mumbai
SOUTH_AMERICA: 'sa-east-1', // Sao Paulo
// SOUTH_AFRICA: 'af-south-1', // Cape Town
},
sizes: {
nano: {
x86_64: 't3.nano',
arm64: 't4g.nano',
description: '512mb RAM',
},
micro: {
x86_64: 't3.micro',
arm64: 't4g.micro',
description: '1gb RAM',
},
medium: {
x86_64: 'm5a.large',
arm64: 'm6g.medium',
description: '4gb RAM',
},
},
},
}
// @todo ini update for prod
export const AWS_SUPPORTED_AZ = {
EAST_US: ['us-east-1a', 'us-east-1b', 'us-east-1c', 'us-east-1d', 'us-east-1e', 'us-east-1f'],
SOUTHEAST_ASIA: ['ap-southeast-1a', 'ap-southeast-1b', 'ap-southeast-1c'],
WEST_EU: ['eu-west-1a', 'eu-west-1b', 'eu-west-1c'],
}
export const SERVICE_STATUS = {
INACTIVE: 'INACTIVE',
ACTIVE_HEALTHY: 'ACTIVE_HEALTHY',
ACTIVE_UNHEALTHY: 'ACTIVE_UNHEALTHY',
COMING_UP: 'COMING_UP',
UNKNOWN: 'UNKNOWN',
GOING_DOWN: 'GOING_DOWN',
INIT_FAILED: 'INIT_FAILED',
REMOVED: 'REMOVED',
}
export const PROJECT_STATUS = {
INACTIVE: 'INACTIVE',
ACTIVE_HEALTHY: 'ACTIVE_HEALTHY',
ACTIVE_UNHEALTHY: 'ACTIVE_UNHEALTHY',
COMING_UP: 'COMING_UP',
UNKNOWN: 'UNKNOWN',
GOING_DOWN: 'GOING_DOWN',
INIT_FAILED: 'INIT_FAILED',
REMOVED: 'REMOVED',
RESTORING: 'RESTORING',
}
export const STRIPE_PRODUCT_IDS = {
PAYG: process?.env?.NEXT_PUBLIC_STRIPE_PAYG_TIER_ID,
PRO: process?.env?.NEXT_PUBLIC_STRIPE_PRO_TIER_ID,
FREE: process?.env?.NEXT_PUBLIC_STRIPE_FREE_TIER_ID,
}
export const DEFAULT_MINIMUM_PASSWORD_STRENGTH = 4
export const PASSWORD_STRENGTH = {
0: 'This password is not acceptable.',
1: 'This password is not secure enough.',
2: 'This password is not secure enough.',
3: 'Not bad, but your password must be harder to guess.',
4: 'This password is strong.',
}
export const PASSWORD_STRENGTH_COLOR = {
0: 'bg-red-500',
1: 'bg-red-400',
2: 'bg-yellow-500',
3: 'bg-yellow-500',
4: 'bg-green-500',
}
export const PASSWORD_STRENGTH_PERCENTAGE = {
0: '10%',
1: '30%',
2: '50%',
3: '80%',
4: '100%',
}
+406
View File
@@ -0,0 +1,406 @@
import { IconActivity, IconArchive, IconDatabase, IconKey, IconHeart } from '@supabase/ui'
export const METRIC_CATEGORIES = {
API: {
label: 'All API usage',
icon: <IconActivity />,
key: 'api',
},
API_DATABASE: {
label: 'Database API',
icon: <IconDatabase size={16} />,
key: 'api_database',
},
API_AUTH: {
label: 'Auth API',
icon: <IconKey size={16} />,
key: 'api_auth',
},
API_STORAGE: {
label: 'Storage API',
icon: <IconArchive size={16} />,
key: 'api_storage',
},
API_REALTIME: {
label: 'Realtime API',
icon: '',
key: 'api_realtime',
},
INSTANCE: {
label: 'Instance health',
icon: <IconHeart size={16} />,
key: 'instance',
},
// POSTGRES: {
// label: 'Postgres usage',
// icon: '',
// key: 'postgres',
// },
}
export const METRICS = [
{
key: 'cpu_usage',
label: 'CPU % usage',
provider: 'infra-monitoring',
category: METRIC_CATEGORIES.INSTANCE,
},
{
key: 'ram_usage',
label: 'Memory % usage',
provider: 'infra-monitoring',
category: METRIC_CATEGORIES.INSTANCE,
},
{
key: 'total_realtime_egress',
label: 'Realtime Connection Egress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
{
key: 'total_realtime_get_requests',
label: 'Realtime Connection Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_REALTIME,
},
{
key: 'total_realtime_ingress',
label: 'Realtime Connection Ingress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_REALTIME,
},
// {
// key: 'total_realtime_post_requests',
// label: 'total_realtime_post_requests',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API_REALTIME,
// },
{
key: 'total_realtime_requests',
label: 'Connection Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_REALTIME,
},
/**
* API
*/
{
key: 'total_rest_ingress',
label: 'API Ingress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_DATABASE,
},
{
key: 'total_rest_egress',
label: 'API Egress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_DATABASE,
},
{
key: 'total_rest_requests',
label: 'API Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_DATABASE,
},
{
key: 'total_rest_get_requests',
label: 'API GET Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_DATABASE,
},
{
key: 'total_rest_post_requests',
label: 'API POST Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_DATABASE,
},
{
key: 'total_rest_patch_requests',
label: 'API PATCH Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_DATABASE,
},
{
key: 'total_rest_delete_requests',
label: 'API DELETE Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_DATABASE,
},
{
key: 'total_rest_options_requests',
label: 'API OPTIONS Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_DATABASE,
},
/**
* Auth
*/
// {
// key: 'total_auth_users',
// label: 'Total Auth Users',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API_AUTH,
// },
{
key: 'total_auth_ingress',
label: 'Auth Ingress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_AUTH,
},
{
key: 'total_auth_egress',
label: 'Auth Egress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_AUTH,
},
{
key: 'total_auth_requests',
label: 'Auth Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_AUTH,
},
{
key: 'total_auth_get_requests',
label: 'Auth GET Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_AUTH,
},
{
key: 'total_auth_post_requests',
label: 'Auth POST Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_AUTH,
},
{
key: 'total_auth_patch_requests',
label: 'Auth PATCH requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_AUTH,
},
{
key: 'total_auth_options_requests',
label: 'Auth OPTIONS requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_AUTH,
},
/**
* Storage
*/
{
key: 'total_storage_ingress',
label: 'Storage Ingress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_STORAGE,
},
{
key: 'total_storage_egress',
label: 'Storage Egress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_STORAGE,
},
{
key: 'total_storage_requests',
label: 'Storage Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_STORAGE,
},
{
key: 'total_storage_get_requests',
label: 'Storage GET Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_STORAGE,
},
{
key: 'total_storage_post_requests',
label: 'Storage POST Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_STORAGE,
},
{
key: 'total_storage_delete_requests',
label: 'Storage DELETE Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_STORAGE,
},
{
key: 'total_storage_options_requests',
label: 'Storage OPTIONS Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_STORAGE,
},
{
key: 'total_auth_delete_requests',
label: 'Auth DELETE Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_AUTH,
},
{
key: 'total_egress',
label: 'All Egress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
// {
// key: 'total_auth_emails',
// name: 'total_auth_emails',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API,
// },
// {
// key: 'total_auth_texts',
// name: 'total_auth_texts',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API,
// },
{
key: 'total_get_requests',
label: 'All GET Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
// {
// key: 'total_db_size_bytes',
// name: 'total_db_size_bytes',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API,
// },
// {
// key: 'total_storage_size_bytes',
// name: 'total_storage_size_bytes',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API,
// },
// {
// key: 'total_realtime_delete_requests',
// label: 'total_realtime_delete_requests',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API_REALTIME,
// },
{
key: 'total_storage_patch_requests',
label: 'Storage PATCH Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API_STORAGE,
},
{
key: 'total_requests',
label: 'All Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
// {
// key: 'total_realtime_options_requests',
// label: 'total_realtime_options_requests',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API_REALTIME,
// },
{
key: 'total_patch_requests',
label: 'All PATCH Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
{
key: 'total_post_requests',
label: 'All POST Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
{
key: 'total_ingress',
label: 'All Ingress',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
// {
// key: 'total_realtime_patch_requests',
// label: 'total_realtime_patch_requests',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API_REALTIME,
// },
{
key: 'total_delete_requests',
label: 'All DELETE Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
{
key: 'total_options_requests',
label: 'All OPTIONS Requests',
provider: 'daily-stats',
category: METRIC_CATEGORIES.API,
},
// {
// key: 'total_db_egress_bytes',
// name: 'total_db_egress_bytes',
// provider: 'daily-stats',
// category: METRIC_CATEGORIES.API,
// },
]
export const TIME_PERIODS_BILLING = [
{
key: 'currentBillingCycle',
label: 'Current billing cycle',
interval: '1d',
},
{
key: 'previousBillingCycle',
label: 'Previous billing cycle',
interval: '1d',
},
]
export const TIME_PERIODS_REPORTS = [
{
key: '7d',
label: 'Last 7 days',
interval: '1d',
},
{
key: '30d',
label: 'Last 30 days',
interval: '1d',
},
// {
// key: '60d',
// label: 'Last 60 days',
// interval: '1d',
// },
// {
// key: '120d',
// label: 'Last 120 days',
// interval: '1d',
// },
{
key: 'startMonth',
label: 'This month',
interval: '1d',
},
]
export const TIME_PERIODS_INFRA = [
{
key: '1h',
label: 'Last hour',
interval: '1m',
},
{
key: '3h',
label: 'Last 3 hours',
interval: '5m',
},
{
key: '1d',
label: 'Last 24 hours',
interval: '30m',
},
]
File renamed without changes.
+1 -1
View File
@@ -6,7 +6,7 @@ import { IconMoon, IconSun, Select, Typography } from '@supabase/ui'
import { useProfile, useStore, withAuth } from 'hooks'
import { post } from 'lib/common/fetch'
import { API_URL } from 'lib/constants'
import AccountLayout from 'components/layouts/AccountLayout'
import { AccountLayout } from 'components/layouts'
import Panel from 'components/to-be-cleaned/Panel'
import SchemaFormPanel from 'components/to-be-cleaned/forms/SchemaFormPanel'
+1 -1
View File
@@ -9,7 +9,7 @@ import { Project } from 'types'
import { useStore, withAuth } from 'hooks'
import { post, delete_ } from 'lib/common/fetch'
import { API_URL, IS_PLATFORM, PROJECT_STATUS } from 'lib/constants'
import AccountLayout from 'components/layouts/AccountLayout'
import { AccountLayout } from 'components/layouts'
import Landing from 'components/interfaces/Home/Landing'
import ProjectList from 'components/interfaces/Home/ProjectList'
import OrganizationDropdown from 'components/to-be-cleaned/Dropdown/OrganizationDropdown'
+1 -1
View File
@@ -24,7 +24,7 @@ import {
} from 'lib/constants'
import { useStore, withAuth } from 'hooks'
import WizardLayout from 'components/layouts/WizardLayout'
import { WizardLayout } from 'components/layouts'
import FormField from 'components/to-be-cleaned/forms/FormField'
import Panel from 'components/to-be-cleaned/Panel'
import InformationBox from 'components/ui/InformationBox'
+1 -1
View File
@@ -7,7 +7,7 @@ import Router from 'next/router'
import { API_URL } from 'lib/constants'
import { useStore, withAuth } from 'hooks'
import { post } from 'lib/common/fetch'
import WizardLayout from 'components/layouts/WizardLayout'
import { WizardLayout } from 'components/layouts'
import FormField from 'components/to-be-cleaned/forms/FormField'
import Panel from 'components/to-be-cleaned/Panel'
+1 -1
View File
@@ -25,7 +25,7 @@ import {
import { API_URL } from 'lib/constants'
import { useOrganizationDetail, useStore, withAuth } from 'hooks'
import { post, delete_ } from 'lib/common/fetch'
import AccountLayout from 'components/layouts/AccountLayout'
import { AccountLayout } from 'components/layouts'
import { BillingSettings, InvoicesSettings } from 'components/interfaces/Organization'
import Table from 'components/to-be-cleaned/Table'
+2 -2
View File
@@ -9,7 +9,7 @@ module.exports = {
{
type: 'category',
label: 'Auth',
items: ['reference/javascript/auth-signup', 'reference/javascript/auth-signin', 'reference/javascript/auth-signout', 'reference/javascript/auth-session', 'reference/javascript/auth-user', 'reference/javascript/auth-update', 'reference/javascript/auth-setauth', 'reference/javascript/auth-onauthstatechange', 'reference/javascript/reset-password-email'],
items: ['reference/javascript/auth-signup', 'reference/javascript/auth-signin', 'reference/javascript/auth-signout', 'reference/javascript/auth-session', 'reference/javascript/auth-user', 'reference/javascript/auth-update', 'reference/javascript/auth-setauth', 'reference/javascript/auth-onauthstatechange', 'reference/javascript/auth-api-getuser', 'reference/javascript/reset-password-email'],
collapsed: true,
},
{
@@ -39,7 +39,7 @@ module.exports = {
{
type: 'category',
label: 'Modifiers',
items: ['reference/javascript/using-modifiers', 'reference/javascript/limit', 'reference/javascript/order', 'reference/javascript/range', 'reference/javascript/single'],
items: ['reference/javascript/using-modifiers', 'reference/javascript/limit', 'reference/javascript/order', 'reference/javascript/range', 'reference/javascript/single', 'reference/javascript/maybesingle'],
collapsed: true,
},
{
+2471 -1958
View File
File diff suppressed because it is too large. Load diff
+1425 -1121
View File
File diff suppressed because it is too large. Load diff
+826 -715
View File
File diff suppressed because it is too large. Load diff
+3 -3
View File
@@ -4606,7 +4606,7 @@
"isExported": true
},
"comment": {
"text": "The body of the file is to be stored in the bucket."
"text": "The body of the file to be stored in the bucket."
},
"type": {
"type": "union",
@@ -4876,7 +4876,7 @@
"isExported": true
},
"comment": {
"text": "The body of the file is to be stored in the bucket."
"text": "The body of the file to be stored in the bucket."
},
"type": {
"type": "union",
@@ -5172,7 +5172,7 @@
"isExported": true
},
"comment": {
"text": "The body of the file is to be stored in the bucket."
"text": "The body of the file to be stored in the bucket."
},
"type": {
"type": "union",
+219 -121
View File
@@ -29,7 +29,7 @@
},
"children": [
{
"id": 307,
"id": 311,
"name": "constructor",
"kind": 512,
"kindString": "Constructor",
@@ -41,7 +41,7 @@
},
"signatures": [
{
"id": 310,
"id": 314,
"name": "new SupabaseClient",
"kind": 16384,
"kindString": "Constructor signature",
@@ -53,7 +53,7 @@
},
"parameters": [
{
"id": 311,
"id": 315,
"name": "supabaseUrl",
"kind": 32768,
"kindString": "Parameter",
@@ -69,7 +69,7 @@
}
},
{
"id": 312,
"id": 316,
"name": "supabaseKey",
"kind": 32768,
"kindString": "Parameter",
@@ -85,7 +85,7 @@
}
},
{
"id": 313,
"id": 317,
"name": "options",
"kind": 32768,
"kindString": "Parameter",
@@ -110,8 +110,8 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 35,
"character": 25
"line": 38,
"character": 3
}
]
},
@@ -183,6 +183,72 @@
"name": "Fetch"
}
},
{
"id": 307,
"name": "headers",
"kind": 1024,
"kindString": "Property",
"flags": {
"isProtected": true,
"isExported": true
},
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 36,
"character": 19
}
],
"type": {
"type": "reflection",
"declaration": {
"id": 308,
"name": "__type",
"kind": 65536,
"kindString": "Type literal",
"flags": {
"isExported": true
},
"indexSignature": [
{
"id": 309,
"name": "__index",
"kind": 8192,
"kindString": "Index signature",
"flags": {
"isExported": true
},
"parameters": [
{
"id": 310,
"name": "key",
"kind": 32768,
"kindString": "Parameter",
"flags": {
"isExported": true
},
"type": {
"type": "intrinsic",
"name": "string"
}
}
],
"type": {
"type": "intrinsic",
"name": "string"
}
}
],
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 36,
"character": 20
}
]
}
}
},
{
"id": 305,
"name": "realtime",
@@ -289,7 +355,7 @@
}
},
{
"id": 309,
"id": 313,
"name": "supabaseKey",
"kind": 1024,
"kindString": "Property",
@@ -304,7 +370,7 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 51,
"line": 54,
"character": 25
}
],
@@ -314,7 +380,7 @@
}
},
{
"id": 308,
"id": 312,
"name": "supabaseUrl",
"kind": 1024,
"kindString": "Property",
@@ -329,7 +395,7 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 50,
"line": 53,
"character": 25
}
],
@@ -339,7 +405,7 @@
}
},
{
"id": 314,
"id": 318,
"name": "storage",
"kind": 262144,
"kindString": "Accessor",
@@ -351,7 +417,7 @@
},
"getSignature": [
{
"id": 315,
"id": 319,
"name": "__get",
"kind": 524288,
"kindString": "Get signature",
@@ -370,13 +436,13 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 80,
"line": 83,
"character": 13
}
]
},
{
"id": 359,
"id": 364,
"name": "_closeChannel",
"kind": 2048,
"kindString": "Method",
@@ -386,7 +452,7 @@
},
"signatures": [
{
"id": 360,
"id": 365,
"name": "_closeChannel",
"kind": 4096,
"kindString": "Call signature",
@@ -395,7 +461,7 @@
},
"parameters": [
{
"id": 361,
"id": 366,
"name": "subscription",
"kind": 32768,
"kindString": "Parameter",
@@ -423,13 +489,13 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 201,
"line": 205,
"character": 23
}
]
},
{
"id": 332,
"id": 336,
"name": "_closeSubscription",
"kind": 2048,
"kindString": "Method",
@@ -439,7 +505,7 @@
},
"signatures": [
{
"id": 333,
"id": 337,
"name": "_closeSubscription",
"kind": 4096,
"kindString": "Call signature",
@@ -448,7 +514,7 @@
},
"parameters": [
{
"id": 334,
"id": 338,
"name": "subscription",
"kind": 32768,
"kindString": "Parameter",
@@ -476,13 +542,13 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 143,
"line": 146,
"character": 34
}
]
},
{
"id": 354,
"id": 359,
"name": "_getAuthHeaders",
"kind": 2048,
"kindString": "Method",
@@ -492,7 +558,7 @@
},
"signatures": [
{
"id": 355,
"id": 360,
"name": "_getAuthHeaders",
"kind": 4096,
"kindString": "Call signature",
@@ -502,7 +568,7 @@
"type": {
"type": "reflection",
"declaration": {
"id": 356,
"id": 361,
"name": "__type",
"kind": 65536,
"kindString": "Type literal",
@@ -511,7 +577,7 @@
},
"indexSignature": [
{
"id": 357,
"id": 362,
"name": "__index",
"kind": 8192,
"kindString": "Index signature",
@@ -520,7 +586,7 @@
},
"parameters": [
{
"id": 358,
"id": 363,
"name": "key",
"kind": 32768,
"kindString": "Parameter",
@@ -542,7 +608,7 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 193,
"line": 197,
"character": 28
}
]
@@ -553,13 +619,13 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 193,
"line": 197,
"character": 25
}
]
},
{
"id": 352,
"id": 357,
"name": "_initPostgRESTClient",
"kind": 2048,
"kindString": "Method",
@@ -569,7 +635,7 @@
},
"signatures": [
{
"id": 353,
"id": 358,
"name": "_initPostgRESTClient",
"kind": 4096,
"kindString": "Call signature",
@@ -585,13 +651,13 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 185,
"line": 189,
"character": 30
}
]
},
{
"id": 349,
"id": 354,
"name": "_initRealtimeClient",
"kind": 2048,
"kindString": "Method",
@@ -601,7 +667,7 @@
},
"signatures": [
{
"id": 350,
"id": 355,
"name": "_initRealtimeClient",
"kind": 4096,
"kindString": "Call signature",
@@ -610,7 +676,7 @@
},
"parameters": [
{
"id": 351,
"id": 356,
"name": "options",
"kind": 32768,
"kindString": "Parameter",
@@ -633,13 +699,13 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 178,
"line": 182,
"character": 29
}
]
},
{
"id": 337,
"id": 341,
"name": "_initSupabaseAuthClient",
"kind": 2048,
"kindString": "Method",
@@ -649,7 +715,7 @@
},
"signatures": [
{
"id": 338,
"id": 342,
"name": "_initSupabaseAuthClient",
"kind": 4096,
"kindString": "Call signature",
@@ -658,7 +724,7 @@
},
"parameters": [
{
"id": 339,
"id": 343,
"name": "__namedParameters",
"kind": 32768,
"kindString": "Parameter",
@@ -669,7 +735,7 @@
"type": {
"type": "reflection",
"declaration": {
"id": 340,
"id": 344,
"name": "__type",
"kind": 65536,
"kindString": "Type literal",
@@ -678,7 +744,7 @@
},
"children": [
{
"id": 341,
"id": 345,
"name": "autoRefreshToken",
"kind": 32,
"kindString": "Variable",
@@ -688,7 +754,7 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 157,
"line": 160,
"character": 20
}
],
@@ -711,7 +777,7 @@
}
},
{
"id": 343,
"id": 347,
"name": "detectSessionInUrl",
"kind": 32,
"kindString": "Variable",
@@ -721,7 +787,7 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 159,
"line": 162,
"character": 22
}
],
@@ -744,7 +810,37 @@
}
},
{
"id": 345,
"id": 353,
"name": "fetch",
"kind": 32,
"kindString": "Variable",
"flags": {
"isExported": true
},
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 165,
"character": 9
}
],
"type": {
"type": "union",
"types": [
{
"type": "intrinsic",
"name": "undefined"
},
{
"type": "reference",
"id": 220,
"name": "fetch"
}
]
}
},
{
"id": 349,
"name": "headers",
"kind": 32,
"kindString": "Variable",
@@ -754,7 +850,7 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 161,
"line": 164,
"character": 11
}
],
@@ -768,7 +864,7 @@
{
"type": "reflection",
"declaration": {
"id": 346,
"id": 350,
"name": "__type",
"kind": 65536,
"kindString": "Type literal",
@@ -777,7 +873,7 @@
},
"indexSignature": [
{
"id": 347,
"id": 351,
"name": "__index",
"kind": 8192,
"kindString": "Index signature",
@@ -786,7 +882,7 @@
},
"parameters": [
{
"id": 348,
"id": 352,
"name": "key",
"kind": 32768,
"kindString": "Parameter",
@@ -811,7 +907,7 @@
}
},
{
"id": 344,
"id": 348,
"name": "localStorage",
"kind": 32,
"kindString": "Variable",
@@ -821,7 +917,7 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 160,
"line": 163,
"character": 16
}
],
@@ -869,7 +965,7 @@
}
},
{
"id": 342,
"id": 346,
"name": "persistSession",
"kind": 32,
"kindString": "Variable",
@@ -879,7 +975,7 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 158,
"line": 161,
"character": 18
}
],
@@ -907,18 +1003,19 @@
"title": "Variables",
"kind": 32,
"children": [
341,
343,
345,
344,
342
347,
353,
349,
348,
346
]
}
],
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 156,
"line": 159,
"character": 34
}
]
@@ -936,13 +1033,13 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 156,
"line": 159,
"character": 33
}
]
},
{
"id": 316,
"id": 320,
"name": "from",
"kind": 2048,
"kindString": "Method",
@@ -951,7 +1048,7 @@
},
"signatures": [
{
"id": 317,
"id": 321,
"name": "from",
"kind": 4096,
"kindString": "Call signature",
@@ -963,7 +1060,7 @@
},
"typeParameter": [
{
"id": 318,
"id": 322,
"name": "T",
"kind": 131072,
"kindString": "Type parameter",
@@ -974,7 +1071,7 @@
],
"parameters": [
{
"id": 319,
"id": 323,
"name": "table",
"kind": 32768,
"kindString": "Parameter",
@@ -1010,13 +1107,13 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 89,
"line": 92,
"character": 6
}
]
},
{
"id": 335,
"id": 339,
"name": "getSubscriptions",
"kind": 2048,
"kindString": "Method",
@@ -1025,7 +1122,7 @@
},
"signatures": [
{
"id": 336,
"id": 340,
"name": "getSubscriptions",
"kind": 4096,
"kindString": "Call signature",
@@ -1047,13 +1144,13 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 152,
"line": 155,
"character": 18
}
]
},
{
"id": 329,
"id": 333,
"name": "removeSubscription",
"kind": 2048,
"kindString": "Method",
@@ -1062,7 +1159,7 @@
},
"signatures": [
{
"id": 330,
"id": 334,
"name": "removeSubscription",
"kind": 4096,
"kindString": "Call signature",
@@ -1074,7 +1171,7 @@
},
"parameters": [
{
"id": 331,
"id": 335,
"name": "subscription",
"kind": 32768,
"kindString": "Parameter",
@@ -1105,13 +1202,13 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 126,
"line": 129,
"character": 20
}
]
},
{
"id": 320,
"id": 324,
"name": "rpc",
"kind": 2048,
"kindString": "Method",
@@ -1120,7 +1217,7 @@
},
"signatures": [
{
"id": 321,
"id": 325,
"name": "rpc",
"kind": 4096,
"kindString": "Call signature",
@@ -1132,7 +1229,7 @@
},
"typeParameter": [
{
"id": 322,
"id": 326,
"name": "T",
"kind": 131072,
"kindString": "Type parameter",
@@ -1143,7 +1240,7 @@
],
"parameters": [
{
"id": 323,
"id": 327,
"name": "fn",
"kind": 32768,
"kindString": "Parameter",
@@ -1159,7 +1256,7 @@
}
},
{
"id": 324,
"id": 328,
"name": "params",
"kind": 32768,
"kindString": "Parameter",
@@ -1185,7 +1282,7 @@
}
},
{
"id": 325,
"id": 329,
"name": "__namedParameters",
"kind": 32768,
"kindString": "Parameter",
@@ -1196,7 +1293,7 @@
"type": {
"type": "reflection",
"declaration": {
"id": 326,
"id": 330,
"name": "__type",
"kind": 65536,
"kindString": "Type literal",
@@ -1205,7 +1302,7 @@
},
"children": [
{
"id": 328,
"id": 332,
"name": "count",
"kind": 32,
"kindString": "Variable",
@@ -1218,7 +1315,7 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 114,
"line": 117,
"character": 11
}
],
@@ -1246,7 +1343,7 @@
"defaultValue": "null"
},
{
"id": 327,
"id": 331,
"name": "head",
"kind": 32,
"kindString": "Variable",
@@ -1259,7 +1356,7 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 113,
"line": 116,
"character": 10
}
],
@@ -1275,15 +1372,15 @@
"title": "Variables",
"kind": 32,
"children": [
328,
327
332,
331
]
}
],
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 111,
"line": 114,
"character": 20
}
]
@@ -1307,7 +1404,7 @@
"sources": [
{
"fileName": "src/SupabaseClient.ts",
"line": 109,
"line": 112,
"character": 5
}
]
@@ -1318,7 +1415,7 @@
"title": "Constructors",
"kind": 512,
"children": [
307
311
]
},
{
@@ -1328,36 +1425,37 @@
299,
303,
306,
307,
305,
302,
301,
300,
304,
309,
308
313,
312
]
},
{
"title": "Accessors",
"kind": 262144,
"children": [
314
318
]
},
{
"title": "Methods",
"kind": 2048,
"children": [
364,
336,
359,
332,
357,
354,
352,
349,
337,
316,
335,
329,
320
341,
320,
339,
333,
324
]
}
],
@@ -1370,7 +1468,7 @@
]
},
{
"id": 362,
"id": 367,
"name": "DEFAULT_OPTIONS",
"kind": 2097152,
"kindString": "Object literal",
@@ -1379,7 +1477,7 @@
},
"children": [
{
"id": 364,
"id": 369,
"name": "autoRefreshToken",
"kind": 32,
"kindString": "Variable",
@@ -1398,7 +1496,7 @@
"defaultValue": "true"
},
{
"id": 366,
"id": 371,
"name": "detectSessionInUrl",
"kind": 32,
"kindString": "Variable",
@@ -1417,7 +1515,7 @@
"defaultValue": "true"
},
{
"id": 367,
"id": 372,
"name": "headers",
"kind": 32,
"kindString": "Variable",
@@ -1432,14 +1530,14 @@
"type": {
"type": "reflection",
"declaration": {
"id": 368,
"id": 373,
"name": "__type",
"kind": 65536,
"kindString": "Type literal",
"flags": {},
"children": [
{
"id": 369,
"id": 374,
"name": "X-Client-Info",
"kind": 32,
"kindString": "Variable",
@@ -1463,7 +1561,7 @@
"title": "Variables",
"kind": 32,
"children": [
369
374
]
}
]
@@ -1472,7 +1570,7 @@
"defaultValue": "DEFAULT_HEADERS"
},
{
"id": 365,
"id": 370,
"name": "persistSession",
"kind": 32,
"kindString": "Variable",
@@ -1491,7 +1589,7 @@
"defaultValue": "true"
},
{
"id": 363,
"id": 368,
"name": "schema",
"kind": 32,
"kindString": "Variable",
@@ -1515,11 +1613,11 @@
"title": "Variables",
"kind": 32,
"children": [
364,
366,
367,
365,
363
369,
371,
372,
370,
368
]
}
],
@@ -1548,7 +1646,7 @@
"title": "Object literals",
"kind": 2097152,
"children": [
362
367
]
}
],
@@ -1561,7 +1659,7 @@
]
},
{
"id": 370,
"id": 375,
"name": "\"index\"",
"kind": 1,
"kindString": "Module",
@@ -1571,7 +1669,7 @@
"originalName": "/home/runner/work/supabase-js/supabase-js/src/index.ts",
"children": [
{
"id": 371,
"id": 376,
"name": "createClient",
"kind": 64,
"kindString": "Function",
@@ -1584,7 +1682,7 @@
},
"signatures": [
{
"id": 372,
"id": 377,
"name": "createClient",
"kind": 4096,
"kindString": "Call signature",
@@ -1596,7 +1694,7 @@
},
"parameters": [
{
"id": 373,
"id": 378,
"name": "supabaseUrl",
"kind": 32768,
"kindString": "Parameter",
@@ -1609,7 +1707,7 @@
}
},
{
"id": 374,
"id": 379,
"name": "supabaseKey",
"kind": 32768,
"kindString": "Parameter",
@@ -1622,7 +1720,7 @@
}
},
{
"id": 375,
"id": 380,
"name": "options",
"kind": 32768,
"kindString": "Parameter",
@@ -1658,7 +1756,7 @@
"title": "Functions",
"kind": 64,
"children": [
371
376
]
}
],
@@ -9271,7 +9369,7 @@
"kind": 1,
"children": [
297,
370,
375,
62,
203,
173,
+42 -1
View File
@@ -128,6 +128,7 @@ info:
- auth.update()
- auth.setAuth()
- auth.onAuthStateChange()
- auth.api.getUser()
- Reset Password (Email)
- name: 'Auth (Server Only)'
items:
@@ -173,6 +174,7 @@ info:
- order()
- range()
- single()
- maybeSingle()
- name: 'Filters'
items:
- Using Filters
@@ -428,6 +430,10 @@ pages:
- If "Email Confirmations" is turned on, a `user` is returned but `session` will be null
- If "Email Confirmations" is turned off, both a `user` and a `session` will be returned
- When the user confirms their email address, they will be redirected to localhost:3000 by default. To change this, you can go to Authentication -> Settings on [app.supabase.io](https://app.supabase.io)
- If signUp() is called for an existing confirmed user:
- If "Enable email confirmations" is enabled on the "Authentication" -> "Settings" page, an obfuscated / fake user object will be returned.
- If "Enable email confirmations" is disabled, an error with a message "User already registered" will be returned.
- To check if a user already exists, refer to getUser().
examples:
- name: Sign up.
isSpotlight: true
@@ -565,6 +571,8 @@ pages:
auth.user():
title: 'user()'
$ref: '@supabase/gotrue-js.GoTrueClient.user'
notes: |
This method gets the user object from memory.
examples:
- name: Get the logged in user
isSpotlight: true
@@ -577,7 +585,9 @@ pages:
title: 'update()'
$ref: '@supabase/gotrue-js.GoTrueClient.update'
notes: |
User email: email updates will send an email to both the user's current and new email with a confirmation link.
User email: Email updates will send an email to both the user's current and new email with a confirmation link by default.
To toggle this behavior off and only send a single confirmation link to the new email, toggle "Double confirm email changes" under "Authentication" -> "Settings" off.
User metadata: It's generally better to store user data in a table inside your public schema (i.e. `public.users`).
Use the `update()` method if you have data which rarely changes or is specific only to the logged in user.
@@ -675,6 +685,22 @@ pages:
console.log(event, session)
})
```
auth.api.getUser():
title: 'getUser()'
$ref: '@supabase/gotrue-js.GoTrueApi.getUser'
notes: |
- Fetches the user object from the database instead of local storage.
- Note that user() fetches the user object from local storage which might not be the most updated.
- Requires the user's access_token.
examples:
- name: Fetch the user object using the access_token jwt.
isSpotlight: true
js: |
```js
const { user, error } = await supabase.auth.api.getUser(
'ACCESS_TOKEN_JWT',
)
```
Reset Password (Email):
$ref: '@supabase/gotrue-js.GoTrueApi.resetPasswordForEmail'
@@ -1611,9 +1637,24 @@ pages:
const { data, error } = await supabase
.from('cities')
.select('name, country_id')
.limit(1)
.single()
```
maybeSingle():
$ref: '@supabase/postgrest-js."lib/PostgrestFilterBuilder".PostgrestFilterBuilder.maybeSingle'
examples:
- name: With `select()`
isSpotlight: true
js: |
```js
const { data, error } = await supabase
.from('cities')
.select('name, country_id')
.eq('name', 'Singapore')
.maybeSingle()
```
Using Filters:
description: |
Filters can be used on `select()`, `update()`, and `delete()` queries.