diff --git a/apps/docs/content/guides/getting-started/quickstarts/sveltekit.mdx b/apps/docs/content/guides/getting-started/quickstarts/sveltekit.mdx index f22ecc0563408..5e475977a6bd5 100644 --- a/apps/docs/content/guides/getting-started/quickstarts/sveltekit.mdx +++ b/apps/docs/content/guides/getting-started/quickstarts/sveltekit.mdx @@ -67,8 +67,8 @@ hideToc: true <$CodeTabs> ```text name=.env - PUBLIC_VITE_SUPABASE_URL= - PUBLIC_VITE_SUPABASE_PUBLISHABLE_KEY= + PUBLIC_SUPABASE_URL= + PUBLIC_SUPABASE_PUBLISHABLE_KEY= ``` @@ -89,17 +89,17 @@ hideToc: true <$CodeTabs> ```js name=src/lib/supabaseClient.js - import { createClient } from '@supabase/supabase-js'; - import { PUBLIC_VITE_SUPABASE_URL, PUBLIC_VITE_SUPABASE_PUBLISHABLE_KEY } from '$env/static/public'; + import { createClient } from '@supabase/supabase-js'; + import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_PUBLISHABLE_KEY } from '$env/static/public'; - export const supabase = createClient(PUBLIC_VITE_SUPABASE_URL, PUBLIC_VITE_SUPABASE_PUBLISHABLE_KEY) + export const supabase = createClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_PUBLISHABLE_KEY) ``` ```ts name=src/lib/supabaseClient.ts - import { createClient } from '@supabase/supabase-js'; - import { PUBLIC_VITE_SUPABASE_URL, PUBLIC_VITE_SUPABASE_PUBLISHABLE_KEY } from '$env/static/public'; + import { createClient } from '@supabase/supabase-js'; + import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_PUBLISHABLE_KEY } from '$env/static/public'; - export const supabase = createClient(PUBLIC_VITE_SUPABASE_URL, PUBLIC_VITE_SUPABASE_PUBLISHABLE_KEY) + export const supabase = createClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_PUBLISHABLE_KEY) ``` diff --git a/apps/studio/components/interfaces/Account/Preferences/AccountConnections.tsx b/apps/studio/components/interfaces/Account/Preferences/AccountConnections.tsx index a8907fa8b35fc..5d7d40521254d 100644 --- a/apps/studio/components/interfaces/Account/Preferences/AccountConnections.tsx +++ b/apps/studio/components/interfaces/Account/Preferences/AccountConnections.tsx @@ -1,10 +1,24 @@ +import { ChevronDown, RefreshCw, Unlink } from 'lucide-react' import Image from 'next/image' +import { useState } from 'react' +import { toast } from 'sonner' import Panel from 'components/ui/Panel' +import { useGitHubAuthorizationDeleteMutation } from 'data/integrations/github-authorization-delete-mutation' import { useGitHubAuthorizationQuery } from 'data/integrations/github-authorization-query' import { BASE_PATH } from 'lib/constants' import { openInstallGitHubIntegrationWindow } from 'lib/github' -import { Badge, Button, cn } from 'ui' +import { + Badge, + Button, + cn, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from 'ui' +import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' import ShimmeringLoader from 'ui-patterns/ShimmeringLoader' export const AccountConnections = () => { @@ -16,12 +30,30 @@ export const AccountConnections = () => { error, } = useGitHubAuthorizationQuery() + const [isRemoveModalOpen, setIsRemoveModalOpen] = useState(false) + const isConnected = gitHubAuthorization !== null + const { mutate: removeAuthorization, isLoading: isRemoving } = + useGitHubAuthorizationDeleteMutation({ + onSuccess: () => { + toast.success('GitHub authorization removed successfully') + setIsRemoveModalOpen(false) + }, + }) + const handleConnect = () => { openInstallGitHubIntegrationWindow('authorize') } + const handleReauthenticate = () => { + openInstallGitHubIntegrationWindow('authorize') + } + + const handleRemove = () => { + removeAuthorization() + } + return ( {

-
+
{isConnected ? ( - Connected + <> + Connected + + + + + + { + event.preventDefault() + handleReauthenticate() + }} + > + +

Re-authenticate

+
+ setIsRemoveModalOpen(true)} + > + +

Remove connection

+
+
+
+ ) : (
)} + setIsRemoveModalOpen(false)} + onConfirm={handleRemove} + loading={isRemoving} + > +

+ Removing this authorization will disconnect your GitHub account from Supabase. You can + reconnect at any time. +

+
) } diff --git a/apps/studio/components/interfaces/LogDrains/LogDrainDestinationSheetForm.tsx b/apps/studio/components/interfaces/LogDrains/LogDrainDestinationSheetForm.tsx index e6bcaafa92e78..b9c930d20ed92 100644 --- a/apps/studio/components/interfaces/LogDrains/LogDrainDestinationSheetForm.tsx +++ b/apps/studio/components/interfaces/LogDrains/LogDrainDestinationSheetForm.tsx @@ -612,6 +612,7 @@ export function LogDrainDestinationSheetForm({ See full pricing breakdown{' '} here diff --git a/apps/studio/components/interfaces/Settings/Integrations/GithubIntegration/GitHubIntegrationConnectionForm.tsx b/apps/studio/components/interfaces/Settings/Integrations/GithubIntegration/GitHubIntegrationConnectionForm.tsx index 252894e8bbec0..28b64e069304f 100644 --- a/apps/studio/components/interfaces/Settings/Integrations/GithubIntegration/GitHubIntegrationConnectionForm.tsx +++ b/apps/studio/components/interfaces/Settings/Integrations/GithubIntegration/GitHubIntegrationConnectionForm.tsx @@ -1,6 +1,6 @@ import { zodResolver } from '@hookform/resolvers/zod' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { ChevronDown, Loader2, PlusIcon } from 'lucide-react' +import { ChevronDown, Info, Loader2, PlusIcon, RefreshCw } from 'lucide-react' import { useEffect, useMemo, useState } from 'react' import { useForm } from 'react-hook-form' import { toast } from 'sonner' @@ -33,6 +33,7 @@ import { CommandInput_Shadcn_, CommandItem_Shadcn_, CommandList_Shadcn_, + CommandSeparator_Shadcn_, Form_Shadcn_, FormControl_Shadcn_, FormField_Shadcn_, @@ -141,7 +142,7 @@ const GitHubIntegrationConnectionForm = ({ const githubRepos = useMemo( () => - githubReposData?.map((repo) => ({ + githubReposData?.repositories?.map((repo) => ({ id: repo.id.toString(), name: repo.name, installation_id: repo.installation_id, @@ -150,6 +151,8 @@ const GitHubIntegrationConnectionForm = ({ [githubReposData] ) + const hasPartialResponseDueToSSO = githubReposData?.partial_response_due_to_sso ?? false + const prodBranch = existingBranches?.find((branch) => branch.is_default) // Combined GitHub Settings Form @@ -474,30 +477,32 @@ const GitHubIntegrationConnectionForm = ({ No repositories found. - - {githubRepos.map((repo, i) => ( - { - field.onChange(repo.id) - setRepoComboboxOpen(false) - githubSettingsForm.setValue( - 'branchName', - repo.default_branch || 'main' - ) - }} - > -
- {GITHUB_ICON} -
- - {repo.name} - -
- ))} -
+ {githubRepos.length > 0 ? ( + + {githubRepos.map((repo, i) => ( + { + field.onChange(repo.id) + setRepoComboboxOpen(false) + githubSettingsForm.setValue( + 'branchName', + repo.default_branch || 'main' + ) + }} + > +
+ {GITHUB_ICON} +
+ + {repo.name} + +
+ ))} +
+ ) : null} + {hasPartialResponseDueToSSO && ( + <> + + + { + openInstallGitHubIntegrationWindow( + 'authorize', + refetchGitHubAuthorizationAndRepositories + ) + }} + > + +
+ Re-authorize GitHub with SSO to show all repositories +
+
+
+ + )}
diff --git a/apps/studio/components/interfaces/Settings/Logs/Logs.DatePickers.tsx b/apps/studio/components/interfaces/Settings/Logs/Logs.DatePickers.tsx index 4d5083d4c8e6e..702e01e77e678 100644 --- a/apps/studio/components/interfaces/Settings/Logs/Logs.DatePickers.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/Logs.DatePickers.tsx @@ -21,6 +21,7 @@ import { } from 'ui' import { LOGS_LARGE_DATE_RANGE_DAYS_THRESHOLD } from './Logs.constants' import type { DatetimeHelper } from './Logs.types' +import { useCheckEntitlements } from 'hooks/misc/useCheckEntitlements' export type DatePickerValue = { to: string @@ -237,13 +238,16 @@ export const LogsDatePicker = ({ Math.abs(dayjs(startDate).diff(dayjs(endDate), 'days')) > LOGS_LARGE_DATE_RANGE_DAYS_THRESHOLD - 1 - const { plan: orgPlan, isLoading: isOrgPlanLoading } = useCurrentOrgPlan() + const { getEntitlementNumericValue } = useCheckEntitlements('log.retention_days') + const entitledToAuditLogDays = getEntitlementNumericValue() + const showHelperBadge = (helper?: DatetimeHelper) => { if (!helper) return false if (!helper.availableIn?.length) return false + if (!entitledToAuditLogDays) return false - if (helper.availableIn.includes('free')) return false - if (helper.availableIn.includes(orgPlan?.id || 'free') && !isOrgPlanLoading) return false + const day = Math.abs(dayjs().diff(dayjs(helper.calcFrom()), 'day')) + if (day <= entitledToAuditLogDays) return false return true } diff --git a/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts b/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts index 279a148a57732..e21744f60f707 100644 --- a/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts +++ b/apps/studio/components/interfaces/Settings/Logs/Logs.utils.ts @@ -291,6 +291,19 @@ export const maybeShowUpgradePrompt = (from: string | null | undefined, planId?: ) } +/** + * Determine if we should show the user an upgrade prompt while browsing logs + * This method should replace maybeShowUpgradePrompt once we have migrated all usage to the Entitlements API. + */ +export const maybeShowUpgradePromptIfNotEntitled = ( + from: string | null | undefined, + entitledToDays: number | undefined +) => { + if (!entitledToDays) return false + const day = Math.abs(dayjs().diff(dayjs(from), 'day')) + return day > entitledToDays +} + export const genCountQuery = (table: LogsTableName, filters: Filters): string => { let where = genWhereStatement(table, filters) // pg_cron logs are a subset of postgres logs diff --git a/apps/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx b/apps/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx index 39dcd1c83d1c7..a35bf61a4f8b8 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx @@ -26,9 +26,10 @@ import { PREVIEWER_DATEPICKER_HELPERS, } from './Logs.constants' import type { Filters, LogSearchCallback, LogTemplate, QueryType } from './Logs.types' -import { maybeShowUpgradePrompt } from './Logs.utils' +import { maybeShowUpgradePromptIfNotEntitled } from './Logs.utils' import { PreviewFilterPanelWithUniversal } from './PreviewFilterPanelWithUniversal' import UpgradePrompt from './UpgradePrompt' +import { useCheckEntitlements } from 'hooks/misc/useCheckEntitlements' /** * Calculates the appropriate time range for bar click filtering based on the current time range duration. @@ -206,6 +207,9 @@ export const LogsPreviewer = ({ refresh() } + const { getEntitlementNumericValue } = useCheckEntitlements('log.retention_days') + const entitledToAuditLogDays = getEntitlementNumericValue() + const handleSearch: LogSearchCallback = async (event, { query, to, from }) => { if (event === 'search-input-change') { setSearch(query || '') @@ -213,8 +217,10 @@ export const LogsPreviewer = ({ } else if (event === 'event-chart-bar-click') { setTimeRange(from || '', to || '') } else if (event === 'datepicker-change') { - const shouldShowUpgradePrompt = maybeShowUpgradePrompt(from || '', organization?.plan?.id) - + const shouldShowUpgradePrompt = maybeShowUpgradePromptIfNotEntitled( + from || '', + entitledToAuditLogDays + ) if (shouldShowUpgradePrompt) { setShowUpgradePrompt(!showUpgradePrompt) } else { @@ -226,7 +232,10 @@ export const LogsPreviewer = ({ // Show the prompt on page load based on query params useEffect(() => { if (timestampStart) { - const shouldShowUpgradePrompt = maybeShowUpgradePrompt(timestampStart, organization?.plan?.id) + const shouldShowUpgradePrompt = maybeShowUpgradePromptIfNotEntitled( + timestampStart, + entitledToAuditLogDays + ) if (shouldShowUpgradePrompt) { setShowUpgradePrompt(!showUpgradePrompt) } diff --git a/apps/studio/components/interfaces/Settings/Logs/LogsQueryPanel.tsx b/apps/studio/components/interfaces/Settings/Logs/LogsQueryPanel.tsx index 6106cf020814d..bb44df24c7402 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogsQueryPanel.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogsQueryPanel.tsx @@ -206,7 +206,6 @@ const LogsQueryPanel = ({ hideFooter triggerElement={ + } + />
) } diff --git a/apps/studio/components/ui/DataTable/DataTableFilters/DataTableFilterTimerange.tsx b/apps/studio/components/ui/DataTable/DataTableFilters/DataTableFilterTimerange.tsx index 7656a5a1c9baf..5bd4bf95c2254 100644 --- a/apps/studio/components/ui/DataTable/DataTableFilters/DataTableFilterTimerange.tsx +++ b/apps/studio/components/ui/DataTable/DataTableFilters/DataTableFilterTimerange.tsx @@ -6,12 +6,12 @@ import { DatePickerValue, LogsDatePicker, } from 'components/interfaces/Settings/Logs/Logs.DatePickers' -import { maybeShowUpgradePrompt } from 'components/interfaces/Settings/Logs/Logs.utils' +import { maybeShowUpgradePromptIfNotEntitled } from 'components/interfaces/Settings/Logs/Logs.utils' import UpgradePrompt from 'components/interfaces/Settings/Logs/UpgradePrompt' -import { useCurrentOrgPlan } from 'hooks/misc/useCurrentOrgPlan' import type { DataTableTimerangeFilterField } from '../DataTable.types' import { isArrayOfDates } from '../DataTable.utils' import { useDataTable } from '../providers/DataTableProvider' +import { useCheckEntitlements } from 'hooks/misc/useCheckEntitlements' export function DataTableFilterTimerange({ value: _value, @@ -21,9 +21,11 @@ export function DataTableFilterTimerange({ const column = table.getColumn(value) const filterValue = columnFilters.find((i) => i.id === value)?.value - const { plan: orgPlan } = useCurrentOrgPlan() const [showUpgradePrompt, setShowUpgradePrompt] = useState(false) + const { getEntitlementNumericValue } = useCheckEntitlements('log.retention_days') + const entitledToAuditLogDays = getEntitlementNumericValue() + const date: DateRange | undefined = useMemo( () => filterValue instanceof Date @@ -36,7 +38,10 @@ export function DataTableFilterTimerange({ const handleDatePickerChange = (vals: DatePickerValue) => { // Check if the selected date range exceeds the plan limits - const shouldShowUpgradePrompt = maybeShowUpgradePrompt(vals.from, orgPlan?.id) + const shouldShowUpgradePrompt = maybeShowUpgradePromptIfNotEntitled( + vals.from, + entitledToAuditLogDays + ) if (shouldShowUpgradePrompt) { setShowUpgradePrompt(true) diff --git a/apps/studio/components/ui/DataTable/DataTableToolbar.tsx b/apps/studio/components/ui/DataTable/DataTableToolbar.tsx index 8f26410a532cd..b2e4bf7098a95 100644 --- a/apps/studio/components/ui/DataTable/DataTableToolbar.tsx +++ b/apps/studio/components/ui/DataTable/DataTableToolbar.tsx @@ -37,7 +37,7 @@ export function DataTableToolbar({ renderActions }: DataTableToolbarProps) { + } + /> ) diff --git a/apps/studio/components/ui/DataTable/RefreshButton.tsx b/apps/studio/components/ui/DataTable/RefreshButton.tsx index 0f664e62586b7..a0e82436b1d1c 100644 --- a/apps/studio/components/ui/DataTable/RefreshButton.tsx +++ b/apps/studio/components/ui/DataTable/RefreshButton.tsx @@ -10,7 +10,7 @@ export const RefreshButton = ({ isLoading, onRefresh }: RefreshButtonProps) => { return ( { + const { ref } = useParams() // [Joshen] Ensure JSON values are stringified for CSV and Markdown const formattedResults = results.map((row) => { const r = { ...row } @@ -106,9 +109,11 @@ export const DownloadResultsButton = ({ - downloadAsCSV()}> - -

Download CSV

+ + + +

Add a Log Drain

+
@@ -118,6 +123,10 @@ export const DownloadResultsButton = ({

Copy as JSON

+ downloadAsCSV()}> + +

Download CSV

+
) diff --git a/apps/studio/components/ui/Logs/LogsExplorerHeader.tsx b/apps/studio/components/ui/Logs/LogsExplorerHeader.tsx index ffc621a04b783..2af86431e4ef7 100644 --- a/apps/studio/components/ui/Logs/LogsExplorerHeader.tsx +++ b/apps/studio/components/ui/Logs/LogsExplorerHeader.tsx @@ -62,7 +62,7 @@ const LogsExplorerHeader = ({ subtitle }: LogsExplorerHeaderProps) => { onClick={() => setShowReference(true)} icon={} > - Field Reference + Field Reference } > diff --git a/apps/studio/data/integrations/github-authorization-create-mutation.ts b/apps/studio/data/integrations/github-authorization-create-mutation.ts index 3fd22101d1748..18b1efea59404 100644 --- a/apps/studio/data/integrations/github-authorization-create-mutation.ts +++ b/apps/studio/data/integrations/github-authorization-create-mutation.ts @@ -1,9 +1,10 @@ -import { useMutation } from '@tanstack/react-query' +import { useMutation, useQueryClient } from '@tanstack/react-query' import { toast } from 'sonner' import { LOCAL_STORAGE_KEYS } from 'common' import { handleError, post } from 'data/fetchers' import type { ResponseError, UseCustomMutationOptions } from 'types' +import { integrationKeys } from './keys' export type GitHubAuthorizationCreateVariables = { code: string @@ -44,6 +45,7 @@ export const useGitHubAuthorizationCreateMutation = ({ >, 'mutationFn' > = {}) => { + const queryClient = useQueryClient() return useMutation< GitHubAuthorizationCreateData, ResponseError, @@ -51,6 +53,14 @@ export const useGitHubAuthorizationCreateMutation = ({ >({ mutationFn: (vars) => createGitHubAuthorization(vars), async onSuccess(data, variables, context) { + await Promise.all([ + queryClient.invalidateQueries({ + queryKey: integrationKeys.githubAuthorization(), + }), + queryClient.invalidateQueries({ + queryKey: integrationKeys.githubRepositoriesList(), + }), + ]) await onSuccess?.(data, variables, context) }, async onError(data, variables, context) { diff --git a/apps/studio/data/integrations/github-authorization-delete-mutation.ts b/apps/studio/data/integrations/github-authorization-delete-mutation.ts new file mode 100644 index 0000000000000..355ec5fc4f3af --- /dev/null +++ b/apps/studio/data/integrations/github-authorization-delete-mutation.ts @@ -0,0 +1,48 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query' +import { toast } from 'sonner' + +import { del, handleError } from 'data/fetchers' +import type { ResponseError, UseCustomMutationOptions } from 'types' +import { integrationKeys } from './keys' + +export async function deleteGitHubAuthorization(signal?: AbortSignal) { + const { data, error } = await del('/platform/integrations/github/authorization', { signal }) + + if (error) handleError(error) + return data +} + +type GitHubAuthorizationDeleteData = Awaited> + +export const useGitHubAuthorizationDeleteMutation = ({ + onSuccess, + onError, + ...options +}: Omit< + UseCustomMutationOptions, + 'mutationFn' +> = {}) => { + const queryClient = useQueryClient() + return useMutation({ + mutationFn: () => deleteGitHubAuthorization(), + async onSuccess(data, variables, context) { + await Promise.all([ + queryClient.invalidateQueries({ + queryKey: integrationKeys.githubAuthorization(), + }), + queryClient.invalidateQueries({ + queryKey: integrationKeys.githubRepositoriesList(), + }), + ]) + await onSuccess?.(data, variables, context) + }, + async onError(data, variables, context) { + if (onError === undefined) { + toast.error(`Failed to remove GitHub authorization: ${data.message}`) + } else { + onError(data, variables, context) + } + }, + ...options, + }) +} diff --git a/apps/studio/data/integrations/github-repositories-query.ts b/apps/studio/data/integrations/github-repositories-query.ts index 9ac089e0a2b19..6d6739321ffe1 100644 --- a/apps/studio/data/integrations/github-repositories-query.ts +++ b/apps/studio/data/integrations/github-repositories-query.ts @@ -10,7 +10,7 @@ export async function getGitHubRepositories(signal?: AbortSignal) { }) if (error) handleError(error) - return data.repositories + return data } export type GitHubRepositoriesData = Awaited> diff --git a/apps/studio/hooks/misc/useUpgradePrompt.tsx b/apps/studio/hooks/misc/useUpgradePrompt.tsx index eadf613ee0b3e..a6126f9a3ef4e 100644 --- a/apps/studio/hooks/misc/useUpgradePrompt.tsx +++ b/apps/studio/hooks/misc/useUpgradePrompt.tsx @@ -1,12 +1,13 @@ -import { maybeShowUpgradePrompt } from 'components/interfaces/Settings/Logs/Logs.utils' +import { maybeShowUpgradePromptIfNotEntitled } from 'components/interfaces/Settings/Logs/Logs.utils' import { useEffect, useState } from 'react' -import { useSelectedOrganizationQuery } from './useSelectedOrganization' +import { useCheckEntitlements } from './useCheckEntitlements' export const useUpgradePrompt = (from: string) => { - const { data: organization } = useSelectedOrganizationQuery() const [showUpgradePrompt, setShowUpgradePrompt] = useState(false) + const { getEntitlementNumericValue } = useCheckEntitlements('log.retention_days') + const entitledToAuditLogDays = getEntitlementNumericValue() - const shouldShowUpgradePrompt = maybeShowUpgradePrompt(from, organization?.plan?.id) + const shouldShowUpgradePrompt = maybeShowUpgradePromptIfNotEntitled(from, entitledToAuditLogDays) useEffect(() => { if (shouldShowUpgradePrompt) { diff --git a/apps/studio/lib/github.ts b/apps/studio/lib/github.ts index b78986c52e31b..e961960cf0ea4 100644 --- a/apps/studio/lib/github.ts +++ b/apps/studio/lib/github.ts @@ -50,7 +50,7 @@ export function openInstallGitHubIntegrationWindow( } else { const state = makeRandomString(32) localStorage.setItem(LOCAL_STORAGE_KEYS.GITHUB_AUTHORIZATION_STATE, state) - windowUrl = `${GITHUB_INTEGRATION_AUTHORIZATION_URL}&state=${state}` + windowUrl = `${GITHUB_INTEGRATION_AUTHORIZATION_URL}&state=${state}&prompt=select_account` } const systemZoom = width / window.screen.availWidth diff --git a/apps/studio/pages/project/[ref]/logs/explorer/index.tsx b/apps/studio/pages/project/[ref]/logs/explorer/index.tsx index 8fe0c6e02e10b..37325967ef261 100644 --- a/apps/studio/pages/project/[ref]/logs/explorer/index.tsx +++ b/apps/studio/pages/project/[ref]/logs/explorer/index.tsx @@ -18,7 +18,7 @@ import { LogsWarning, } from 'components/interfaces/Settings/Logs/Logs.types' import { - maybeShowUpgradePrompt, + maybeShowUpgradePromptIfNotEntitled, useEditorHints, } from 'components/interfaces/Settings/Logs/Logs.utils' import LogsQueryPanel from 'components/interfaces/Settings/Logs/LogsQueryPanel' @@ -52,6 +52,7 @@ import { ResizablePanel, ResizablePanelGroup, } from 'ui' +import { useCheckEntitlements } from 'hooks/misc/useCheckEntitlements' const LOCAL_PLACEHOLDER_QUERY = 'select\n timestamp, event_message, metadata\n from edge_logs limit 5' @@ -66,7 +67,6 @@ export const LogsExplorerPage: NextPageWithLayout = () => { const { profile } = useProfile() const { ref, q, queryId } = useParams() const projectRef = ref as string - const { data: organization } = useSelectedOrganizationQuery() const { logsShowMetadataIpTemplate } = useIsFeatureEnabled(['logs:show_metadata_ip_template']) const allTemplates = useMemo(() => { @@ -93,6 +93,9 @@ export const LogsExplorerPage: NextPageWithLayout = () => { [] ) + const { getEntitlementNumericValue } = useCheckEntitlements('log.retention_days') + const entitledToAuditLogDays = getEntitlementNumericValue() + const { data: content } = useContentQuery({ projectRef: ref, type: 'log_sql', @@ -253,7 +256,10 @@ export const LogsExplorerPage: NextPageWithLayout = () => { } const handleDateChange = ({ to, from }: DatePickerToFrom) => { - const shouldShowUpgradePrompt = maybeShowUpgradePrompt(from, organization?.plan?.id) + const shouldShowUpgradePrompt = maybeShowUpgradePromptIfNotEntitled( + from, + entitledToAuditLogDays + ) if (shouldShowUpgradePrompt) { setShowUpgradePrompt(!showUpgradePrompt) @@ -288,12 +294,15 @@ export const LogsExplorerPage: NextPageWithLayout = () => { // Show the prompt on page load based on query params useEffect(() => { if (timestampStart) { - const shouldShowUpgradePrompt = maybeShowUpgradePrompt(timestampStart, organization?.plan?.id) + const shouldShowUpgradePrompt = maybeShowUpgradePromptIfNotEntitled( + timestampStart, + entitledToAuditLogDays + ) if (shouldShowUpgradePrompt) { setShowUpgradePrompt(!showUpgradePrompt) } } - }, [timestampStart, organization]) + }, [timestampStart, entitledToAuditLogDays]) return (
diff --git a/apps/studio/pages/project/[ref]/storage/files/buckets/[bucketId].tsx b/apps/studio/pages/project/[ref]/storage/files/buckets/[bucketId].tsx index 8a8b741620908..433b8e73e2312 100644 --- a/apps/studio/pages/project/[ref]/storage/files/buckets/[bucketId].tsx +++ b/apps/studio/pages/project/[ref]/storage/files/buckets/[bucketId].tsx @@ -35,7 +35,7 @@ const BucketPage: NextPageWithLayout = () => { } // If the bucket is not found or the bucket type is ANALYTICS or VECTOR, show an error message - if (!bucket || bucket.type !== 'STANDARD') { + if (!bucket || ('type' in bucket && bucket.type !== 'STANDARD')) { return (

Bucket "{bucketId}" cannot be found

diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 54884e5c1ec0a..4a7448920cf5d 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -32,6 +32,9 @@ services: HOSTNAME: "::" STUDIO_PG_META_URL: http://meta:8080 + POSTGRES_PORT: ${POSTGRES_PORT} + POSTGRES_HOST: ${POSTGRES_HOST} + POSTGRES_DB: ${POSTGRES_DB} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} PG_META_CRYPTO_KEY: ${PG_META_CRYPTO_KEY} diff --git a/docker/volumes/logs/vector.yml b/docker/volumes/logs/vector.yml index 3493e9b3f8e55..f97f8e1e94687 100644 --- a/docker/volumes/logs/vector.yml +++ b/docker/volumes/logs/vector.yml @@ -33,7 +33,7 @@ transforms: kong: '.appname == "supabase-kong"' auth: '.appname == "supabase-auth"' rest: '.appname == "supabase-rest"' - realtime: '.appname == "supabase-realtime"' + realtime: '.appname == "realtime-dev.supabase-realtime"' storage: '.appname == "supabase-storage"' functions: '.appname == "supabase-edge-functions"' db: '.appname == "supabase-db"' diff --git a/packages/api-types/types/platform.d.ts b/packages/api-types/types/platform.d.ts index 2a9186388b709..91a27a413eb9e 100644 --- a/packages/api-types/types/platform.d.ts +++ b/packages/api-types/types/platform.d.ts @@ -544,9 +544,16 @@ export interface paths { /** Get GitHub authorization */ get: operations['GitHubAuthorizationsController_getGitHubAuthorization'] put?: never - /** Create GitHub authorization */ + /** + * Upsert GitHub authorization + * @description Creates or updates a GitHub authorization for the current user + */ post: operations['GitHubAuthorizationsController_createGitHubAuthorization'] - delete?: never + /** + * Remove GitHub authorization + * @description Removes the GitHub authorization for the current user + */ + delete: operations['GitHubAuthorizationsController_removeGitHubAuthorization'] options?: never head?: never patch?: never @@ -6992,6 +6999,8 @@ export interface components { }[] } ListGitHubRepositoriesResponse: { + /** @description The authorized user may not have access to all GitHub repositories in case they haven't gone through the authorization process with SSO yet. This field will be `true` if this is the case. The calling user must reauthorize their GitHub account with SSO to see all repositories. */ + partial_response_due_to_sso: boolean repositories: { default_branch: string id: number @@ -12130,6 +12139,37 @@ export interface operations { } } } + GitHubAuthorizationsController_removeGitHubAuthorization: { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + requestBody?: never + responses: { + 200: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description There was no GitHub authorization attached to the user */ + 404: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Failed to remove GitHub authorization */ + 500: { + headers: { + [name: string]: unknown + } + content?: never + } + } + } GitHubBranchesController_listConnectionBranches: { parameters: { query?: { diff --git a/packages/ui/src/components/SidePanel/SidePanel.tsx b/packages/ui/src/components/SidePanel/SidePanel.tsx index 88f711bf33af8..2ef745d1f1571 100644 --- a/packages/ui/src/components/SidePanel/SidePanel.tsx +++ b/packages/ui/src/components/SidePanel/SidePanel.tsx @@ -106,11 +106,7 @@ const SidePanel = ({ return ( - {triggerElement && ( - - {triggerElement} - - )} + {triggerElement && {triggerElement}}