diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationPanel.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationPanel.tsx index 812ad66d7b83d..102a14e09fe13 100644 --- a/apps/studio/components/interfaces/Database/Replication/DestinationPanel.tsx +++ b/apps/studio/components/interfaces/Database/Replication/DestinationPanel.tsx @@ -123,20 +123,24 @@ export const DestinationPanel = ({ pipelineId: existingDestination?.pipelineId, }) - const defaultValues = useMemo( - () => ({ + const defaultValues = useMemo(() => { + const bigQueryConfig = + destinationData && 'big_query' in destinationData.config + ? destinationData?.config.big_query + : null + + return { type: TypeEnum.enum.BigQuery, name: destinationData?.name ?? '', - projectId: destinationData?.config?.big_query?.project_id ?? '', - datasetId: destinationData?.config?.big_query?.dataset_id ?? '', + projectId: bigQueryConfig?.project_id ?? '', + datasetId: bigQueryConfig?.dataset_id ?? '', // For now, the password will always be set as empty for security reasons. - serviceAccountKey: destinationData?.config?.big_query?.service_account_key ?? '', + serviceAccountKey: bigQueryConfig?.service_account_key ?? '', publicationName: pipelineData?.config.publication_name ?? '', maxFillMs: pipelineData?.config?.batch?.max_fill_ms, - maxStalenessMins: destinationData?.config?.big_query?.max_staleness_mins, - }), - [destinationData, pipelineData] - ) + maxStalenessMins: bigQueryConfig?.max_staleness_mins, + } + }, [destinationData, pipelineData]) const form = useForm>({ mode: 'onBlur', diff --git a/apps/studio/components/interfaces/Database/Replication/Destinations.tsx b/apps/studio/components/interfaces/Database/Replication/Destinations.tsx index 88a6a25458872..7cef5dd364b4a 100644 --- a/apps/studio/components/interfaces/Database/Replication/Destinations.tsx +++ b/apps/studio/components/interfaces/Database/Replication/Destinations.tsx @@ -159,7 +159,7 @@ export const Destinations = () => { sourceId={sourceId} destinationId={destination.id} destinationName={destination.name} - type={destination.config.big_query ? 'BigQuery' : 'Other'} + type={'big_query' in destination.config ? 'BigQuery' : 'Other'} pipeline={pipeline} error={pipelinesError} isLoading={isPipelinesLoading} diff --git a/apps/studio/components/interfaces/DiskManagement/DiskManagementForm.tsx b/apps/studio/components/interfaces/DiskManagement/DiskManagementForm.tsx index f02ce81f0ada1..34b228bc60ef2 100644 --- a/apps/studio/components/interfaces/DiskManagement/DiskManagementForm.tsx +++ b/apps/studio/components/interfaces/DiskManagement/DiskManagementForm.tsx @@ -71,7 +71,8 @@ export function DiskManagementForm() { const { data: org } = useSelectedOrganizationQuery() const { setProjectStatus } = useSetProjectStatus() - const { data: resourceWarnings } = useResourceWarningsQuery() + const { data: resourceWarnings } = useResourceWarningsQuery({ ref: projectRef }) + // [Joshen Cleanup] JFYI this client side filtering can be cleaned up once BE changes are live which will only return the warnings based on the provided ref const projectResourceWarnings = (resourceWarnings ?? [])?.find( (warning) => warning.project === project?.ref ) diff --git a/apps/studio/components/interfaces/Home/ProjectList/ProjectList.tsx b/apps/studio/components/interfaces/Home/ProjectList/ProjectList.tsx index 5bfb9b0214d73..98b05396dc607 100644 --- a/apps/studio/components/interfaces/Home/ProjectList/ProjectList.tsx +++ b/apps/studio/components/interfaces/Home/ProjectList/ProjectList.tsx @@ -86,7 +86,7 @@ export const ProjectList = ({ organization: organization_, rewriteHref }: Projec isError: isErrorPermissions, error: permissionsError, } = usePermissionsQuery() - const { data: resourceWarnings } = useResourceWarningsQuery() + const { data: resourceWarnings } = useResourceWarningsQuery({ slug }) // Move all hooks to the top to comply with Rules of Hooks const { data: integrations } = useOrgIntegrationsQuery({ orgSlug: organization?.slug }) diff --git a/apps/studio/components/interfaces/Settings/Database/DatabaseReadOnlyAlert.tsx b/apps/studio/components/interfaces/Settings/Database/DatabaseReadOnlyAlert.tsx index 79beef58d7c54..2c0ad328f65a1 100644 --- a/apps/studio/components/interfaces/Settings/Database/DatabaseReadOnlyAlert.tsx +++ b/apps/studio/components/interfaces/Settings/Database/DatabaseReadOnlyAlert.tsx @@ -14,8 +14,9 @@ export const DatabaseReadOnlyAlert = () => { const { data: organization } = useSelectedOrganizationQuery() const [showConfirmationModal, setShowConfirmationModal] = useState(false) - const { data: resourceWarnings } = useResourceWarningsQuery() - + const { data: resourceWarnings } = useResourceWarningsQuery({ ref: projectRef }) + // [Joshen Cleanup] JFYI this can be cleaned up once BE changes are live which will only return the warnings based on the provided ref + // No longer need to filter by ref on the client side const isReadOnlyMode = (resourceWarnings ?? [])?.find((warning) => warning.project === projectRef) ?.is_readonly_mode_enabled ?? false diff --git a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx index 582c7b2f8230f..04840ec30dfb3 100644 --- a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx +++ b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx @@ -59,7 +59,8 @@ export const InfrastructureActivity = () => { }) const isFreePlan = organization?.plan?.id === 'free' - const { data: resourceWarnings } = useResourceWarningsQuery() + const { data: resourceWarnings } = useResourceWarningsQuery({ ref: projectRef }) + // [Joshen Cleanup] JFYI this client side filtering can be cleaned up once BE changes are live which will only return the warnings based on the provided ref const projectResourceWarnings = resourceWarnings?.find((x) => x.project === projectRef) const { data: addons } = useProjectAddonsQuery({ projectRef }) diff --git a/apps/studio/components/interfaces/Support/ProjectAndPlanInfo.tsx b/apps/studio/components/interfaces/Support/ProjectAndPlanInfo.tsx index 0d85a5015c55b..dcb593f5bc46b 100644 --- a/apps/studio/components/interfaces/Support/ProjectAndPlanInfo.tsx +++ b/apps/studio/components/interfaces/Support/ProjectAndPlanInfo.tsx @@ -11,9 +11,9 @@ import { OrganizationProjectSelector } from 'components/ui/OrganizationProjectSe import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { Button, + cn, CommandGroup_Shadcn_, CommandItem_Shadcn_, - cn, FormControl_Shadcn_, FormField_Shadcn_, } from 'ui' @@ -76,6 +76,7 @@ function ProjectSelector({ form, orgSlug, projectRef }: ProjectSelectorProps) { { expect(submitSpy).toHaveBeenCalledTimes(1) }) expect(submitSpy.mock.calls[0]?.[0]?.dashboardSentryIssueId).toBe(sentryIssueId) - }) + }, 10_000) test('includes initial error message from URL in submission payload', async () => { const initialError = 'failed to fetch user data' @@ -551,7 +551,7 @@ describe('SupportFormPage', () => { const payload = submitSpy.mock.calls[0]?.[0] expect(payload?.message).toMatch(initialError) - }) + }, 10_000) test('submits support request with problem category, library, and affected services', async () => { const submitSpy = vi.fn() @@ -640,7 +640,7 @@ describe('SupportFormPage', () => { await waitFor(() => { expect(screen.getByRole('heading', { name: /success/i })).toBeInTheDocument() }) - }) + }, 10_000) test('submits urgent login issues ticket for a different organization', async () => { const submitSpy = vi.fn() @@ -729,7 +729,7 @@ describe('SupportFormPage', () => { await waitFor(() => { expect(screen.getByRole('heading', { name: /success/i })).toBeInTheDocument() }) - }) + }, 10_000) test('submits database unresponsive ticket with initial error', async () => { const submitSpy = vi.fn() @@ -829,7 +829,7 @@ describe('SupportFormPage', () => { await waitFor(() => { expect(screen.getByRole('heading', { name: /success/i })).toBeInTheDocument() }) - }) + }, 10_000) test('when organization changes, project selector updates to match', async () => { renderSupportFormPage() @@ -968,7 +968,7 @@ describe('SupportFormPage', () => { expect(screen.getByRole('heading', { name: /success/i })).toBeInTheDocument() }) } - }) + }, 10_000) test('shows toast on submission error and allows form re-editing and resubmission', async () => { const submitSpy = vi.fn() @@ -1046,7 +1046,7 @@ describe('SupportFormPage', () => { await waitFor(() => { expect(screen.getByRole('heading', { name: /success/i })).toBeInTheDocument() }) - }) + }, 10_000) test('submits support request with attachments and includes attachment URLs in message', async () => { const submitSpy = vi.fn() @@ -1199,7 +1199,7 @@ describe('SupportFormPage', () => { url.revokeObjectURL = originalRevokeObjectURL vi.mocked(createSupportStorageClient).mockReset() } - }) + }, 10_000) test('can submit form with no organizations and no projects', async () => { const submitSpy = vi.fn() @@ -1266,5 +1266,5 @@ describe('SupportFormPage', () => { await waitFor(() => { expect(screen.getByRole('heading', { name: /success/i })).toBeInTheDocument() }) - }) + }, 10_000) }) diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.tsx index 780093e1517c9..10690bd227b12 100644 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableEditor.tsx @@ -1,6 +1,7 @@ import type { PostgresTable } from '@supabase/postgres-meta' +import dayjs from 'dayjs' import { isEmpty, isUndefined, noop } from 'lodash' -import { useEffect, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import { toast } from 'sonner' import { DocsButton } from 'components/ui/DocsButton' @@ -24,6 +25,7 @@ import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import { useUrlState } from 'hooks/ui/useUrlState' import { useProtectedSchemas } from 'hooks/useProtectedSchemas' import { DOCS_URL } from 'lib/constants' +import { usePHFlag } from 'hooks/ui/useFlag' import { useTableEditorStateSnapshot } from 'state/table-editor' import { Badge, Checkbox, Input, SidePanel } from 'ui' import { Admonition } from 'ui-patterns' @@ -45,6 +47,12 @@ import { generateTableFieldFromPostgresTable, validateFields, } from './TableEditor.utils' +import { TableTemplateSelector } from './TableQuickstart/TableTemplateSelector' +import { QuickstartVariant } from './TableQuickstart/types' +import { LOCAL_STORAGE_KEYS } from 'common' +import { useLocalStorage } from 'hooks/misc/useLocalStorage' + +const NEW_PROJECT_THRESHOLD_DAYS = 7 export interface TableEditorProps { table?: PostgresTable @@ -90,6 +98,31 @@ export const TableEditor = ({ const { realtimeAll: realtimeEnabled } = useIsFeatureEnabled(['realtime:all']) const { mutate: sendEvent } = useSendEventMutation() + /** + * Returns: + * - `QuickstartVariant`: user variation (if bucketed) + * - `false`: user not yet bucketed or targeted + * - `undefined`: posthog still loading + */ + const tableQuickstartVariant = usePHFlag('tableQuickstart') + + const [quickstartDismissed, setQuickstartDismissed] = useLocalStorage( + LOCAL_STORAGE_KEYS.TABLE_QUICKSTART_DISMISSED, + false + ) + + const isRecentProject = useMemo(() => { + if (!project?.inserted_at) return false + return dayjs().diff(dayjs(project.inserted_at), 'day') < NEW_PROJECT_THRESHOLD_DAYS + }, [project?.inserted_at]) + + const shouldShowTemplateQuickstart = + isNewRecord && + !isDuplicating && + tableQuickstartVariant === QuickstartVariant.TEMPLATES && + !quickstartDismissed && + isRecentProject + const { docsRowLevelSecurityGuidePath } = useCustomContent(['docs:row_level_security_guide_path']) const [params, setParams] = useUrlState() @@ -280,6 +313,20 @@ export const TableEditor = ({ } > + {shouldShowTemplateQuickstart && ( + { + const updates: Partial = {} + if (template.name) updates.name = template.name + if (template.comment) updates.comment = template.comment + if (template.columns) updates.columns = template.columns + onUpdateField(updates) + }} + onDismiss={() => setQuickstartDismissed(true)} + disabled={false} + /> + )} // [Sean] this will be used in PR #38934 + onSelectTemplate: (tableField: Partial) => void + onDismiss?: () => void + disabled?: boolean +} + +const SUCCESS_MESSAGE_DURATION_MS = 3000 + +export const TableTemplateSelector = ({ + variant: _variant, + onSelectTemplate, + onDismiss, + disabled, +}: TableTemplateSelectorProps) => { + const [activeCategory, setActiveCategory] = useState(null) // null => All + const [selectedTemplate, setSelectedTemplate] = useState(null) + + const handleSelectTemplate = useCallback( + (template: TableSuggestion) => { + const tableField = convertTableSuggestionToTableField(template) + onSelectTemplate(tableField) + setSelectedTemplate(template) + toast.success( + `${template.tableName} template applied. You can add or modify the fields below.`, + { + duration: SUCCESS_MESSAGE_DURATION_MS, + } + ) + }, + [onSelectTemplate] + ) + + const categories = useMemo(() => Object.keys(tableTemplates), []) + + useEffect(() => { + if (activeCategory === null && categories.length > 0) { + setActiveCategory(categories[0]) + } + }, [categories, activeCategory]) + + const displayed = useMemo( + () => (activeCategory ? tableTemplates[activeCategory] || [] : []), + [activeCategory] + ) + + return ( +
+
+
+

Start faster with a table template

+

+ Save time by starting from a ready-made table schema. +

+
+ {onDismiss && ( + + )} +
+ +
+ {categories.map((category) => ( + + ))} +
+ +
+ {displayed.map((t) => ( + + ))} +
+
+ ) +} diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableQuickstart/constants.ts b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableQuickstart/constants.ts new file mode 100644 index 0000000000000..506a4e7d0cbbe --- /dev/null +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableQuickstart/constants.ts @@ -0,0 +1,12 @@ +export const LIMITS = { + MAX_PROMPT_LENGTH: 500, + MAX_TABLES_TO_GENERATE: 3, + MIN_TABLES_TO_GENERATE: 2, +} as const + +export const AI_QUICK_IDEAS = [ + 'Recipe sharing app', + 'Event ticketing system', + 'Fitness tracker', + 'Learning management platform', +] as const diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableQuickstart/templates.ts b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableQuickstart/templates.ts new file mode 100644 index 0000000000000..0566358236c61 --- /dev/null +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableQuickstart/templates.ts @@ -0,0 +1,416 @@ +import { TableSource, TableSuggestion } from './types' + +export const tableTemplates: Record = { + 'Social Network': [ + { + tableName: 'profiles', + fields: [ + { + name: 'id', + type: 'uuid', + nullable: false, + isPrimary: true, + default: 'gen_random_uuid()', + }, + { + name: 'user_id', + type: 'uuid', + nullable: false, + unique: true, + isForeign: true, + references: 'auth.users(id)', + }, + { name: 'username', type: 'text', nullable: true, unique: true }, + { name: 'display_name', type: 'text', nullable: true }, + { name: 'avatar_url', type: 'text', nullable: true }, + { name: 'bio', type: 'text', nullable: true }, + { name: 'location', type: 'text', nullable: true }, + { name: 'website', type: 'text', nullable: true }, + { name: 'created_at', type: 'timestamptz', nullable: false, default: 'now()' }, + { name: 'updated_at', type: 'timestamptz', nullable: false, default: 'now()' }, + ], + rationale: 'Stores user profile details such as name, avatar, and bio', + source: TableSource.TEMPLATE, + }, + { + tableName: 'posts', + fields: [ + { + name: 'id', + type: 'uuid', + nullable: false, + isPrimary: true, + default: 'gen_random_uuid()', + }, + { + name: 'author_id', + type: 'uuid', + nullable: false, + isForeign: true, + references: 'profiles(id)', + }, + { name: 'content', type: 'text', nullable: false }, + { name: 'image_url', type: 'text', nullable: true }, + { name: 'likes_count', type: 'int4', nullable: false, default: '0' }, + { name: 'comments_count', type: 'int4', nullable: false, default: '0' }, + { name: 'created_at', type: 'timestamptz', nullable: false, default: 'now()' }, + { name: 'updated_at', type: 'timestamptz', nullable: false, default: 'now()' }, + ], + rationale: 'Stores user posts, images, and engagement counts', + source: TableSource.TEMPLATE, + }, + { + tableName: 'follows', + fields: [ + { + name: 'id', + type: 'uuid', + nullable: false, + isPrimary: true, + default: 'gen_random_uuid()', + }, + { + name: 'follower_id', + type: 'uuid', + nullable: false, + isForeign: true, + references: 'profiles(id)', + }, + { + name: 'following_id', + type: 'uuid', + nullable: false, + isForeign: true, + references: 'profiles(id)', + }, + { name: 'created_at', type: 'timestamptz', nullable: false, default: 'now()' }, + ], + rationale: 'Tracks relationships between followers and followed users', + source: TableSource.TEMPLATE, + }, + ], + 'E-commerce': [ + { + tableName: 'products', + fields: [ + { + name: 'id', + type: 'uuid', + nullable: false, + isPrimary: true, + default: 'gen_random_uuid()', + }, + { name: 'name', type: 'text', nullable: false }, + { name: 'slug', type: 'text', nullable: false, unique: true }, + { name: 'description', type: 'text', nullable: true }, + { name: 'price', type: 'numeric', nullable: false }, + { name: 'compare_at_price', type: 'numeric', nullable: true }, + { name: 'cost', type: 'numeric', nullable: true }, + { name: 'sku', type: 'text', nullable: true, unique: true }, + { name: 'barcode', type: 'text', nullable: true }, + { name: 'stock_quantity', type: 'int4', nullable: false, default: '0' }, + { name: 'weight', type: 'numeric', nullable: true }, + { name: 'published', type: 'bool', nullable: false, default: 'false' }, + { name: 'created_at', type: 'timestamptz', nullable: false, default: 'now()' }, + { name: 'updated_at', type: 'timestamptz', nullable: false, default: 'now()' }, + ], + rationale: 'Stores product details, pricing, and stock levels', + source: TableSource.TEMPLATE, + }, + { + tableName: 'orders', + fields: [ + { + name: 'id', + type: 'uuid', + nullable: false, + isPrimary: true, + default: 'gen_random_uuid()', + }, + { name: 'order_number', type: 'text', nullable: false, unique: true }, + { + name: 'customer_id', + type: 'uuid', + nullable: false, + isForeign: true, + references: 'profiles(id)', + }, + { name: 'status', type: 'text', nullable: false, default: "'pending'" }, + { name: 'subtotal', type: 'numeric', nullable: false }, + { name: 'tax', type: 'numeric', nullable: false, default: '0' }, + { name: 'shipping', type: 'numeric', nullable: false, default: '0' }, + { name: 'total', type: 'numeric', nullable: false }, + { name: 'notes', type: 'text', nullable: true }, + { name: 'created_at', type: 'timestamptz', nullable: false, default: 'now()' }, + { name: 'updated_at', type: 'timestamptz', nullable: false, default: 'now()' }, + ], + rationale: 'Manages customer orders, payment totals, and statuses', + source: TableSource.TEMPLATE, + }, + { + tableName: 'cart_items', + fields: [ + { + name: 'id', + type: 'uuid', + nullable: false, + isPrimary: true, + default: 'gen_random_uuid()', + }, + { + name: 'user_id', + type: 'uuid', + nullable: false, + isForeign: true, + references: 'profiles(id)', + }, + { + name: 'product_id', + type: 'uuid', + nullable: false, + isForeign: true, + references: 'products(id)', + }, + { name: 'quantity', type: 'int4', nullable: false, default: '1' }, + { name: 'added_at', type: 'timestamptz', nullable: false, default: 'now()' }, + ], + rationale: 'Tracks products added to customer shopping carts', + source: TableSource.TEMPLATE, + }, + ], + Blog: [ + { + tableName: 'articles', + fields: [ + { + name: 'id', + type: 'uuid', + nullable: false, + isPrimary: true, + default: 'gen_random_uuid()', + }, + { name: 'title', type: 'text', nullable: false }, + { name: 'slug', type: 'text', nullable: false, unique: true }, + { name: 'content', type: 'text', nullable: true }, + { name: 'excerpt', type: 'text', nullable: true }, + { name: 'cover_image', type: 'text', nullable: true }, + { + name: 'author_id', + type: 'uuid', + nullable: false, + isForeign: true, + references: 'profiles(id)', + }, + { name: 'status', type: 'text', nullable: false, default: "'draft'" }, + { name: 'published_at', type: 'timestamptz', nullable: true }, + { name: 'created_at', type: 'timestamptz', nullable: false, default: 'now()' }, + { name: 'updated_at', type: 'timestamptz', nullable: false, default: 'now()' }, + ], + rationale: 'Stores blog posts with author, status, and publish dates', + source: TableSource.TEMPLATE, + }, + { + tableName: 'categories', + fields: [ + { + name: 'id', + type: 'uuid', + nullable: false, + isPrimary: true, + default: 'gen_random_uuid()', + }, + { name: 'name', type: 'text', nullable: false }, + { name: 'slug', type: 'text', nullable: false, unique: true }, + { name: 'description', type: 'text', nullable: true }, + { name: 'color', type: 'text', nullable: true }, + { name: 'created_at', type: 'timestamptz', nullable: false, default: 'now()' }, + ], + rationale: 'Organizes articles into categories or tags for filtering', + source: TableSource.TEMPLATE, + }, + { + tableName: 'comments', + fields: [ + { + name: 'id', + type: 'uuid', + nullable: false, + isPrimary: true, + default: 'gen_random_uuid()', + }, + { + name: 'article_id', + type: 'uuid', + nullable: false, + isForeign: true, + references: 'articles(id)', + }, + { name: 'author_name', type: 'text', nullable: false }, + { name: 'author_email', type: 'text', nullable: false }, + { name: 'content', type: 'text', nullable: false }, + { name: 'approved', type: 'bool', nullable: false, default: 'false' }, + { name: 'created_at', type: 'timestamptz', nullable: false, default: 'now()' }, + ], + rationale: 'Stores reader comments and approval status for moderation', + source: TableSource.TEMPLATE, + }, + ], + 'To-Do List': [ + { + tableName: 'tasks', + fields: [ + { + name: 'id', + type: 'uuid', + nullable: false, + isPrimary: true, + default: 'gen_random_uuid()', + }, + { name: 'title', type: 'text', nullable: false }, + { name: 'description', type: 'text', nullable: true }, + { name: 'completed', type: 'bool', nullable: false, default: 'false' }, + { name: 'priority', type: 'text', nullable: true, default: "'medium'" }, + { name: 'due_date', type: 'date', nullable: true }, + { + name: 'user_id', + type: 'uuid', + nullable: false, + isForeign: true, + references: 'auth.users(id)', + }, + { name: 'list_id', type: 'uuid', nullable: true, isForeign: true, references: 'lists(id)' }, + { name: 'created_at', type: 'timestamptz', nullable: false, default: 'now()' }, + { name: 'updated_at', type: 'timestamptz', nullable: false, default: 'now()' }, + ], + rationale: 'Stores user tasks with priorities, due dates, and status', + source: TableSource.TEMPLATE, + }, + { + tableName: 'lists', + fields: [ + { + name: 'id', + type: 'uuid', + nullable: false, + isPrimary: true, + default: 'gen_random_uuid()', + }, + { name: 'name', type: 'text', nullable: false }, + { name: 'description', type: 'text', nullable: true }, + { name: 'color', type: 'text', nullable: true }, + { name: 'icon', type: 'text', nullable: true }, + { + name: 'user_id', + type: 'uuid', + nullable: false, + isForeign: true, + references: 'auth.users(id)', + }, + { name: 'created_at', type: 'timestamptz', nullable: false, default: 'now()' }, + { name: 'updated_at', type: 'timestamptz', nullable: false, default: 'now()' }, + ], + rationale: 'Groups tasks into named lists for better organization', + source: TableSource.TEMPLATE, + }, + { + tableName: 'subtasks', + fields: [ + { + name: 'id', + type: 'uuid', + nullable: false, + isPrimary: true, + default: 'gen_random_uuid()', + }, + { + name: 'task_id', + type: 'uuid', + nullable: false, + isForeign: true, + references: 'tasks(id)', + }, + { name: 'title', type: 'text', nullable: false }, + { name: 'completed', type: 'bool', nullable: false, default: 'false' }, + { name: 'position', type: 'int4', nullable: false, default: '0' }, + { name: 'created_at', type: 'timestamptz', nullable: false, default: 'now()' }, + ], + rationale: 'Tracks smaller subtasks linked to a main task', + source: TableSource.TEMPLATE, + }, + ], + Analytics: [ + { + tableName: 'events', + fields: [ + { + name: 'id', + type: 'uuid', + nullable: false, + isPrimary: true, + default: 'gen_random_uuid()', + }, + { + name: 'user_id', + type: 'uuid', + nullable: true, + isForeign: true, + references: 'profiles(id)', + }, + { name: 'session_id', type: 'text', nullable: true }, + { name: 'event_type', type: 'text', nullable: false }, + { name: 'properties', type: 'jsonb', nullable: true }, + { name: 'user_agent', type: 'text', nullable: true }, + { name: 'ip_address', type: 'text', nullable: true }, + { name: 'created_at', type: 'timestamptz', nullable: false, default: 'now()' }, + ], + rationale: 'Captures user events and properties for analytics tracking', + source: TableSource.TEMPLATE, + }, + { + tableName: 'page_views', + fields: [ + { + name: 'id', + type: 'uuid', + nullable: false, + isPrimary: true, + default: 'gen_random_uuid()', + }, + { + name: 'user_id', + type: 'uuid', + nullable: true, + isForeign: true, + references: 'profiles(id)', + }, + { name: 'session_id', type: 'text', nullable: true }, + { name: 'path', type: 'text', nullable: false }, + { name: 'referrer', type: 'text', nullable: true }, + { name: 'duration', type: 'int4', nullable: true }, + { name: 'created_at', type: 'timestamptz', nullable: false, default: 'now()' }, + ], + rationale: 'Tracks user page views, referrers, and session info', + source: TableSource.TEMPLATE, + }, + { + tableName: 'metrics', + fields: [ + { + name: 'id', + type: 'uuid', + nullable: false, + isPrimary: true, + default: 'gen_random_uuid()', + }, + { name: 'metric_name', type: 'text', nullable: false }, + { name: 'value', type: 'numeric', nullable: false }, + { name: 'tags', type: 'jsonb', nullable: true }, + { name: 'timestamp', type: 'timestamptz', nullable: false, default: 'now()' }, + { name: 'aggregation_period', type: 'text', nullable: true }, + { name: 'created_at', type: 'timestamptz', nullable: false, default: 'now()' }, + ], + rationale: 'Stores aggregated metrics and KPIs with timestamps', + source: TableSource.TEMPLATE, + }, + ], +} diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableQuickstart/types.ts b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableQuickstart/types.ts new file mode 100644 index 0000000000000..aeceaa3acdb36 --- /dev/null +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableQuickstart/types.ts @@ -0,0 +1,82 @@ +export type PostgresType = + | 'text' + | 'varchar' + | 'uuid' + | 'int2' + | 'int4' + | 'int8' + | 'float4' + | 'float8' + | 'numeric' + | 'bool' + | 'json' + | 'jsonb' + | 'date' + | 'time' + | 'timestamp' + | 'timestamptz' + | 'timetz' + | 'bytea' + +export type TableField = { + name: string + type: PostgresType + nullable?: boolean + unique?: boolean + default?: string // Must be string for table editor compatibility + description?: string + isPrimary?: boolean + isForeign?: boolean + references?: string +} + +export type TableRelationship = { + from: string + to: string + type: 'one-to-one' | 'one-to-many' | 'many-to-many' | 'many-to-one' +} + +export enum TableSource { + AI = 'ai', + TEMPLATE = 'template', +} + +export enum QuickstartVariant { + CONTROL = 'control', + AI = 'ai', + TEMPLATES = 'templates', +} + +export enum ViewMode { + INITIAL = 'initial', + AI_INPUT = 'ai-input', + AI_RESULTS = 'ai-results', + CATEGORY_SELECTED = 'category-selected', +} + +export type TableSuggestion = { + tableName: string + fields: TableField[] + rationale?: string + source: TableSource + relationships?: TableRelationship[] +} + +export type AIGeneratedSchema = { + tables: Array<{ + name: string + description: string + columns: Array<{ + name: string + type: string + isPrimary?: boolean + isForeign?: boolean + references?: string + isNullable?: boolean + defaultValue?: string + isUnique?: boolean + }> + relationships?: TableRelationship[] + }> + summary: string +} diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableQuickstart/utils.ts b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableQuickstart/utils.ts new file mode 100644 index 0000000000000..727d5b7ac5c48 --- /dev/null +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableQuickstart/utils.ts @@ -0,0 +1,47 @@ +import type { TableSuggestion, TableField } from './types' +import type { ColumnField } from '../../SidePanelEditor.types' +import type { TableField as EditorTableField } from '../TableEditor.types' + +export const DEFAULT_SCHEMA = 'public' as const + +export function isPrimaryKeyField(field: TableField): boolean { + return field.isPrimary === true || field.name === 'id' +} + +export function isIdentityField(field: TableField): boolean { + return field.name === 'id' && field.type.toLowerCase().includes('int') && !field.default +} + +export function convertTableSuggestionToTableField( + table: TableSuggestion +): Partial { + const columns: ColumnField[] = table.fields.map((field, index) => { + const isPrimaryKey = isPrimaryKeyField(field) + const isIdentity = isIdentityField(field) + const defaultValue = field.default ? String(field.default) : null + + return { + id: `column-${index}`, + name: field.name, + format: field.type, + defaultValue, + isNullable: field.nullable !== false, + isUnique: field.unique ?? false, + isIdentity, + isPrimaryKey, + comment: field.description || '', + isNewColumn: true, + table: table.tableName, + schema: DEFAULT_SCHEMA, + check: null, + isArray: false, + isEncrypted: false, + } + }) + + return { + name: table.tableName, + comment: table.rationale || '', + columns, + } +} diff --git a/apps/studio/components/layouts/ProjectLayout/OrganizationSettingsLayout.tsx b/apps/studio/components/layouts/ProjectLayout/OrganizationSettingsLayout.tsx index 836ad3584ac9c..b9f974dc65446 100644 --- a/apps/studio/components/layouts/ProjectLayout/OrganizationSettingsLayout.tsx +++ b/apps/studio/components/layouts/ProjectLayout/OrganizationSettingsLayout.tsx @@ -16,7 +16,12 @@ function OrganizationSettingsLayout({ children }: PropsWithChildren) { const { organizationShowSsoSettings: showSsoSettings, organizationShowSecuritySettings: showSecuritySettings, - } = useIsFeatureEnabled(['organization:show_sso_settings', 'organization:show_security_settings']) + organizationShowLegalDocuments: showLegalDocuments, + } = useIsFeatureEnabled([ + 'organization:show_sso_settings', + 'organization:show_security_settings', + 'organization:show_legal_documents', + ]) const navMenuItems = [ { @@ -48,10 +53,14 @@ function OrganizationSettingsLayout({ children }: PropsWithChildren) { label: 'Audit Logs', href: `/org/${slug}/audit`, }, - { - label: 'Legal Documents', - href: `/org/${slug}/documents`, - }, + ...(showLegalDocuments + ? [ + { + label: 'Legal Documents', + href: `/org/${slug}/documents`, + }, + ] + : []), ] return ( diff --git a/apps/studio/components/layouts/SignInLayout/SignInLayout.tsx b/apps/studio/components/layouts/SignInLayout/SignInLayout.tsx index 0ef51bb710a7e..0785b981c5c45 100644 --- a/apps/studio/components/layouts/SignInLayout/SignInLayout.tsx +++ b/apps/studio/components/layouts/SignInLayout/SignInLayout.tsx @@ -30,8 +30,15 @@ const SignInLayout = ({ const { resolvedTheme } = useTheme() const ongoingIncident = useFlag('ongoingIncident') - const { dashboardAuthShowTestimonial: showTestimonial, brandingLargeLogo: largeLogo } = - useIsFeatureEnabled(['dashboard_auth:show_testimonial', 'branding:large_logo']) + const { + dashboardAuthShowTestimonial: showTestimonial, + brandingLargeLogo: largeLogo, + dashboardAuthShowTos: showTos, + } = useIsFeatureEnabled([ + 'dashboard_auth:show_testimonial', + 'branding:large_logo', + 'dashboard_auth:show_tos', + ]) // This useEffect redirects the user to MFA if they're already halfway signed in useEffect(() => { @@ -126,7 +133,7 @@ const SignInLayout = ({ {children} - {showDisclaimer && ( + {showDisclaimer && showTos && (

By continuing, you agree to Supabase's{' '} diff --git a/apps/studio/components/ui/OrganizationProjectSelector.tsx b/apps/studio/components/ui/OrganizationProjectSelector.tsx index 678f11bfdc428..70a9a26e2a2f9 100644 --- a/apps/studio/components/ui/OrganizationProjectSelector.tsx +++ b/apps/studio/components/ui/OrganizationProjectSelector.tsx @@ -41,6 +41,7 @@ interface OrganizationProjectSelectorSelectorProps { renderActions?: (setOpen: (value: boolean) => void) => ReactNode onSelect?: (project: OrgProject) => void onInitialLoad?: (projects: OrgProject[]) => void + fetchOnMount?: boolean } export const OrganizationProjectSelector = ({ @@ -56,6 +57,7 @@ export const OrganizationProjectSelector = ({ renderActions, onSelect, onInitialLoad, + fetchOnMount = false, }: OrganizationProjectSelectorSelectorProps) => { const { data: organization } = useSelectedOrganizationQuery() const slug = _slug ?? organization?.slug @@ -86,7 +88,7 @@ export const OrganizationProjectSelector = ({ fetchNextPage, } = useOrgProjectsInfiniteQuery( { slug, search: search.length === 0 ? search : debouncedSearch }, - { enabled: open, keepPreviousData: true } + { enabled: fetchOnMount || open, keepPreviousData: true } ) const projects = useMemo(() => data?.pages.flatMap((page) => page.projects), [data?.pages]) || [] diff --git a/apps/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx b/apps/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx index 60c1ce45da7a3..bb8f56feff9fa 100644 --- a/apps/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx +++ b/apps/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx @@ -11,7 +11,8 @@ import { getWarningContent } from './ResourceExhaustionWarningBanner.utils' export const ResourceExhaustionWarningBanner = () => { const { ref } = useParams() const router = useRouter() - const { data: resourceWarnings } = useResourceWarningsQuery() + const { data: resourceWarnings } = useResourceWarningsQuery({ ref: ref }) + // [Joshen Cleanup] JFYI this client side filtering can be cleaned up once BE changes are live which will only return the warnings based on the provided ref const projectResourceWarnings = (resourceWarnings ?? [])?.find( (warning) => warning.project === ref ) diff --git a/apps/studio/data/integrations/github-branch-check-query.ts b/apps/studio/data/integrations/github-branch-check-query.ts index 05f21499c6744..b7f98fbe41602 100644 --- a/apps/studio/data/integrations/github-branch-check-query.ts +++ b/apps/studio/data/integrations/github-branch-check-query.ts @@ -14,12 +14,12 @@ export async function checkGithubBranchValidity( signal?: AbortSignal ) { const { data, error } = await get( - '/platform/integrations/github/repositories/{repositoryId}/branches/{branchName}', + '/platform/integrations/github/repositories/{repository_id}/branches/{branch_name}', { params: { path: { - repositoryId, - branchName, + repository_id: repositoryId, + branch_name: branchName, }, }, signal, diff --git a/apps/studio/data/integrations/github-branches-query.ts b/apps/studio/data/integrations/github-branches-query.ts index 342238466c54e..361039e1397e8 100644 --- a/apps/studio/data/integrations/github-branches-query.ts +++ b/apps/studio/data/integrations/github-branches-query.ts @@ -13,8 +13,8 @@ export async function getGitHubBranches( ) { if (!connectionId) throw new Error('connectionId is required') - const { data, error } = await get(`/platform/integrations/github/branches/{connectionId}`, { - params: { path: { connectionId } }, + const { data, error } = await get(`/platform/integrations/github/branches/{connection_id}`, { + params: { path: { connection_id: connectionId } }, signal, }) diff --git a/apps/studio/data/invoices/invoice-payment-link-mutation.ts b/apps/studio/data/invoices/invoice-payment-link-mutation.ts index 35fcdbdd93947..d4ea25ca3e5a1 100644 --- a/apps/studio/data/invoices/invoice-payment-link-mutation.ts +++ b/apps/studio/data/invoices/invoice-payment-link-mutation.ts @@ -18,12 +18,12 @@ export async function updateInvoicePaymentLink({ if (!invoiceId) throw new Error('Invoice ID is required') const { data, error } = await get( - '/platform/organizations/{slug}/billing/invoices/{invoiceId}/payment-link', + '/platform/organizations/{slug}/billing/invoices/{invoice_id}/payment-link', { params: { path: { slug, - invoiceId, + invoice_id: invoiceId, }, }, } diff --git a/apps/studio/data/invoices/invoice-query.ts b/apps/studio/data/invoices/invoice-query.ts index 770957f5bedc7..d3e09dcf0f087 100644 --- a/apps/studio/data/invoices/invoice-query.ts +++ b/apps/studio/data/invoices/invoice-query.ts @@ -12,10 +12,13 @@ export async function getInvoice({ invoiceId, slug }: InvoiceVariables, signal?: if (!invoiceId) throw new Error('Invoice ID is required') if (!slug) throw new Error('Slug is required') - const { data, error } = await get(`/platform/organizations/{slug}/billing/invoices/{invoiceId}`, { - params: { path: { invoiceId, slug } }, - signal, - }) + const { data, error } = await get( + `/platform/organizations/{slug}/billing/invoices/{invoice_id}`, + { + params: { path: { invoice_id: invoiceId, slug } }, + signal, + } + ) if (error) handleError(error) return data diff --git a/apps/studio/data/usage/keys.ts b/apps/studio/data/usage/keys.ts index 066dca1bc6fc6..bddb545c550e8 100644 --- a/apps/studio/data/usage/keys.ts +++ b/apps/studio/data/usage/keys.ts @@ -2,5 +2,6 @@ export const usageKeys = { usage: (projectRef: string | undefined) => ['projects', projectRef, 'usage'] as const, orgUsage: (orgSlug: string | undefined, projectRef?: string, start?: string, end?: string) => ['organizations', orgSlug, 'usage', projectRef, start, end] as const, - resourceWarnings: () => ['projects', 'resource-warnings'] as const, + resourceWarnings: (slug?: string, projectRef?: string) => + ['projects', 'resource-warnings', { slug, projectRef }] as const, } diff --git a/apps/studio/data/usage/resource-warnings-query.ts b/apps/studio/data/usage/resource-warnings-query.ts index 9248c2dc9d5a0..cf69871c80b0f 100644 --- a/apps/studio/data/usage/resource-warnings-query.ts +++ b/apps/studio/data/usage/resource-warnings-query.ts @@ -6,8 +6,24 @@ import { get, handleError } from 'data/fetchers' import type { ResponseError } from 'types' import { usageKeys } from './keys' -export async function getResourceWarnings(signal?: AbortSignal) { - const { data, error } = await get(`/platform/projects-resource-warnings`, { signal }) +export type ResourceWarningsVariables = { + ref?: string + slug?: string +} + +export async function getResourceWarnings( + variables?: ResourceWarningsVariables, + signal?: AbortSignal +) { + const { data, error } = await get(`/platform/projects-resource-warnings`, { + params: { + query: { + ref: variables?.ref, + slug: variables?.slug, + }, + }, + signal, + }) if (error) handleError(error) return data @@ -17,15 +33,19 @@ export type ResourceWarning = components['schemas']['ProjectResourceWarningsResp export type ResourceWarningsData = Awaited> export type ResourceWarningsError = ResponseError -export const useResourceWarningsQuery = ({ - enabled = true, - ...options -}: UseQueryOptions = {}) => +export const useResourceWarningsQuery = ( + variables: ResourceWarningsVariables, + { + enabled = true, + ...options + }: UseQueryOptions = {} +) => useQuery( - usageKeys.resourceWarnings(), - ({ signal }) => getResourceWarnings(signal), + usageKeys.resourceWarnings(variables.slug, variables.ref), + ({ signal }) => getResourceWarnings(variables, signal), { - enabled: IS_PLATFORM && enabled, + enabled: + IS_PLATFORM && enabled && (variables.ref !== undefined || variables.slug !== undefined), staleTime: 1000 * 60 * 60, // default 60 minutes ...options, } diff --git a/apps/studio/pages/org/[slug]/documents.tsx b/apps/studio/pages/org/[slug]/documents.tsx index 829b83ba8d7dd..1d2fb1e44d519 100644 --- a/apps/studio/pages/org/[slug]/documents.tsx +++ b/apps/studio/pages/org/[slug]/documents.tsx @@ -1,11 +1,21 @@ +import { useParams } from 'common' import { Documents } from 'components/interfaces/Organization' -import AppLayout from 'components/layouts/AppLayout/AppLayout' import DefaultLayout from 'components/layouts/DefaultLayout' import OrganizationLayout from 'components/layouts/OrganizationLayout' import OrganizationSettingsLayout from 'components/layouts/ProjectLayout/OrganizationSettingsLayout' +import { UnknownInterface } from 'components/ui/UnknownInterface' +import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import type { NextPageWithLayout } from 'types' const OrgDocuments: NextPageWithLayout = () => { + const { slug } = useParams() + + const showLegalDocuments = useIsFeatureEnabled('organization:show_legal_documents') + + if (!showLegalDocuments) { + return + } + return } diff --git a/packages/api-types/types/api.d.ts b/packages/api-types/types/api.d.ts index 6bfe7cfbaa493..e43f935e651da 100644 --- a/packages/api-types/types/api.d.ts +++ b/packages/api-types/types/api.d.ts @@ -1729,6 +1729,23 @@ export interface paths { patch?: never trace?: never } + '/v1/projects/available-regions': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** [Beta] Gets the list of available regions that can be used for a new project */ + get: operations['v1-get-available-regions'] + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/v1/snippets': { parameters: { query?: never @@ -1951,6 +1968,9 @@ export interface components { external_zoom_email_optional: boolean | null external_zoom_enabled: boolean | null external_zoom_secret: string | null + hook_after_user_created_enabled: boolean | null + hook_after_user_created_secrets: string | null + hook_after_user_created_uri: string | null hook_before_user_created_enabled: boolean | null hook_before_user_created_secrets: string | null hook_before_user_created_uri: string | null @@ -2139,6 +2159,8 @@ export interface components { */ latest_check_run_id?: number name: string + /** Format: uri */ + notify_url?: string parent_project_ref: string persistent: boolean /** Format: int32 */ @@ -2232,6 +2254,11 @@ export interface components { | '48xlarge_high_memory' git_branch?: string is_default?: boolean + /** + * Format: uri + * @description HTTP endpoint to receive branch status updates. + */ + notify_url?: string persistent?: boolean /** * @description Postgres engine version. If not provided, the latest version will be used. @@ -2900,10 +2927,11 @@ export interface components { redirect_uri?: string refresh_token?: string /** + * Format: uri * @description Resource indicator for MCP (Model Context Protocol) clients - * @enum {string} */ - resource?: 'https://api.supabase.green/mcp' | 'https://mcp.supabase.green/mcp' + resource?: string + scope?: string } OAuthTokenResponse: { access_token: string @@ -2932,8 +2960,6 @@ export interface components { }[] /** @enum {string} */ source_subscription_plan: 'free' | 'pro' | 'team' | 'enterprise' - target_organization_eligible: boolean | null - target_organization_has_free_project_slots: boolean | null /** @enum {string|null} */ target_subscription_plan: 'free' | 'pro' | 'team' | 'enterprise' | null valid: boolean @@ -3028,6 +3054,46 @@ export interface components { override_active_until: string override_enabled: boolean } + RegionsInfo: { + all: { + smartGroup: { + /** @enum {string} */ + code: 'americas' | 'emea' | 'apac' + name: string + /** @enum {string} */ + type: 'smartGroup' + }[] + specific: { + code: string + name: string + /** @enum {string} */ + provider: 'AWS' | 'FLY' | 'AWS_K8S' | 'AWS_NIMBUS' + /** @enum {string} */ + status?: 'capacity' | 'other' + /** @enum {string} */ + type: 'specific' + }[] + } + recommendations: { + smartGroup: { + /** @enum {string} */ + code: 'americas' | 'emea' | 'apac' + name: string + /** @enum {string} */ + type: 'smartGroup' + } + specific: { + code: string + name: string + /** @enum {string} */ + provider: 'AWS' | 'FLY' | 'AWS_K8S' | 'AWS_NIMBUS' + /** @enum {string} */ + status?: 'capacity' | 'other' + /** @enum {string} */ + type: 'specific' + }[] + } + } RemoveNetworkBanRequest: { identifier?: string /** @description List of IP addresses to unban. */ @@ -3332,6 +3398,9 @@ export interface components { external_zoom_email_optional?: boolean | null external_zoom_enabled?: boolean | null external_zoom_secret?: string | null + hook_after_user_created_enabled?: boolean | null + hook_after_user_created_secrets?: string | null + hook_after_user_created_uri?: string | null hook_before_user_created_enabled?: boolean | null hook_before_user_created_secrets?: string | null hook_before_user_created_uri?: string | null @@ -3469,6 +3538,11 @@ export interface components { UpdateBranchBody: { branch_name?: string git_branch?: string + /** + * Format: uri + * @description HTTP endpoint to receive branch status updates. + */ + notify_url?: string persistent?: boolean request_review?: boolean /** @@ -3751,10 +3825,17 @@ export interface components { */ plan?: 'free' | 'pro' /** - * @description Region you want your server to reside in + * @deprecated + * @description Postgres engine version. If not provided, the latest version will be used. * @enum {string} */ - region: + postgres_engine?: '15' | '17' | '17-oriole' + /** + * @deprecated + * @description Region you want your server to reside in. Use region_selection instead. + * @enum {string} + */ + region?: | 'us-east-1' | 'us-east-2' | 'us-west-1' @@ -3773,6 +3854,54 @@ export interface components { | 'ca-central-1' | 'ap-south-1' | 'sa-east-1' + /** + * @description Region selection. Only one of region or region_selection can be specified. + * @example { type: 'smartGroup', code: 'americas' } + */ + region_selection?: + | { + /** + * @description Specific region code. The codes supported are not a stable API, and should be retrieved from the /available-regions endpoint. + * @enum {string} + */ + code: + | 'us-east-1' + | 'us-east-2' + | 'us-west-1' + | 'us-west-2' + | 'ap-east-1' + | 'ap-southeast-1' + | 'ap-northeast-1' + | 'ap-northeast-2' + | 'ap-southeast-2' + | 'eu-west-1' + | 'eu-west-2' + | 'eu-west-3' + | 'eu-north-1' + | 'eu-central-1' + | 'eu-central-2' + | 'ca-central-1' + | 'ap-south-1' + | 'sa-east-1' + /** @enum {string} */ + type: 'specific' + } + | { + /** + * @description The Smart Region Group's code. The codes supported are not a stable API, and should be retrieved from the /available-regions endpoint. + * @example apac + * @enum {string} + */ + code: 'americas' | 'emea' | 'apac' + /** @enum {string} */ + type: 'smartGroup' + } + /** + * @deprecated + * @description Release channel. If not provided, GA will be used. + * @enum {string} + */ + release_channel?: 'internal' | 'alpha' | 'beta' | 'ga' | 'withdrawn' | 'preview' /** * Format: uri * @description Template URL used to create the project from the CLI. @@ -4322,7 +4451,7 @@ export interface operations { organization_slug?: string redirect_uri: string /** @description Resource indicator for MCP (Model Context Protocol) clients */ - resource?: 'https://api.supabase.green/mcp' | 'https://mcp.supabase.green/mcp' + resource?: string response_mode?: string response_type: 'code' | 'token' | 'id_token token' scope?: string @@ -10675,6 +10804,30 @@ export interface operations { } } } + 'v1-get-available-regions': { + parameters: { + query: { + /** @description Continent code to determine regional recommendations: NA (North America), SA (South America), EU (Europe), AF (Africa), AS (Asia), OC (Oceania), AN (Antarctica) */ + continent?: 'NA' | 'SA' | 'EU' | 'AF' | 'AS' | 'OC' | 'AN' + /** @description Slug of your organization */ + organization_slug: string + } + header?: never + path?: never + cookie?: never + } + requestBody?: never + responses: { + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['RegionsInfo'] + } + } + } + } 'v1-list-all-snippets': { parameters: { query?: { diff --git a/packages/api-types/types/platform.d.ts b/packages/api-types/types/platform.d.ts index 924e77e68b36c..b2a53293795be 100644 --- a/packages/api-types/types/platform.d.ts +++ b/packages/api-types/types/platform.d.ts @@ -552,7 +552,7 @@ export interface paths { patch?: never trace?: never } - '/platform/integrations/github/branches/{connectionId}': { + '/platform/integrations/github/branches/{connection_id}': { parameters: { query?: never header?: never @@ -569,7 +569,7 @@ export interface paths { patch?: never trace?: never } - '/platform/integrations/github/branches/{connectionId}/{branchName}': { + '/platform/integrations/github/branches/{connection_id}/{branch_name}': { parameters: { query?: never header?: never @@ -639,7 +639,7 @@ export interface paths { patch?: never trace?: never } - '/platform/integrations/github/repositories/{repositoryId}/branches': { + '/platform/integrations/github/repositories/{repository_id}/branches': { parameters: { query?: never header?: never @@ -656,7 +656,7 @@ export interface paths { patch?: never trace?: never } - '/platform/integrations/github/repositories/{repositoryId}/branches/{branchName}': { + '/platform/integrations/github/repositories/{repository_id}/branches/{branch_name}': { parameters: { query?: never header?: never @@ -801,7 +801,7 @@ export interface paths { patch?: never trace?: never } - '/platform/integrations/vercel/connections/project/{project_ref}': { + '/platform/integrations/vercel/connections/project/{ref}': { parameters: { query?: never header?: never @@ -1028,7 +1028,7 @@ export interface paths { patch?: never trace?: never } - '/platform/organizations/{slug}/billing/invoices/{invoiceId}': { + '/platform/organizations/{slug}/billing/invoices/{invoice_id}': { parameters: { query?: never header?: never @@ -1045,7 +1045,7 @@ export interface paths { patch?: never trace?: never } - '/platform/organizations/{slug}/billing/invoices/{invoiceId}/payment-link': { + '/platform/organizations/{slug}/billing/invoices/{invoice_id}/payment-link': { parameters: { query?: never header?: never @@ -1200,40 +1200,6 @@ export interface paths { patch?: never trace?: never } - '/platform/organizations/{slug}/daily-stats': { - parameters: { - query?: never - header?: never - path?: never - cookie?: never - } - /** Gets daily organization stats */ - get: operations['OrgDailyStatsController_getDailyStats'] - put?: never - post?: never - delete?: never - options?: never - head?: never - patch?: never - trace?: never - } - '/platform/organizations/{slug}/daily-stats/compute': { - parameters: { - query?: never - header?: never - path?: never - cookie?: never - } - /** Gets daily organization stats for compute */ - get: operations['OrgDailyStatsController_getDailyStatsCompute'] - put?: never - post?: never - delete?: never - options?: never - head?: never - patch?: never - trace?: never - } '/platform/organizations/{slug}/documents/dpa': { parameters: { query?: never @@ -2168,6 +2134,42 @@ export interface paths { patch?: never trace?: never } + '/platform/profile/scoped-access-tokens': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** Gets the user's scoped access tokens */ + get: operations['ScopedAccessTokensController_getAccessTokens'] + put?: never + /** Creates a new scoped access token */ + post: operations['ScopedAccessTokensController_createAccessToken'] + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } + '/platform/profile/scoped-access-tokens/{id}': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** Gets the scoped access token with the given ID */ + get: operations['ScopedAccessTokensController_getAccessToken'] + put?: never + post?: never + /** Deletes the scoped access token with the given ID */ + delete: operations['ScopedAccessTokensController_deleteAccessToken'] + options?: never + head?: never + patch?: never + trace?: never + } '/platform/projects': { parameters: { query?: never @@ -4730,6 +4732,9 @@ export interface components { url: string username?: string | null } + | { + dsn: string + } description?: string name: string /** @enum {string} */ @@ -5168,32 +5173,74 @@ export interface components { } CreateReplicationDestinationBody: { /** @description Destination configuration */ - config: { - big_query: { - /** - * @description BigQuery dataset id - * @example analytics - */ - dataset_id: string - /** - * @description Maximum number of concurrent write streams - * @example 8 - */ - max_concurrent_streams?: number - /** - * @description Maximum data staleness in minutes - * @example 5 - */ - max_staleness_mins?: number - /** - * @description BigQuery project id - * @example my-gcp-project - */ - project_id: string - /** @description BigQuery service account key */ - service_account_key: string - } - } + config: + | { + big_query: { + /** + * @description BigQuery dataset id + * @example analytics + */ + dataset_id: string + /** + * @description Maximum number of concurrent write streams + * @example 8 + */ + max_concurrent_streams?: number + /** + * @description Maximum data staleness in minutes + * @example 5 + */ + max_staleness_mins?: number + /** + * @description BigQuery project id + * @example my-gcp-project + */ + project_id: string + /** @description BigQuery service account key */ + service_account_key: string + } + } + | { + iceberg: { + supabase: { + /** + * @description Catalog token + * @example A jwt secret + */ + catalog_token: string + /** + * @description Namespace + * @example my-namespace + */ + namespace: string + /** + * @description Project ref + * @example abcdefghijklmnopqrst + */ + project_ref: string + /** + * @description S3 access key ID + * @example 53383b1d0cdb16a3afa63152656aa3cc + */ + s3_access_key_id: string + /** + * @description S3 region + * @example ap-southeast-1 + */ + s3_region: string + /** + * @description S3 secret access key + * @example 25a0c5e69d847088a3e6ffb901adf4d19bbf74a400dec2ee49f46401039b3258 + */ + s3_secret_access_key: string + /** + * @description Warehouse name + * @example my-warehouse + */ + warehouse_name: string + } + } + } /** * @description Destination name * @example bq-analytics @@ -5202,32 +5249,74 @@ export interface components { } CreateReplicationDestinationPipelineBody: { /** @description Destination configuration */ - destination_config: { - big_query: { - /** - * @description BigQuery dataset id - * @example analytics - */ - dataset_id: string - /** - * @description Maximum number of concurrent write streams - * @example 8 - */ - max_concurrent_streams?: number - /** - * @description Maximum data staleness in minutes - * @example 5 - */ - max_staleness_mins?: number - /** - * @description BigQuery project id - * @example my-gcp-project - */ - project_id: string - /** @description BigQuery service account key */ - service_account_key: string - } - } + destination_config: + | { + big_query: { + /** + * @description BigQuery dataset id + * @example analytics + */ + dataset_id: string + /** + * @description Maximum number of concurrent write streams + * @example 8 + */ + max_concurrent_streams?: number + /** + * @description Maximum data staleness in minutes + * @example 5 + */ + max_staleness_mins?: number + /** + * @description BigQuery project id + * @example my-gcp-project + */ + project_id: string + /** @description BigQuery service account key */ + service_account_key: string + } + } + | { + iceberg: { + supabase: { + /** + * @description Catalog token + * @example A jwt secret + */ + catalog_token: string + /** + * @description Namespace + * @example my-namespace + */ + namespace: string + /** + * @description Project ref + * @example abcdefghijklmnopqrst + */ + project_ref: string + /** + * @description S3 access key ID + * @example 53383b1d0cdb16a3afa63152656aa3cc + */ + s3_access_key_id: string + /** + * @description S3 region + * @example ap-southeast-1 + */ + s3_region: string + /** + * @description S3 secret access key + * @example 25a0c5e69d847088a3e6ffb901adf4d19bbf74a400dec2ee49f46401039b3258 + */ + s3_secret_access_key: string + /** + * @description Warehouse name + * @example my-warehouse + */ + warehouse_name: string + } + } + } /** * @description Destination name * @example bq-analytics @@ -5317,6 +5406,89 @@ export interface components { name: string owner: string } + CreateScopedAccessTokenBody: { + /** Format: date-time */ + expires_at?: string + name: string + organization_slugs?: string[] + permissions: ( + | 'organizations_read' + | 'organizations_write' + | 'projects_read' + | 'available_regions_read' + | 'snippets_read' + | 'organization_admin_read' + | 'organization_admin_write' + | 'members_read' + | 'members_write' + | 'project_admin_read' + | 'project_admin_write' + | 'advisors_read' + | 'api_gateway_keys_read' + | 'api_gateway_keys_write' + | 'auth_config_read' + | 'auth_config_write' + | 'auth_signing_keys_read' + | 'auth_signing_keys_write' + | 'backups_read' + | 'backups_write' + | 'branching_development_read' + | 'branching_development_write' + | 'branching_production_read' + | 'branching_production_write' + | 'custom_domain_read' + | 'custom_domain_write' + | 'data_api_config_read' + | 'data_api_config_write' + | 'database_read' + | 'database_write' + | 'database_config_read' + | 'database_config_write' + | 'database_network_bans_read' + | 'database_network_bans_write' + | 'database_network_restrictions_read' + | 'database_network_restrictions_write' + | 'database_migrations_read' + | 'database_migrations_write' + | 'database_pooling_config_read' + | 'database_pooling_config_write' + | 'database_readonly_config_read' + | 'database_readonly_config_write' + | 'database_ssl_config_read' + | 'database_ssl_config_write' + | 'database_webhooks_config_read' + | 'database_webhooks_config_write' + | 'edge_functions_read' + | 'edge_functions_write' + | 'edge_functions_secrets_read' + | 'edge_functions_secrets_write' + | 'infra_add-ons_read' + | 'infra_add-ons_write' + | 'infra_read_replicas_read' + | 'infra_read_replicas_write' + | 'project_snippets_read' + | 'project_snippets_write' + | 'storage_read' + | 'storage_write' + | 'storage_config_read' + | 'storage_config_write' + | 'telemetry_logs_read' + | 'telemetry_usage_read' + )[] + project_refs?: string[] + } + CreateScopedAccessTokenResponse: { + created_at: string + expires_at: string | null + id: string + last_used_at: string | null + name: string + organization_slugs?: string[] + permissions: string[] + project_refs?: string[] + token: string + token_alias: string + } CreateSourceResponse: { /** @description Source ID */ id: number @@ -6095,6 +6267,27 @@ export interface components { } path: string } + GetScopedAccessTokenResponse: { + created_at: string + expires_at: string | null + id: string + last_used_at: string | null + name: string + organization_slugs?: string[] + permissions: string[] + project_refs?: string[] + token_alias: string + } + GetScopedAccessTokensResponse: { + tokens: { + created_at: string + expires_at: string | null + id: string + last_used_at: string | null + name: string + token_alias: string + }[] + } GetSignedUrlBody: { expiresIn: number options?: { @@ -6471,6 +6664,9 @@ export interface components { EXTERNAL_ZOOM_EMAIL_OPTIONAL: boolean EXTERNAL_ZOOM_ENABLED: boolean EXTERNAL_ZOOM_SECRET: string + HOOK_AFTER_USER_CREATED_ENABLED: boolean + HOOK_AFTER_USER_CREATED_SECRETS: string + HOOK_AFTER_USER_CREATED_URI: string HOOK_BEFORE_USER_CREATED_ENABLED: boolean HOOK_BEFORE_USER_CREATED_SECRETS: string HOOK_BEFORE_USER_CREATED_URI: string @@ -6541,6 +6737,9 @@ export interface components { MFA_WEB_AUTHN_VERIFY_ENABLED: boolean NIMBUS_OAUTH_CLIENT_ID: string | null NIMBUS_OAUTH_CLIENT_SECRET: string | null + OAUTH_SERVER_ALLOW_DYNAMIC_REGISTRATION: boolean + OAUTH_SERVER_AUTHORIZATION_PATH: string | null + OAUTH_SERVER_ENABLED: boolean PASSWORD_HIBP_ENABLED: boolean PASSWORD_MIN_LENGTH: number PASSWORD_REQUIRED_CHARACTERS: string @@ -6683,6 +6882,9 @@ export interface components { url: string username?: string | null } + | { + dsn: string + } description?: string id: number metadata: { @@ -7318,6 +7520,7 @@ export interface components { } PauseStatusResponse: { can_restore: boolean + last_paused_on: string | null latest_downloadable_backup_id: number | null max_days_till_restore_disabled: number remaining_days_till_restore_disabled: number | null @@ -7671,11 +7874,8 @@ export interface components { limit: number name: string }[] - source_project_eligible: boolean /** @enum {string} */ source_subscription_plan: 'free' | 'pro' | 'team' | 'enterprise' - target_organization_eligible: boolean | null - target_organization_has_free_project_slots: boolean | null /** @enum {string|null} */ target_subscription_plan: 'free' | 'pro' | 'team' | 'enterprise' | null valid: boolean @@ -8091,32 +8291,74 @@ export interface components { } ReplicationDestinationResponse: { /** @description Destination configuration */ - config: { - big_query: { - /** - * @description BigQuery dataset id - * @example analytics - */ - dataset_id: string - /** - * @description Maximum number of concurrent write streams - * @example 8 - */ - max_concurrent_streams?: number - /** - * @description Maximum data staleness in minutes - * @example 5 - */ - max_staleness_mins?: number - /** - * @description BigQuery project id - * @example my-gcp-project - */ - project_id: string - /** @description BigQuery service account key */ - service_account_key: string - } - } + config: + | { + big_query: { + /** + * @description BigQuery dataset id + * @example analytics + */ + dataset_id: string + /** + * @description Maximum number of concurrent write streams + * @example 8 + */ + max_concurrent_streams?: number + /** + * @description Maximum data staleness in minutes + * @example 5 + */ + max_staleness_mins?: number + /** + * @description BigQuery project id + * @example my-gcp-project + */ + project_id: string + /** @description BigQuery service account key */ + service_account_key: string + } + } + | { + iceberg: { + supabase: { + /** + * @description Catalog token + * @example A jwt secret + */ + catalog_token: string + /** + * @description Namespace + * @example my-namespace + */ + namespace: string + /** + * @description Project ref + * @example abcdefghijklmnopqrst + */ + project_ref: string + /** + * @description S3 access key ID + * @example 53383b1d0cdb16a3afa63152656aa3cc + */ + s3_access_key_id: string + /** + * @description S3 region + * @example ap-southeast-1 + */ + s3_region: string + /** + * @description S3 secret access key + * @example 25a0c5e69d847088a3e6ffb901adf4d19bbf74a400dec2ee49f46401039b3258 + */ + s3_secret_access_key: string + /** + * @description Warehouse name + * @example my-warehouse + */ + warehouse_name: string + } + } + } /** * @description Destination id * @example 2001 @@ -8137,32 +8379,74 @@ export interface components { /** @description List of destinations */ destinations: { /** @description Destination configuration */ - config: { - big_query: { - /** - * @description BigQuery dataset id - * @example analytics - */ - dataset_id: string - /** - * @description Maximum number of concurrent write streams - * @example 8 - */ - max_concurrent_streams?: number - /** - * @description Maximum data staleness in minutes - * @example 5 - */ - max_staleness_mins?: number - /** - * @description BigQuery project id - * @example my-gcp-project - */ - project_id: string - /** @description BigQuery service account key */ - service_account_key: string - } - } + config: + | { + big_query: { + /** + * @description BigQuery dataset id + * @example analytics + */ + dataset_id: string + /** + * @description Maximum number of concurrent write streams + * @example 8 + */ + max_concurrent_streams?: number + /** + * @description Maximum data staleness in minutes + * @example 5 + */ + max_staleness_mins?: number + /** + * @description BigQuery project id + * @example my-gcp-project + */ + project_id: string + /** @description BigQuery service account key */ + service_account_key: string + } + } + | { + iceberg: { + supabase: { + /** + * @description Catalog token + * @example A jwt secret + */ + catalog_token: string + /** + * @description Namespace + * @example my-namespace + */ + namespace: string + /** + * @description Project ref + * @example abcdefghijklmnopqrst + */ + project_ref: string + /** + * @description S3 access key ID + * @example 53383b1d0cdb16a3afa63152656aa3cc + */ + s3_access_key_id: string + /** + * @description S3 region + * @example ap-southeast-1 + */ + s3_region: string + /** + * @description S3 secret access key + * @example 25a0c5e69d847088a3e6ffb901adf4d19bbf74a400dec2ee49f46401039b3258 + */ + s3_secret_access_key: string + /** + * @description Warehouse name + * @example my-warehouse + */ + warehouse_name: string + } + } + } /** * @description Destination id * @example 2001 @@ -9192,10 +9476,22 @@ export interface components { url: string username?: string | null } + | { + dsn: string + } description?: string name?: string /** @enum {string} */ - type: 'postgres' | 'bigquery' | 'webhook' | 'datadog' | 'elastic' | 'loki' + type: + | 'postgres' + | 'bigquery' + | 'clickhouse' + | 'webhook' + | 'datadog' + | 'elastic' + | 'loki' + | 'sentry' + | 's3' } UpdateCollectionBody: { name: string @@ -9357,6 +9653,9 @@ export interface components { EXTERNAL_ZOOM_EMAIL_OPTIONAL?: boolean | null EXTERNAL_ZOOM_ENABLED?: boolean | null EXTERNAL_ZOOM_SECRET?: string | null + HOOK_AFTER_USER_CREATED_ENABLED?: boolean | null + HOOK_AFTER_USER_CREATED_SECRETS?: string | null + HOOK_AFTER_USER_CREATED_URI?: string | null HOOK_BEFORE_USER_CREATED_ENABLED?: boolean | null HOOK_BEFORE_USER_CREATED_SECRETS?: string | null HOOK_BEFORE_USER_CREATED_URI?: string | null @@ -9427,6 +9726,9 @@ export interface components { MFA_WEB_AUTHN_VERIFY_ENABLED?: boolean | null NIMBUS_OAUTH_CLIENT_ID?: string | null NIMBUS_OAUTH_CLIENT_SECRET?: string | null + OAUTH_SERVER_ALLOW_DYNAMIC_REGISTRATION?: boolean | null + OAUTH_SERVER_AUTHORIZATION_PATH?: string | null + OAUTH_SERVER_ENABLED?: boolean | null PASSWORD_HIBP_ENABLED?: boolean | null PASSWORD_MIN_LENGTH?: number | null /** @enum {string|null} */ @@ -9494,6 +9796,9 @@ export interface components { URI_ALLOW_LIST?: string | null } UpdateGoTrueConfigHooksBody: { + HOOK_AFTER_USER_CREATED_ENABLED?: boolean | null + HOOK_AFTER_USER_CREATED_SECRETS?: string | null + HOOK_AFTER_USER_CREATED_URI?: string | null HOOK_BEFORE_USER_CREATED_ENABLED?: boolean | null HOOK_BEFORE_USER_CREATED_SECRETS?: string | null HOOK_BEFORE_USER_CREATED_URI?: string | null @@ -9689,32 +9994,74 @@ export interface components { } UpdateReplicationDestinationBody: { /** @description Destination configuration */ - config: { - big_query: { - /** - * @description BigQuery dataset id - * @example analytics - */ - dataset_id: string - /** - * @description Maximum number of concurrent write streams - * @example 8 - */ - max_concurrent_streams?: number - /** - * @description Maximum data staleness in minutes - * @example 5 - */ - max_staleness_mins?: number - /** - * @description BigQuery project id - * @example my-gcp-project - */ - project_id: string - /** @description BigQuery service account key */ - service_account_key: string - } - } + config: + | { + big_query: { + /** + * @description BigQuery dataset id + * @example analytics + */ + dataset_id: string + /** + * @description Maximum number of concurrent write streams + * @example 8 + */ + max_concurrent_streams?: number + /** + * @description Maximum data staleness in minutes + * @example 5 + */ + max_staleness_mins?: number + /** + * @description BigQuery project id + * @example my-gcp-project + */ + project_id: string + /** @description BigQuery service account key */ + service_account_key: string + } + } + | { + iceberg: { + supabase: { + /** + * @description Catalog token + * @example A jwt secret + */ + catalog_token: string + /** + * @description Namespace + * @example my-namespace + */ + namespace: string + /** + * @description Project ref + * @example abcdefghijklmnopqrst + */ + project_ref: string + /** + * @description S3 access key ID + * @example 53383b1d0cdb16a3afa63152656aa3cc + */ + s3_access_key_id: string + /** + * @description S3 region + * @example ap-southeast-1 + */ + s3_region: string + /** + * @description S3 secret access key + * @example 25a0c5e69d847088a3e6ffb901adf4d19bbf74a400dec2ee49f46401039b3258 + */ + s3_secret_access_key: string + /** + * @description Warehouse name + * @example my-warehouse + */ + warehouse_name: string + } + } + } /** * @description Destination name * @example bq-analytics @@ -9723,32 +10070,74 @@ export interface components { } UpdateReplicationDestinationPipelineBody: { /** @description Destination configuration */ - destination_config: { - big_query: { - /** - * @description BigQuery dataset id - * @example analytics - */ - dataset_id: string - /** - * @description Maximum number of concurrent write streams - * @example 8 - */ - max_concurrent_streams?: number - /** - * @description Maximum data staleness in minutes - * @example 5 - */ - max_staleness_mins?: number - /** - * @description BigQuery project id - * @example my-gcp-project - */ - project_id: string - /** @description BigQuery service account key */ - service_account_key: string - } - } + destination_config: + | { + big_query: { + /** + * @description BigQuery dataset id + * @example analytics + */ + dataset_id: string + /** + * @description Maximum number of concurrent write streams + * @example 8 + */ + max_concurrent_streams?: number + /** + * @description Maximum data staleness in minutes + * @example 5 + */ + max_staleness_mins?: number + /** + * @description BigQuery project id + * @example my-gcp-project + */ + project_id: string + /** @description BigQuery service account key */ + service_account_key: string + } + } + | { + iceberg: { + supabase: { + /** + * @description Catalog token + * @example A jwt secret + */ + catalog_token: string + /** + * @description Namespace + * @example my-namespace + */ + namespace: string + /** + * @description Project ref + * @example abcdefghijklmnopqrst + */ + project_ref: string + /** + * @description S3 access key ID + * @example 53383b1d0cdb16a3afa63152656aa3cc + */ + s3_access_key_id: string + /** + * @description S3 region + * @example ap-southeast-1 + */ + s3_region: string + /** + * @description S3 secret access key + * @example 25a0c5e69d847088a3e6ffb901adf4d19bbf74a400dec2ee49f46401039b3258 + */ + s3_secret_access_key: string + /** + * @description Warehouse name + * @example my-warehouse + */ + warehouse_name: string + } + } + } /** * @description Destination name * @example bq-analytics @@ -11775,7 +12164,7 @@ export interface operations { } header?: never path: { - connectionId: number + connection_id: number } cookie?: never } @@ -11803,8 +12192,8 @@ export interface operations { query?: never header?: never path: { - branchName: string - connectionId: number + branch_name: string + connection_id: number } cookie?: never } @@ -11972,7 +12361,7 @@ export interface operations { query?: never header?: never path: { - repositoryId: number + repository_id: number } cookie?: never } @@ -12000,8 +12389,8 @@ export interface operations { query?: never header?: never path: { - branchName: string - repositoryId: number + branch_name: string + repository_id: number } cookie?: never } @@ -12327,7 +12716,7 @@ export interface operations { query?: never header?: never path: { - project_ref: string + ref: string } cookie?: never } @@ -13036,7 +13425,7 @@ export interface operations { query?: never header?: never path: { - invoiceId: string + invoice_id: string /** @description Organization slug */ slug: string } @@ -13087,7 +13476,7 @@ export interface operations { query?: never header?: never path: { - invoiceId: string + invoice_id: string /** @description Organization slug */ slug: string } @@ -13645,157 +14034,6 @@ export interface operations { } } } - OrgDailyStatsController_getDailyStats: { - parameters: { - query: { - endDate: string - interval?: string - metric: - | 'EGRESS' - | 'CACHED_EGRESS' - | 'DATABASE_SIZE' - | 'STORAGE_SIZE' - | 'MONTHLY_ACTIVE_USERS' - | 'MONTHLY_ACTIVE_SSO_USERS' - | 'FUNCTION_INVOCATIONS' - | 'FUNCTION_CPU_MILLISECONDS' - | 'STORAGE_IMAGES_TRANSFORMED' - | 'REALTIME_MESSAGE_COUNT' - | 'REALTIME_PEAK_CONNECTIONS' - | 'DISK_SIZE_GB_HOURS_GP3' - | 'DISK_SIZE_GB_HOURS_IO2' - | 'AUTH_MFA_PHONE' - | 'AUTH_MFA_WEB_AUTHN' - | 'LOG_DRAIN_EVENTS' - | 'MONTHLY_ACTIVE_THIRD_PARTY_USERS' - | 'DISK_THROUGHPUT_GP3' - | 'DISK_IOPS_GP3' - | 'DISK_IOPS_IO2' - | 'COMPUTE_HOURS_BRANCH' - | 'COMPUTE_HOURS_XS' - | 'COMPUTE_HOURS_SM' - | 'COMPUTE_HOURS_MD' - | 'COMPUTE_HOURS_L' - | 'COMPUTE_HOURS_XL' - | 'COMPUTE_HOURS_2XL' - | 'COMPUTE_HOURS_4XL' - | 'COMPUTE_HOURS_8XL' - | 'COMPUTE_HOURS_12XL' - | 'COMPUTE_HOURS_16XL' - | 'COMPUTE_HOURS_24XL' - | 'COMPUTE_HOURS_24XL_OPTIMIZED_CPU' - | 'COMPUTE_HOURS_24XL_OPTIMIZED_MEMORY' - | 'COMPUTE_HOURS_24XL_HIGH_MEMORY' - | 'COMPUTE_HOURS_48XL' - | 'COMPUTE_HOURS_48XL_OPTIMIZED_CPU' - | 'COMPUTE_HOURS_48XL_OPTIMIZED_MEMORY' - | 'COMPUTE_HOURS_48XL_HIGH_MEMORY' - | 'CUSTOM_DOMAIN' - | 'PITR_7' - | 'PITR_14' - | 'PITR_28' - | 'IPV4' - | 'LOG_DRAIN' - projectRef?: string - startDate: string - } - header?: never - path: { - /** @description Organization slug */ - slug: string - } - cookie?: never - } - requestBody?: never - responses: { - 200: { - headers: { - [name: string]: unknown - } - content?: never - } - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown - } - content?: never - } - /** @description Forbidden action */ - 403: { - headers: { - [name: string]: unknown - } - content?: never - } - /** @description Rate limit exceeded */ - 429: { - headers: { - [name: string]: unknown - } - content?: never - } - /** @description Failed to get daily organization stats */ - 500: { - headers: { - [name: string]: unknown - } - content?: never - } - } - } - OrgDailyStatsController_getDailyStatsCompute: { - parameters: { - query: { - endDate: string - projectRef?: string - startDate: string - } - header?: never - path: { - /** @description Organization slug */ - slug: string - } - cookie?: never - } - requestBody?: never - responses: { - 200: { - headers: { - [name: string]: unknown - } - content?: never - } - /** @description Unauthorized */ - 401: { - headers: { - [name: string]: unknown - } - content?: never - } - /** @description Forbidden action */ - 403: { - headers: { - [name: string]: unknown - } - content?: never - } - /** @description Rate limit exceeded */ - 429: { - headers: { - [name: string]: unknown - } - content?: never - } - /** @description Failed to get daily organization stats for compute */ - 500: { - headers: { - [name: string]: unknown - } - content?: never - } - } - } OrgDocumentsController_createDpaDocument: { parameters: { query?: never @@ -18480,6 +18718,116 @@ export interface operations { } } } + ScopedAccessTokensController_getAccessTokens: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody?: never + responses: { + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['GetScopedAccessTokensResponse'] + } + } + /** @description Failed to get user's scoped access tokens */ + 500: { + headers: { + [name: string]: unknown + } + content?: never + } + } + } + ScopedAccessTokensController_createAccessToken: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody: { + content: { + 'application/json': components['schemas']['CreateScopedAccessTokenBody'] + } + } + responses: { + 201: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['CreateScopedAccessTokenResponse'] + } + } + /** @description Failed to create scoped access token */ + 500: { + headers: { + [name: string]: unknown + } + content?: never + } + } + } + ScopedAccessTokensController_getAccessToken: { + parameters: { + query?: never + header?: never + path: { + id: string + } + cookie?: never + } + requestBody?: never + responses: { + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['GetScopedAccessTokenResponse'] + } + } + /** @description Failed to get scoped access token */ + 500: { + headers: { + [name: string]: unknown + } + content?: never + } + } + } + ScopedAccessTokensController_deleteAccessToken: { + parameters: { + query?: never + header?: never + path: { + id: string + } + cookie?: never + } + requestBody?: never + responses: { + 200: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Failed to delete scoped access token */ + 500: { + headers: { + [name: string]: unknown + } + content?: never + } + } + } ProjectsController_getProjects: { parameters: { query?: never @@ -18524,7 +18872,12 @@ export interface operations { } ProjectsResourceWarningsController_getProjectsResourceWarnings: { parameters: { - query?: never + query?: { + /** @description Project ref */ + ref?: string + /** @description Organization slug */ + slug?: string + } header?: never path?: never cookie?: never diff --git a/packages/common/constants/local-storage.ts b/packages/common/constants/local-storage.ts index 0ac015cb730d5..c6a1308420816 100644 --- a/packages/common/constants/local-storage.ts +++ b/packages/common/constants/local-storage.ts @@ -90,6 +90,9 @@ export const LOCAL_STORAGE_KEYS = { * WWW */ BLOG_VIEW: 'supabase-blog-view', + + // Used to track if user has dismissed table editor quickstart prompt + TABLE_QUICKSTART_DISMISSED: 'table-quickstart-dismissed', } as const export type LocalStorageKey = (typeof LOCAL_STORAGE_KEYS)[keyof typeof LOCAL_STORAGE_KEYS] @@ -111,6 +114,7 @@ const LOCAL_STORAGE_KEYS_ALLOWLIST = [ LOCAL_STORAGE_KEYS.AI_ASSISTANT_MCP_OPT_IN, LOCAL_STORAGE_KEYS.UI_PREVIEW_BRANCHING_2_0, LOCAL_STORAGE_KEYS.LINTER_SHOW_FOOTER, + LOCAL_STORAGE_KEYS.TABLE_QUICKSTART_DISMISSED, ] export function clearLocalStorage() { diff --git a/packages/common/enabled-features/enabled-features.json b/packages/common/enabled-features/enabled-features.json index e5204a6f50f8e..6420164ff2311 100644 --- a/packages/common/enabled-features/enabled-features.json +++ b/packages/common/enabled-features/enabled-features.json @@ -34,6 +34,7 @@ "dashboard_auth:sign_in_with_sso": true, "dashboard_auth:sign_in_with_email": true, "dashboard_auth:show_testimonial": true, + "dashboard_auth:show_tos": true, "database:replication": true, "database:roles": true, @@ -78,6 +79,7 @@ "organization:show_sso_settings": true, "organization:show_security_settings": true, + "organization:show_legal_documents": true, "profile:show_email": true, "profile:show_information": true, diff --git a/packages/common/enabled-features/enabled-features.schema.json b/packages/common/enabled-features/enabled-features.schema.json index 8fe3d88dbcc7d..c742c5a2848e3 100644 --- a/packages/common/enabled-features/enabled-features.schema.json +++ b/packages/common/enabled-features/enabled-features.schema.json @@ -120,6 +120,10 @@ "type": "boolean", "description": "Enable the testimonial on the sign in/up page" }, + "dashboard_auth:show_tos": { + "type": "boolean", + "description": "Enable the terms of service link on the sign in/up page" + }, "database:replication": { "type": "boolean", @@ -272,6 +276,10 @@ "type": "boolean", "description": "Show the security settings tab in the organization settings page" }, + "organization:show_legal_documents": { + "type": "boolean", + "description": "Show the legal documents tab in the organization settings page" + }, "profile:show_email": { "type": "boolean", @@ -430,6 +438,7 @@ "dashboard_auth:sign_in_with_github", "dashboard_auth:sign_in_with_sso", "dashboard_auth:sign_in_with_email", + "dashboard_auth:show_tos", "database:replication", "database:roles", "database:restore_to_new_project", @@ -467,6 +476,8 @@ "logs:metadata", "logs:show_metadata_ip_template", "organization:show_sso_settings", + "organization:show_security_settings", + "organization:show_legal_documents", "project_creation:show_advanced_config", "project_homepage:show_instance_size", "project_homepage:show_examples",