diff --git a/.gitignore b/.gitignore index a2566899919aa..6f13c135c9bb4 100644 --- a/.gitignore +++ b/.gitignore @@ -156,4 +156,7 @@ gcloud.json keys.json # Playwright MCP -.playwright-mcp/* \ No newline at end of file +.playwright-mcp/* + +# Application +**/.superset/** diff --git a/apps/docs/components/GuidesSidebar.tsx b/apps/docs/components/GuidesSidebar.tsx index ee4e8c49b4be2..0fc561ff94f43 100644 --- a/apps/docs/components/GuidesSidebar.tsx +++ b/apps/docs/components/GuidesSidebar.tsx @@ -19,16 +19,10 @@ interface TOCHeader { function AiTools({ className }: { className?: string }) { const [copied, setCopied] = useState(false) - let url = '' - - // Safe check for server side rendering. - try { - const urlParts = new URL(`${window.location}`) - url = urlParts.origin + urlParts.pathname - } catch (error) {} + const path = usePathname() async function copyMarkdown() { - const mdUrl = `${url}.md` + const mdUrl = `/docs/${path}.md` try { const res = await fetch(mdUrl) @@ -62,7 +56,7 @@ function AiTools({ className }: { className?: string }) { {copied ? 'Copied!' : 'Copy as Markdown'} void disabled?: boolean + hasAccess?: boolean } -export const BackupsList = ({ onSelectRestore, disabled }: BackupsListProps) => { +export const BackupsList = ({ onSelectRestore, disabled, hasAccess }: BackupsListProps) => { const { data: project } = useSelectedProjectQuery() - const { data: organization } = useSelectedOrganizationQuery() - - const isFreePlan = organization?.plan?.id === 'free' - const { data: cloneBackups } = useCloneBackupsQuery( { projectRef: project?.ref }, - { enabled: !isFreePlan } + { enabled: hasAccess } ) return ( diff --git a/apps/studio/components/interfaces/Database/Backups/RestoreToNewProject/CreateNewProjectDialog.tsx b/apps/studio/components/interfaces/Database/Backups/RestoreToNewProject/CreateNewProjectDialog.tsx index a57a29ef73829..b93a387c38293 100644 --- a/apps/studio/components/interfaces/Database/Backups/RestoreToNewProject/CreateNewProjectDialog.tsx +++ b/apps/studio/components/interfaces/Database/Backups/RestoreToNewProject/CreateNewProjectDialog.tsx @@ -7,7 +7,6 @@ import { z } from 'zod' import { PasswordStrengthBar } from 'components/ui/PasswordStrengthBar' import { useProjectCloneMutation } from 'data/projects/clone-mutation' import { useCloneBackupsQuery } from 'data/projects/clone-query' -import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import { passwordStrength, PasswordStrengthScore } from 'lib/password-strength' import { generateStrongPassword } from 'lib/project' @@ -37,6 +36,7 @@ interface CreateNewProjectDialogProps { onOpenChange: (value: boolean) => void onCloneSuccess: () => void additionalMonthlySpend: NewProjectPrice + hasAccess?: boolean } export const CreateNewProjectDialog = ({ @@ -46,10 +46,9 @@ export const CreateNewProjectDialog = ({ onOpenChange, onCloneSuccess, additionalMonthlySpend, + hasAccess, }: CreateNewProjectDialogProps) => { const { data: project } = useSelectedProjectQuery() - const { data: organization } = useSelectedOrganizationQuery() - const [passwordStrengthScore, setPasswordStrengthScore] = useState(0) const [passwordStrengthMessage, setPasswordStrengthMessage] = useState('') @@ -66,11 +65,9 @@ export const CreateNewProjectDialog = ({ }, }) - const isFreePlan = organization?.plan?.id === 'free' - const { data: cloneBackups } = useCloneBackupsQuery( { projectRef: project?.ref }, - { enabled: !isFreePlan } + { enabled: hasAccess } ) const hasPITREnabled = cloneBackups?.pitr_enabled diff --git a/apps/studio/components/interfaces/Database/RestoreToNewProject/RestoreToNewProject.tsx b/apps/studio/components/interfaces/Database/RestoreToNewProject/RestoreToNewProject.tsx index 8cf054582daf7..b73c6628a5961 100644 --- a/apps/studio/components/interfaces/Database/RestoreToNewProject/RestoreToNewProject.tsx +++ b/apps/studio/components/interfaces/Database/RestoreToNewProject/RestoreToNewProject.tsx @@ -18,6 +18,7 @@ import { UpgradeToPro } from 'components/ui/UpgradeToPro' import { useDiskAttributesQuery } from 'data/config/disk-attributes-query' import { useCloneBackupsQuery } from 'data/projects/clone-query' import { useCloneStatusQuery } from 'data/projects/clone-status-query' +import { useCheckEntitlements } from 'hooks/misc/useCheckEntitlements' import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' import { @@ -35,7 +36,8 @@ import { PreviousRestoreItem } from './PreviousRestoreItem' export const RestoreToNewProject = () => { const { data: project } = useSelectedProjectQuery() const { data: organization } = useSelectedOrganizationQuery() - const isFreePlan = organization?.plan?.id === 'free' + const { hasAccess: hasAccessToRestoreToNewProject, isLoading: isLoadingEntitlement } = + useCheckEntitlements('backup.restore_to_new_project') const isOrioleDb = useIsOrioleDb() const isAwsK8s = useIsAwsK8sCloudProvider() @@ -50,7 +52,10 @@ export const RestoreToNewProject = () => { error, isPending: cloneBackupsLoading, isError, - } = useCloneBackupsQuery({ projectRef: project?.ref }, { enabled: !isFreePlan }) + } = useCloneBackupsQuery( + { projectRef: project?.ref }, + { enabled: hasAccessToRestoreToNewProject } + ) const isActiveHealthy = project?.status === PROJECT_STATUS.ACTIVE_HEALTHY @@ -102,7 +107,11 @@ export const RestoreToNewProject = () => { const isRestoring = previousClones?.some((c) => c.status === 'IN_PROGRESS') const restoringClone = previousClones?.find((c) => c.status === 'IN_PROGRESS') - if (isFreePlan) { + if (isLoadingEntitlement) { + return + } + + if (!hasAccessToRestoreToNewProject) { return ( { selectedBackupId={selectedBackupId} recoveryTimeTarget={recoveryTimeTarget} additionalMonthlySpend={additionalMonthlySpend} + hasAccess={hasAccessToRestoreToNewProject} onOpenChange={setShowNewProjectDialog} onCloneSuccess={() => { refetchCloneStatus() @@ -307,6 +317,7 @@ export const RestoreToNewProject = () => { ) : ( { setSelectedBackupId(id) setShowConfirmationDialog(true) diff --git a/apps/studio/components/interfaces/Settings/Integrations/GithubIntegration/GitHubIntegrationConnectionForm.tsx b/apps/studio/components/interfaces/Settings/Integrations/GithubIntegration/GitHubIntegrationConnectionForm.tsx index 218c82d9ca521..1d9d98afe9d6c 100644 --- a/apps/studio/components/interfaces/Settings/Integrations/GithubIntegration/GitHubIntegrationConnectionForm.tsx +++ b/apps/studio/components/interfaces/Settings/Integrations/GithubIntegration/GitHubIntegrationConnectionForm.tsx @@ -50,6 +50,7 @@ import { InlineLink } from 'components/ui/InlineLink' import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' import * as z from 'zod' +import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements' const GITHUB_ICON = ( @@ -79,8 +80,10 @@ const GitHubIntegrationConnectionForm = ({ const [repoComboBoxOpen, setRepoComboboxOpen] = useState(false) const isParentProject = !selectedProject?.parent_project_ref - const isProPlanAndUp = selectedOrganization?.plan?.id !== 'free' - const promptProPlanUpgrade = IS_PLATFORM && !isProPlanAndUp + const { hasAccess: hasAccessToGitHubIntegration, isLoading: isLoadingEntitlements } = + useCheckEntitlements('integrations.github_connections') + const promptProPlanUpgrade = + IS_PLATFORM && !isLoadingEntitlements && !hasAccessToGitHubIntegration const { can: canUpdateGitHubConnection } = useAsyncCheckPermissions( PermissionAction.UPDATE, @@ -425,7 +428,8 @@ const GitHubIntegrationConnectionForm = ({ ) } - const isLoading = isCreatingConnection || isUpdatingConnection || isDeletingConnection + const isLoading = + isLoadingEntitlements || isCreatingConnection || isUpdatingConnection || isDeletingConnection return ( <> diff --git a/apps/studio/package.json b/apps/studio/package.json index f12464045b25c..e12597aec341a 100644 --- a/apps/studio/package.json +++ b/apps/studio/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "preinstall": "npx only-allow pnpm", - "dev": "next dev -p 8082", + "dev": "next dev -p ${STUDIO_PORT:-8082}", "build": "next build && if [ \"$SKIP_ASSET_UPLOAD\" != \"1\" ]; then ./../../scripts/upload-static-assets.sh; fi", "start": "next start -p 8082", "lint": "eslint .", @@ -57,8 +57,8 @@ "@stripe/react-stripe-js": "^3.7.0", "@stripe/stripe-js": "^7.5.0", "@supabase/auth-js": "catalog:", - "@supabase/mcp-server-supabase": "^0.6.3", - "@supabase/mcp-utils": "^0.3.2", + "@supabase/mcp-server-supabase": "^0.7.0", + "@supabase/mcp-utils": "^0.4.0", "@supabase/pg-meta": "workspace:*", "@supabase/realtime-js": "catalog:", "@supabase/shared-types": "0.1.84", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dfcd31ce38240..cb6c665e22330 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -835,11 +835,11 @@ importers: specifier: 'catalog:' version: 2.98.1-canary.0 '@supabase/mcp-server-supabase': - specifier: ^0.6.3 - version: 0.6.3(@modelcontextprotocol/sdk@1.27.0(supports-color@8.1.1)(zod@3.25.76))(zod@3.25.76) + specifier: ^0.7.0 + version: 0.7.0(@modelcontextprotocol/sdk@1.27.0(supports-color@8.1.1)(zod@3.25.76))(zod@3.25.76) '@supabase/mcp-utils': - specifier: ^0.3.2 - version: 0.3.2(@modelcontextprotocol/sdk@1.27.0(supports-color@8.1.1)(zod@3.25.76))(zod@3.25.76) + specifier: ^0.4.0 + version: 0.4.0(@modelcontextprotocol/sdk@1.27.0(supports-color@8.1.1)(zod@3.25.76))(zod@3.25.76) '@supabase/pg-meta': specifier: workspace:* version: link:../../packages/pg-meta @@ -8201,15 +8201,15 @@ packages: resolution: {integrity: sha512-SD9lN0ko+NEXV27qVxV2/zrMUaYLvZgIwzroZqZLHZ8bU0ahPh+Pbh/ve67PLelZ9+3lYsLKV949sJ3VhVk9Xw==} engines: {node: '>=20.0.0'} - '@supabase/mcp-server-supabase@0.6.3': - resolution: {integrity: sha512-kAnCD/OT7/0aEa58aC7Zey5N/kb3zeTjxIzvgoJNegHung9NBjojHYXjm5qr0o0If6SF61FhrG4dEdWQmtWFdw==} + '@supabase/mcp-server-supabase@0.7.0': + resolution: {integrity: sha512-t0sOS27T5mDxp6jUYSh3zuyjWhYZarbXCPUO7HTVSyPq2smY6d5UM4Lko81eawYCSRDE8qwDZOKnVkXwCS4RSg==} hasBin: true peerDependencies: '@modelcontextprotocol/sdk': ^1.25.2 zod: ^3.25.0 || ^4.0.0 - '@supabase/mcp-utils@0.3.2': - resolution: {integrity: sha512-hGenK6oEFHkWhAyKGD1gNeIsHRRCl/N/kHmwubZm+UWCj4O1iuS6XgX1Ub2+mH25qqpUHIMaVm9BEV2p1RbhNQ==} + '@supabase/mcp-utils@0.4.0': + resolution: {integrity: sha512-mJ06GYLLZGW4zfz4yl08P2wrx0ORqW5iIAFyqvWJAInIjoa3w7EfJs/h+RbvCE+x7UYuSQeDUXpLJc4lJWDBKA==} peerDependencies: '@modelcontextprotocol/sdk': ^1.25.2 zod: ^3.25.0 || ^4.0.0 @@ -25491,18 +25491,18 @@ snapshots: dependencies: tslib: 2.8.1 - '@supabase/mcp-server-supabase@0.6.3(@modelcontextprotocol/sdk@1.27.0(supports-color@8.1.1)(zod@3.25.76))(zod@3.25.76)': + '@supabase/mcp-server-supabase@0.7.0(@modelcontextprotocol/sdk@1.27.0(supports-color@8.1.1)(zod@3.25.76))(zod@3.25.76)': dependencies: '@mjackson/multipart-parser': 0.10.1 '@modelcontextprotocol/sdk': 1.27.0(supports-color@8.1.1)(zod@3.25.76) - '@supabase/mcp-utils': 0.3.2(@modelcontextprotocol/sdk@1.27.0(supports-color@8.1.1)(zod@3.25.76))(zod@3.25.76) + '@supabase/mcp-utils': 0.4.0(@modelcontextprotocol/sdk@1.27.0(supports-color@8.1.1)(zod@3.25.76))(zod@3.25.76) common-tags: 1.8.2 gqlmin: 0.3.1 graphql: 16.11.0 openapi-fetch: 0.13.8 zod: 3.25.76 - '@supabase/mcp-utils@0.3.2(@modelcontextprotocol/sdk@1.27.0(supports-color@8.1.1)(zod@3.25.76))(zod@3.25.76)': + '@supabase/mcp-utils@0.4.0(@modelcontextprotocol/sdk@1.27.0(supports-color@8.1.1)(zod@3.25.76))(zod@3.25.76)': dependencies: '@modelcontextprotocol/sdk': 1.27.0(supports-color@8.1.1)(zod@3.25.76) zod: 3.25.76