mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
feat: allow disabling and re-enabling of anon and service_role (#36395)
* feat: allow disabling and re-enabling of `anon` and `service_role` * Cleanup the RQ hooks. * Restructure the enable/disable alert in API keys. * Minor cleanups. * Minor fixes. * Fix a type error. --------- Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
This commit is contained in:
1 parent
d6aa546f3e
commit
7c3f79578e
12 files changed
+358
-25
No files matched your search
@@ -8,7 +8,6 @@ import { useParams } from 'common'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import DatabaseSelector from 'components/ui/DatabaseSelector'
|
||||
import Panel from 'components/ui/Panel'
|
||||
import { DisplayApiSettings } from 'components/ui/ProjectSettings'
|
||||
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
|
||||
import { useJwtSecretUpdatingStatusQuery } from 'data/config/jwt-secret-updating-status-query'
|
||||
import { configKeys } from 'data/config/keys'
|
||||
@@ -19,9 +18,9 @@ import { PROJECT_STATUS } from 'lib/constants'
|
||||
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
|
||||
import { Badge, Input } from 'ui'
|
||||
import { JWT_SECRET_UPDATE_ERROR_MESSAGES } from './API.constants'
|
||||
import { ApiKeysMoved } from './ApiKeysMoved'
|
||||
import JWTSettings from './JWTSettings'
|
||||
import { PostgrestConfig } from './PostgrestConfig'
|
||||
import { ApiKeysMoved } from './ApiKeysMoved'
|
||||
|
||||
const ServiceList = () => {
|
||||
const client = useQueryClient()
|
||||
|
||||
@@ -122,8 +122,8 @@ const BuildingState = () => {
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-12 lg:col-span-8">
|
||||
<DisplayApiSettings />
|
||||
<div className="col-span-12 lg:col-span-8 flex flex-col gap-8">
|
||||
<DisplayApiSettings showLegacyText={false} />
|
||||
<DisplayConfigSettings />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { JwtSecretUpdateStatus } from '@supabase/shared-types/out/events'
|
||||
import { AlertCircle, Loader2 } from 'lucide-react'
|
||||
import { useMemo } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import Panel from 'components/ui/Panel'
|
||||
import { useJwtSecretUpdatingStatusQuery } from 'data/config/jwt-secret-updating-status-query'
|
||||
import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
import { AlertCircle, Loader2 } from 'lucide-react'
|
||||
import { useMemo } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import { Input } from 'ui'
|
||||
import { getLastUsedAPIKeys, useLastUsedAPIKeysLogQuery } from './DisplayApiSettings.utils'
|
||||
import { ToggleLegacyApiKeysPanel } from './ToggleLegacyApiKeys'
|
||||
|
||||
const DisplayApiSettings = ({
|
||||
legacy,
|
||||
export const DisplayApiSettings = ({
|
||||
showTitle = true,
|
||||
showNotice = true,
|
||||
showLegacyText = true,
|
||||
}: {
|
||||
legacy?: boolean
|
||||
showTitle?: boolean
|
||||
showNotice?: boolean
|
||||
showLegacyText?: boolean
|
||||
}) => {
|
||||
const { ref: projectRef } = useParams()
|
||||
|
||||
@@ -35,13 +39,16 @@ const DisplayApiSettings = ({
|
||||
} = useJwtSecretUpdatingStatusQuery({ projectRef })
|
||||
const jwtSecretUpdateStatus = data?.jwtSecretUpdateStatus
|
||||
|
||||
const { isLoading, can: canReadAPIKeys } = useAsyncCheckProjectPermissions(
|
||||
const { isLoading: isLoadingPermissions, can: canReadAPIKeys } = useAsyncCheckProjectPermissions(
|
||||
PermissionAction.READ,
|
||||
'service_api_keys'
|
||||
)
|
||||
|
||||
const isLoading = isProjectSettingsLoading || isLoadingPermissions
|
||||
|
||||
const isNotUpdatingJwtSecret =
|
||||
jwtSecretUpdateStatus === undefined || jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updated
|
||||
const apiKeys = settings?.service_api_keys ?? []
|
||||
const apiKeys = useMemo(() => settings?.service_api_keys ?? [], [settings])
|
||||
// api keys should not be empty. However it can be populated with a delay on project creation
|
||||
const isApiKeysEmpty = apiKeys.length === 0
|
||||
|
||||
@@ -66,8 +73,9 @@ const DisplayApiSettings = ({
|
||||
return (
|
||||
<>
|
||||
<Panel
|
||||
noMargin
|
||||
title={
|
||||
!legacy && (
|
||||
showTitle && (
|
||||
<div className="space-y-3">
|
||||
<h5 className="text-base">Project API Keys</h5>
|
||||
<p className="text-sm text-foreground-light">
|
||||
@@ -159,9 +167,9 @@ const DisplayApiSettings = ({
|
||||
descriptionText={
|
||||
x.tags === 'service_role'
|
||||
? 'This key has the ability to bypass Row Level Security. Never share it publicly. If leaked, generate a new JWT secret immediately. ' +
|
||||
(legacy ? 'Prefer using Publishable API keys instead.' : '')
|
||||
(showLegacyText ? 'Prefer using Publishable API keys instead.' : '')
|
||||
: 'This key is safe to use in a browser if you have enabled Row Level Security for your tables and configured policies. ' +
|
||||
(legacy ? 'Prefer using Secret API keys instead.' : '')
|
||||
(showLegacyText ? 'Prefer using Secret API keys instead.' : '')
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -201,7 +209,7 @@ const DisplayApiSettings = ({
|
||||
)
|
||||
) : null}
|
||||
</Panel>
|
||||
{newApiKeysInRollOut && !showNotice && <ToggleLegacyApiKeysPanel />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default DisplayApiSettings
|
||||
@@ -0,0 +1,130 @@
|
||||
import { useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { useToggleLegacyAPIKeysMutation } from 'data/api-keys/legacy-api-key-toggle-mutation'
|
||||
import { useLegacyAPIKeysStatusQuery } from 'data/api-keys/legacy-api-keys-status-query'
|
||||
import { useAsyncCheckProjectPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { Alert_Shadcn_, AlertDescription_Shadcn_, AlertTitle_Shadcn_, CriticalIcon } from 'ui'
|
||||
import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal'
|
||||
|
||||
export const ToggleLegacyApiKeysPanel = () => {
|
||||
const { project } = useProjectContext()
|
||||
const [isConfirmOpen, setIsConfirmOpen] = useState(false)
|
||||
|
||||
const { data: legacyAPIKeysStatusData, isSuccess: isLegacyAPIKeysStatusSuccess } =
|
||||
useLegacyAPIKeysStatusQuery({ projectRef: project!.ref })
|
||||
|
||||
const { can: canUpdateAPIKeys, isSuccess: isPermissionsSuccess } =
|
||||
useAsyncCheckProjectPermissions(PermissionAction.SECRETS_WRITE, '*')
|
||||
|
||||
if (!(isLegacyAPIKeysStatusSuccess && isPermissionsSuccess)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
<Alert_Shadcn_ variant={legacyAPIKeysStatusData.enabled ? 'destructive' : 'warning'}>
|
||||
<CriticalIcon />
|
||||
<AlertTitle_Shadcn_>
|
||||
{legacyAPIKeysStatusData.enabled
|
||||
? 'Disabling your legacy API keys may cause your applications to break.'
|
||||
: 'Re-enabling your legacy API keys may expose your applications to security risks.'}
|
||||
</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_>
|
||||
{legacyAPIKeysStatusData.enabled
|
||||
? 'Make sure you are no longer using your legacy API keys before proceeding.'
|
||||
: "Make sure you've tested your RLS policies."}
|
||||
</AlertDescription_Shadcn_>
|
||||
<div className="mt-2">
|
||||
<ButtonTooltip
|
||||
type={legacyAPIKeysStatusData.enabled ? 'danger' : 'warning'}
|
||||
onClick={() => setIsConfirmOpen(true)}
|
||||
disabled={!canUpdateAPIKeys}
|
||||
tooltip={{
|
||||
content: {
|
||||
side: 'bottom',
|
||||
text: !canUpdateAPIKeys
|
||||
? 'You need additional permissions to enable or disable JWT-based API keys'
|
||||
: undefined,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{legacyAPIKeysStatusData.enabled
|
||||
? 'Disable JWT-based API keys'
|
||||
: 'Re-enable JWT-based API keys'}
|
||||
</ButtonTooltip>
|
||||
</div>
|
||||
</Alert_Shadcn_>
|
||||
|
||||
<ToggleApiKeysModal
|
||||
visible={isConfirmOpen}
|
||||
onClose={() => setIsConfirmOpen(false)}
|
||||
legacyAPIKeysStatusData={legacyAPIKeysStatusData}
|
||||
/>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
const ToggleApiKeysModal = ({
|
||||
visible,
|
||||
onClose,
|
||||
legacyAPIKeysStatusData,
|
||||
}: {
|
||||
visible: boolean
|
||||
onClose: () => void
|
||||
legacyAPIKeysStatusData: { enabled: boolean }
|
||||
}) => {
|
||||
const { project } = useProjectContext()
|
||||
|
||||
const { mutate: toggleLegacyAPIKey, isLoading: isTogglingLegacyAPIKey } =
|
||||
useToggleLegacyAPIKeysMutation()
|
||||
|
||||
const onToggleLegacyAPIKeysEnabled = () => {
|
||||
const enabled = !legacyAPIKeysStatusData?.enabled
|
||||
|
||||
toggleLegacyAPIKey(
|
||||
{ projectRef: project!.ref, enabled },
|
||||
{
|
||||
onSuccess: () => {
|
||||
toast.success(
|
||||
enabled
|
||||
? 'Your anon and service_role keys have been re-enabled!'
|
||||
: 'Your anon and service_role keys have been disabled!'
|
||||
)
|
||||
onClose()
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<TextConfirmModal
|
||||
visible={visible}
|
||||
onCancel={() => onClose()}
|
||||
onConfirm={onToggleLegacyAPIKeysEnabled}
|
||||
title={
|
||||
legacyAPIKeysStatusData.enabled ? 'Disable JWT-based keys' : 'Re-enable JWT-based keys'
|
||||
}
|
||||
confirmString={legacyAPIKeysStatusData.enabled ? 'disable' : 're-enable'}
|
||||
confirmLabel={`Yes, ${legacyAPIKeysStatusData.enabled ? 'disable' : 're-enable'} anon and service_role`}
|
||||
confirmPlaceholder={legacyAPIKeysStatusData.enabled ? 'disable' : 're-enable'}
|
||||
loading={isTogglingLegacyAPIKey}
|
||||
variant={legacyAPIKeysStatusData.enabled ? 'destructive' : 'default'}
|
||||
alert={
|
||||
legacyAPIKeysStatusData.enabled
|
||||
? {
|
||||
title: 'Disabling can cause downtime!',
|
||||
description: `If you disable your anon and service_role keys while they are in use, your applications will stop functioning. All API endpoints will receive HTTP 401 Unauthorized. Make sure you are no longer using them before proceeding.`,
|
||||
}
|
||||
: {
|
||||
title: 'Prefer publishable and secret keys',
|
||||
description:
|
||||
'While re-enabling anon and service_role keys makes sense in some cases, a better and more secure alternative is the publishable or secret key. Consider using those before proceeding!',
|
||||
}
|
||||
}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import DisplayApiSettings from './DisplayApiSettings'
|
||||
import { DisplayApiSettings } from './DisplayApiSettings'
|
||||
import DisplayConfigSettings from './DisplayConfigSettings'
|
||||
|
||||
export { DisplayApiSettings, DisplayConfigSettings }
|
||||
@@ -43,12 +43,12 @@ type PublishableKeys = {
|
||||
updated_at?: string
|
||||
}
|
||||
|
||||
export interface APIKeysVariables {
|
||||
interface APIKeysVariables {
|
||||
projectRef?: string
|
||||
reveal: boolean
|
||||
}
|
||||
|
||||
export async function getAPIKeys({ projectRef, reveal }: APIKeysVariables, signal?: AbortSignal) {
|
||||
async function getAPIKeys({ projectRef, reveal }: APIKeysVariables, signal?: AbortSignal) {
|
||||
if (!projectRef) throw new Error('projectRef is required')
|
||||
|
||||
const { data, error } = await get(`/v1/projects/{ref}/api-keys`, {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const apiKeysKeys = {
|
||||
list: (projectRef?: string) => ['projects', projectRef, 'api-keys'] as const,
|
||||
single: (projectRef?: string, id?: string) => ['projects', projectRef, 'api-keys', id] as const,
|
||||
status: (projectRef?: string) => ['projects', projectRef, 'api-keys', 'legacy'] as const,
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query'
|
||||
import { handleError, put } from 'data/fetchers'
|
||||
import { toast } from 'sonner'
|
||||
import type { ResponseError } from 'types'
|
||||
import { apiKeysKeys } from './keys'
|
||||
|
||||
export type ToggleLegacyAPIKeysVariables = {
|
||||
projectRef?: string
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export async function toggleLegacyAPIKeys(payload: ToggleLegacyAPIKeysVariables) {
|
||||
if (!payload.projectRef) throw new Error('projectRef is required')
|
||||
|
||||
const { data, error } = await put('/v1/projects/{ref}/api-keys/legacy', {
|
||||
params: {
|
||||
path: { ref: payload.projectRef },
|
||||
query: { enabled: payload.enabled },
|
||||
},
|
||||
})
|
||||
|
||||
if (error) handleError(error)
|
||||
return data
|
||||
}
|
||||
|
||||
type ToggleLegacyAPIKeysData = Awaited<ReturnType<typeof toggleLegacyAPIKeys>>
|
||||
|
||||
export const useToggleLegacyAPIKeysMutation = ({
|
||||
onSuccess,
|
||||
onError,
|
||||
...options
|
||||
}: Omit<
|
||||
UseMutationOptions<ToggleLegacyAPIKeysData, ResponseError, ToggleLegacyAPIKeysVariables>,
|
||||
'mutationFn'
|
||||
> = {}) => {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
return useMutation<ToggleLegacyAPIKeysData, ResponseError, ToggleLegacyAPIKeysVariables>(
|
||||
(vars) => toggleLegacyAPIKeys(vars),
|
||||
{
|
||||
async onSuccess(data, variables, context) {
|
||||
const { projectRef } = variables
|
||||
|
||||
await queryClient.invalidateQueries(apiKeysKeys.status(projectRef))
|
||||
|
||||
await onSuccess?.(data, variables, context)
|
||||
},
|
||||
async onError(data, variables, context) {
|
||||
if (onError === undefined) {
|
||||
toast.error(
|
||||
`Failed to ${variables.enabled ? 're-enable' : 'disable'} JWT-based API keys: ${data.message}`
|
||||
)
|
||||
} else {
|
||||
onError(data, variables, context)
|
||||
}
|
||||
},
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||
import { get, handleError } from 'data/fetchers'
|
||||
import { ResponseError } from 'types'
|
||||
import { apiKeysKeys } from './keys'
|
||||
|
||||
interface LegacyAPIKeysStatusVariables {
|
||||
projectRef?: string
|
||||
}
|
||||
|
||||
async function getLegacyAPIKeysStatus(
|
||||
{ projectRef }: LegacyAPIKeysStatusVariables,
|
||||
signal?: AbortSignal
|
||||
) {
|
||||
if (!projectRef) throw new Error('projectRef is required')
|
||||
|
||||
const { data, error } = await get(`/v1/projects/{ref}/api-keys/legacy`, {
|
||||
params: { path: { ref: projectRef } },
|
||||
signal,
|
||||
})
|
||||
|
||||
if (error) {
|
||||
handleError(error)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
type LegacyAPIKeysStatusData = Awaited<ReturnType<typeof getLegacyAPIKeysStatus>>
|
||||
|
||||
export const useLegacyAPIKeysStatusQuery = <TData = LegacyAPIKeysStatusData>(
|
||||
{ projectRef }: LegacyAPIKeysStatusVariables,
|
||||
{ ...options }: UseQueryOptions<LegacyAPIKeysStatusData, ResponseError, TData> = {}
|
||||
) =>
|
||||
useQuery<LegacyAPIKeysStatusData, ResponseError, TData>(
|
||||
apiKeysKeys.status(projectRef),
|
||||
({ signal }) => getLegacyAPIKeysStatus({ projectRef }, signal),
|
||||
{
|
||||
enabled: !!projectRef,
|
||||
...options,
|
||||
}
|
||||
)
|
||||
@@ -5,7 +5,7 @@ import { DisplayApiSettings } from 'components/ui/ProjectSettings'
|
||||
import type { NextPageWithLayout } from 'types'
|
||||
|
||||
const ApiKeysLegacyPage: NextPageWithLayout = () => {
|
||||
return <DisplayApiSettings legacy showNotice={false} />
|
||||
return <DisplayApiSettings showTitle={false} showNotice={false} />
|
||||
}
|
||||
|
||||
ApiKeysLegacyPage.getLayout = (page) => (
|
||||
|
||||
Vendored
+88
-1
@@ -395,6 +395,24 @@ export interface paths {
|
||||
patch: operations['updateApiKey']
|
||||
trace?: never
|
||||
}
|
||||
'/v1/projects/{ref}/api-keys/legacy': {
|
||||
parameters: {
|
||||
query?: never
|
||||
header?: never
|
||||
path?: never
|
||||
cookie?: never
|
||||
}
|
||||
/** [Beta] Check whether JWT based legacy (anon, service_role) API keys are enabled. This API endpoint will be removed in the future, check for HTTP 404 Not Found. */
|
||||
get: operations['checkLegacyApiKeys']
|
||||
/** [Beta] Disable or re-enable JWT based legacy (anon, service_role) API keys. This API endpoint will be removed in the future, check for HTTP 404 Not Found. */
|
||||
put: operations['updateLegacyApiKeys']
|
||||
post?: never
|
||||
delete?: never
|
||||
options?: never
|
||||
head?: never
|
||||
patch?: never
|
||||
trace?: never
|
||||
}
|
||||
'/v1/projects/{ref}/billing/addons': {
|
||||
parameters: {
|
||||
query?: never
|
||||
@@ -2062,6 +2080,9 @@ export interface components {
|
||||
}
|
||||
updated_at?: string
|
||||
}
|
||||
LegacyApiKeysResponse: {
|
||||
enabled: boolean
|
||||
}
|
||||
ListProjectAddonsResponse: {
|
||||
available_addons: {
|
||||
name: string
|
||||
@@ -2967,7 +2988,11 @@ export interface components {
|
||||
allowed_release_channels: ('internal' | 'alpha' | 'beta' | 'ga' | 'withdrawn' | 'preview')[]
|
||||
id: string
|
||||
name: string
|
||||
opt_in_tags: 'AI_SQL_GENERATOR_OPT_IN'[]
|
||||
opt_in_tags: (
|
||||
| 'AI_SQL_GENERATOR_OPT_IN'
|
||||
| 'AI_DATA_GENERATOR_OPT_IN'
|
||||
| 'AI_LOG_GENERATOR_OPT_IN'
|
||||
)[]
|
||||
/** @enum {string} */
|
||||
plan?: 'free' | 'pro' | 'team' | 'enterprise'
|
||||
}
|
||||
@@ -3955,8 +3980,11 @@ export interface operations {
|
||||
deleteApiKey: {
|
||||
parameters: {
|
||||
query?: {
|
||||
reason?: string
|
||||
/** @description Boolean string, true or false */
|
||||
reveal?: boolean
|
||||
/** @description Boolean string, true or false */
|
||||
was_compromised?: boolean
|
||||
}
|
||||
header?: never
|
||||
path: {
|
||||
@@ -4020,6 +4048,65 @@ export interface operations {
|
||||
}
|
||||
}
|
||||
}
|
||||
checkLegacyApiKeys: {
|
||||
parameters: {
|
||||
query?: never
|
||||
header?: never
|
||||
path: {
|
||||
/** @description Project ref */
|
||||
ref: string
|
||||
}
|
||||
cookie?: never
|
||||
}
|
||||
requestBody?: never
|
||||
responses: {
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown
|
||||
}
|
||||
content: {
|
||||
'application/json': components['schemas']['LegacyApiKeysResponse']
|
||||
}
|
||||
}
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown
|
||||
}
|
||||
content?: never
|
||||
}
|
||||
}
|
||||
}
|
||||
updateLegacyApiKeys: {
|
||||
parameters: {
|
||||
query: {
|
||||
/** @description Boolean string, true or false */
|
||||
enabled: boolean
|
||||
}
|
||||
header?: never
|
||||
path: {
|
||||
/** @description Project ref */
|
||||
ref: string
|
||||
}
|
||||
cookie?: never
|
||||
}
|
||||
requestBody?: never
|
||||
responses: {
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown
|
||||
}
|
||||
content: {
|
||||
'application/json': components['schemas']['LegacyApiKeysResponse']
|
||||
}
|
||||
}
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown
|
||||
}
|
||||
content?: never
|
||||
}
|
||||
}
|
||||
}
|
||||
'v1-list-project-addons': {
|
||||
parameters: {
|
||||
query?: never
|
||||
|
||||
+11
-4
@@ -4251,7 +4251,7 @@ export interface components {
|
||||
/** @default 0 */
|
||||
recoveryTimeTarget?: number
|
||||
}
|
||||
ConfirmSubscriptionChangeBody: {
|
||||
ConfirmCreateSubscriptionChangeBody: {
|
||||
kind?: string
|
||||
name: string
|
||||
payment_intent_id: string
|
||||
@@ -4554,6 +4554,7 @@ export interface components {
|
||||
is_owner: boolean
|
||||
name: string
|
||||
opt_in_tags: string[]
|
||||
organization_requires_mfa: boolean
|
||||
plan: {
|
||||
/** @enum {string} */
|
||||
id: 'free' | 'pro' | 'team' | 'enterprise'
|
||||
@@ -6151,6 +6152,7 @@ export interface components {
|
||||
is_owner: boolean
|
||||
name: string
|
||||
opt_in_tags: string[]
|
||||
organization_requires_mfa: boolean
|
||||
plan: {
|
||||
/** @enum {string} */
|
||||
id: 'free' | 'pro' | 'team' | 'enterprise'
|
||||
@@ -7274,8 +7276,9 @@ export interface components {
|
||||
}[]
|
||||
}
|
||||
RunQueryBody: {
|
||||
query: string
|
||||
/** @default false */
|
||||
disable_statement_timeout?: boolean
|
||||
query: string
|
||||
}
|
||||
SearchProfileBody: {
|
||||
keywords: string
|
||||
@@ -8032,7 +8035,11 @@ export interface components {
|
||||
/** Format: email */
|
||||
billing_email?: string
|
||||
name?: string
|
||||
opt_in_tags?: 'AI_SQL_GENERATOR_OPT_IN'[]
|
||||
opt_in_tags?: (
|
||||
| 'AI_SQL_GENERATOR_OPT_IN'
|
||||
| 'AI_DATA_GENERATOR_OPT_IN'
|
||||
| 'AI_LOG_GENERATOR_OPT_IN'
|
||||
)[]
|
||||
}
|
||||
UpdateOrganizationResponse: {
|
||||
billing_email?: string
|
||||
@@ -12047,7 +12054,7 @@ export interface operations {
|
||||
}
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': components['schemas']['ConfirmSubscriptionChangeBody']
|
||||
'application/json': components['schemas']['ConfirmCreateSubscriptionChangeBody']
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
|
||||
Reference in new issue
Block a user