diff --git a/apps/docs/content/_partials/auth_helpers.mdx b/apps/docs/content/_partials/auth_helpers.mdx new file mode 100644 index 0000000000000..ccbf1888699a0 --- /dev/null +++ b/apps/docs/content/_partials/auth_helpers.mdx @@ -0,0 +1,5 @@ + + +The Auth helpers package is deprecated. Use the new `@supabase/ssr` package for Server Side Authentication. `@supabase/ssr` takes the core concepts of the Auth Helpers package and makes them available to any server framework. Read out the [migration doc](/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers) to learn more. + + diff --git a/apps/docs/content/guides/auth/auth-helpers.mdx b/apps/docs/content/guides/auth/auth-helpers.mdx index 52fd9f29bfe2c..9c1d1653e9a04 100644 --- a/apps/docs/content/guides/auth/auth-helpers.mdx +++ b/apps/docs/content/guides/auth/auth-helpers.mdx @@ -5,11 +5,9 @@ description: 'Server-Side Auth guides and utilities for working with Supabase.' sidebar_label: 'Overview' --- - - -The Auth helpers package is deprecated. Use the new `@supabase/ssr` package for Server Side Authentication. `@supabase/ssr` takes the core concepts of the Auth Helpers package and makes them available to any server framework. Check out the [migration doc](/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers) to learn more. - - +<$Partial +path="auth_helpers.mdx" +/> Working with server-side frameworks is slightly different to client-side frameworks. In this section we cover the various ways of handling server-side authentication and demonstrate how to use the Supabase helper-libraries to make the process more seamless. diff --git a/apps/docs/content/guides/auth/auth-helpers/auth-ui.mdx b/apps/docs/content/guides/auth/auth-helpers/auth-ui.mdx index 810e447bb2ca0..951cdc75d9423 100644 --- a/apps/docs/content/guides/auth/auth-helpers/auth-ui.mdx +++ b/apps/docs/content/guides/auth/auth-helpers/auth-ui.mdx @@ -2,7 +2,7 @@ id: 'auth-ui' title: 'Auth UI' description: 'A prebuilt, customizable React component for authenticating users.' -sitemapPriority: 0.5 +sitemapPriority: 0.3 --- diff --git a/apps/docs/content/guides/auth/auth-helpers/flutter-auth-ui.mdx b/apps/docs/content/guides/auth/auth-helpers/flutter-auth-ui.mdx index c0e25cb4fd099..2c82ecfb49674 100644 --- a/apps/docs/content/guides/auth/auth-helpers/flutter-auth-ui.mdx +++ b/apps/docs/content/guides/auth/auth-helpers/flutter-auth-ui.mdx @@ -2,7 +2,7 @@ id: 'flutter-auth-ui' title: 'Flutter Auth UI' description: 'Prebuilt, customizable Flutter widgets for authenticating users.' -sitemapPriority: 0.5 +sitemapPriority: 0.3 --- Flutter Auth UI is a Flutter package containing pre-built widgets for authenticating users. diff --git a/apps/docs/content/guides/auth/auth-helpers/nextjs-pages.mdx b/apps/docs/content/guides/auth/auth-helpers/nextjs-pages.mdx index cae3db4e57b03..bac6b7abc5805 100644 --- a/apps/docs/content/guides/auth/auth-helpers/nextjs-pages.mdx +++ b/apps/docs/content/guides/auth/auth-helpers/nextjs-pages.mdx @@ -3,12 +3,14 @@ id: 'nextjs-pages' title: 'Supabase Auth with Next.js Pages Directory' description: 'Authentication helpers for Next.js API routes, middleware, and SSR in the Pages Directory.' sidebar_label: 'Next.js (pages)' -sitemapPriority: 0.5 +sitemapPriority: 0.3 --- -The `auth-helpers` package has been replaced with the `@supabase/ssr` package. We recommend setting up Auth for your Next.js app with `@supabase/ssr` instead. See the [Next.js Server-Side Auth guide](/docs/guides/auth/server-side/nextjs?router=pages) to learn how. +The Auth helpers package is deprecated. Use the new `@supabase/ssr` package for Server Side Authentication. `@supabase/ssr` takes the core concepts of the Auth Helpers package and makes them available to any server framework. Read the [migration doc](/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers) to learn more. + +We recommend setting up Auth for your Next.js app with `@supabase/ssr` instead. Read the [Next.js Server-Side Auth guide](/docs/guides/auth/server-side/nextjs?router=pages) to learn how. diff --git a/apps/docs/content/guides/auth/auth-helpers/nextjs.mdx b/apps/docs/content/guides/auth/auth-helpers/nextjs.mdx index 94af0d45b25c6..9eaab4f00aacb 100644 --- a/apps/docs/content/guides/auth/auth-helpers/nextjs.mdx +++ b/apps/docs/content/guides/auth/auth-helpers/nextjs.mdx @@ -3,12 +3,14 @@ id: 'nextjs' title: 'Supabase Auth with the Next.js App Router' description: 'Authentication and Authorization helpers for creating an authenticated Supabase client with the Next.js 13 App Router.' sidebar_label: 'Next.js' -sitemapPriority: 0.5 +sitemapPriority: 0.3 --- -The `auth-helpers` are now deprecated. Use `@supabase/ssr` to set up Auth for your Next.js app. See the [Next.js Server-Side Auth guide](/docs/guides/auth/server-side/nextjs) to learn how. +The Auth helpers package is deprecated. Use the new `@supabase/ssr` package for Server Side Authentication. `@supabase/ssr` takes the core concepts of the Auth Helpers package and makes them available to any server framework. Read the [migration doc](/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers) to learn more. + +We recommend setting up Auth for your Next.js app with `@supabase/ssr` instead. Read the [Next.js Server-Side Auth guide](/docs/guides/auth/server-side/nextjs?router=pages) to learn how. diff --git a/apps/docs/content/guides/auth/auth-helpers/remix.mdx b/apps/docs/content/guides/auth/auth-helpers/remix.mdx index cd75f826aec89..a8b7074c9ec87 100644 --- a/apps/docs/content/guides/auth/auth-helpers/remix.mdx +++ b/apps/docs/content/guides/auth/auth-helpers/remix.mdx @@ -3,12 +3,12 @@ id: 'remix' title: 'Supabase Auth with Remix' description: 'Authentication helpers for loaders and actions in Remix.' sidebar_label: 'Remix' -sitemapPriority: 0.5 +sitemapPriority: 0.3 --- -We generally recommend using the new `@supabase/ssr` package instead of `auth-helpers`. `@supabase/ssr` takes the core concepts of the Auth Helpers package and makes them available to any server framework. Check out the [migration doc](/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers) to learn more. +The Auth helpers package is deprecated. Use the new `@supabase/ssr` package for Server Side Authentication. `@supabase/ssr` takes the core concepts of the Auth Helpers package and makes them available to any server framework. Read the [migration doc](/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers) to learn more. diff --git a/apps/docs/content/guides/auth/auth-helpers/sveltekit.mdx b/apps/docs/content/guides/auth/auth-helpers/sveltekit.mdx index 5b96f99bcc0a0..9a2e26f135499 100644 --- a/apps/docs/content/guides/auth/auth-helpers/sveltekit.mdx +++ b/apps/docs/content/guides/auth/auth-helpers/sveltekit.mdx @@ -3,12 +3,12 @@ id: 'sveltekit' title: 'Supabase Auth with SvelteKit' description: 'Convenience helpers for implementing user authentication in SvelteKit.' sidebar_label: 'SvelteKit' -sitemapPriority: 0.5 +sitemapPriority: 0.3 --- -We generally recommend using the new `@supabase/ssr` package instead of `auth-helpers`. `@supabase/ssr` takes the core concepts of the Auth Helpers package and makes them available to any server framework. Check out the [migration doc](/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers) to learn more. +The Auth helpers package is deprecated. Use the new `@supabase/ssr` package for Server Side Authentication. `@supabase/ssr` takes the core concepts of the Auth Helpers package and makes them available to any server framework. Read the [migration doc](/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers) to learn more. diff --git a/apps/docs/content/guides/auth/quickstarts/nextjs.mdx b/apps/docs/content/guides/auth/quickstarts/nextjs.mdx index 5c42213369253..94924ff67ca5c 100644 --- a/apps/docs/content/guides/auth/quickstarts/nextjs.mdx +++ b/apps/docs/content/guides/auth/quickstarts/nextjs.mdx @@ -31,7 +31,7 @@ hideToc: true Use the `create-next-app` command and the `with-supabase` template, to create a Next.js app pre-configured with: - - [Cookie-based Auth](/docs/guides/auth/auth-helpers/nextjs) + - [Cookie-based Auth](docs/guides/auth/server-side/creating-a-client?queryGroups=package-manager&package-manager=npm&queryGroups=framework&framework=nextjs&queryGroups=environment&environment=server) - [TypeScript](https://www.typescriptlang.org/) - [Tailwind CSS](https://tailwindcss.com/) diff --git a/apps/docs/content/guides/auth/server-side.mdx b/apps/docs/content/guides/auth/server-side.mdx index b18e8f0950fbc..3c7d3d3cb64d2 100644 --- a/apps/docs/content/guides/auth/server-side.mdx +++ b/apps/docs/content/guides/auth/server-side.mdx @@ -17,11 +17,9 @@ Make sure to use the PKCE flow instructions where those differ from the implicit We have developed an [`@supabase/ssr`](https://www.npmjs.com/package/@supabase/ssr) package to make setting up the Supabase client as simple as possible. This package is currently in beta. Adoption is recommended but be aware that the API is still unstable and may have breaking changes in the future. - - -If you're currently using the [Auth Helpers package](https://github.com/supabase/auth-helpers), the [docs are still available](/docs/guides/auth/auth-helpers), however we recommend migrating to the new `@supabase/ssr` package as this will be the recommended path moving forward. - - +<$Partial +path="auth_helpers.mdx" +/> ## Framework quickstarts diff --git a/apps/docs/content/guides/auth/server-side/advanced-guide.mdx b/apps/docs/content/guides/auth/server-side/advanced-guide.mdx index d189ff66a969c..536b92ddd08aa 100644 --- a/apps/docs/content/guides/auth/server-side/advanced-guide.mdx +++ b/apps/docs/content/guides/auth/server-side/advanced-guide.mdx @@ -24,8 +24,6 @@ In the PKCE flow, a redirect is made to your app, with an Auth Code contained in To maintain the session, these tokens must be stored in a storage medium securely shared between client and server, which is traditionally cookies. Whenever the session is refreshed, the auth and refresh tokens in the shared storage medium must be updated. Supabase client libraries provide a customizable `storage` option when a client is initiated, allowing you to change where tokens are stored. -For an implementation example, see the [@supabase/ssr](https://github.com/supabase/auth-helpers/blob/main/packages/ssr/src/index.ts) package. - ## Frequently asked questions {/* supa-mdx-lint-disable Rule004ExcludeWords */} diff --git a/apps/docs/content/guides/getting-started.mdx b/apps/docs/content/guides/getting-started.mdx index c41613e11db9a..b95027ac13447 100644 --- a/apps/docs/content/guides/getting-started.mdx +++ b/apps/docs/content/guides/getting-started.mdx @@ -93,6 +93,8 @@ hideToc: true ### Framework quickstarts +<$Show if="docs:framework_quickstarts"> +
{[ { @@ -211,6 +213,14 @@ hideToc: true })}
+ + +<$Show if="!quickstarts:hide_nimbus"> + +<$Partial path="quickstart_nimbus.mdx" /> + + + <$Show if="docs:web_apps"> ### Web app demos diff --git a/apps/docs/content/guides/getting-started/quickstarts/nextjs.mdx b/apps/docs/content/guides/getting-started/quickstarts/nextjs.mdx index 5dace7e590c41..e7fc33911c063 100644 --- a/apps/docs/content/guides/getting-started/quickstarts/nextjs.mdx +++ b/apps/docs/content/guides/getting-started/quickstarts/nextjs.mdx @@ -18,7 +18,7 @@ hideToc: true Use the `create-next-app` command and the `with-supabase` template, to create a Next.js app pre-configured with: - - [Cookie-based Auth](/docs/guides/auth/auth-helpers/nextjs) + - [Cookie-based Auth](docs/guides/auth/server-side/creating-a-client?queryGroups=package-manager&package-manager=npm&queryGroups=framework&framework=nextjs&queryGroups=environment&environment=server) - [TypeScript](https://www.typescriptlang.org/) - [Tailwind CSS](https://tailwindcss.com/) diff --git a/apps/docs/content/guides/getting-started/tutorials/with-nextjs.mdx b/apps/docs/content/guides/getting-started/tutorials/with-nextjs.mdx index 00dfdeacf8997..4fe57e4e3aa12 100644 --- a/apps/docs/content/guides/getting-started/tutorials/with-nextjs.mdx +++ b/apps/docs/content/guides/getting-started/tutorials/with-nextjs.mdx @@ -1028,7 +1028,6 @@ At this stage you have a fully functional application! - See the complete [example on GitHub](https://github.com/supabase/supabase/tree/master/examples/user-management/nextjs-user-management) and deploy it to Vercel - [Build a Twitter Clone with the Next.js App Router and Supabase - free egghead course](https://egghead.io/courses/build-a-twitter-clone-with-the-next-js-app-router-and-supabase-19bebadb) -- Explore the [pre-built Auth UI for React](/docs/guides/auth/auth-helpers/auth-ui) -- Explore the [Auth Helpers for Next.js](/docs/guides/auth/auth-helpers/nextjs) +- Explore the [pre-built Auth components](/ui/docs/nextjs/password-based-auth) - Explore the [Supabase Cache Helpers](https://github.com/psteinroe/supabase-cache-helpers) - See the [Next.js Subscription Payments Starter](https://github.com/vercel/nextjs-subscription-payments) template on GitHub diff --git a/apps/docs/content/guides/realtime/authorization.mdx b/apps/docs/content/guides/realtime/authorization.mdx index df978682739c6..4bdb5850eabc2 100644 --- a/apps/docs/content/guides/realtime/authorization.mdx +++ b/apps/docs/content/guides/realtime/authorization.mdx @@ -20,10 +20,9 @@ Realtime Authorization is in Public Beta. To use Authorization for your Realtime -To enforce private channels you need to disable the 'Allow public access' setting in [Realtime Settings](/dashboard/project/_?featurePreviewModal=supabase-ui-realtime-settings) +To enforce private channels you need to disable the 'Allow public access' setting in [Realtime Settings](/dashboard/project/_/realtime/settings) - ## How it works Realtime uses the `messages` table in your database's `realtime` schema to generate access policies for your clients when they connect to a Channel topic. diff --git a/apps/docs/content/guides/realtime/settings.mdx b/apps/docs/content/guides/realtime/settings.mdx index da1b323aaafaf..9675d92b5bb21 100644 --- a/apps/docs/content/guides/realtime/settings.mdx +++ b/apps/docs/content/guides/realtime/settings.mdx @@ -6,12 +6,6 @@ subtitle: 'Realtime Settings that allow you to configure your Realtime usage.' ## Settings - - -Realtime settings are currently under Feature Preview section in the dashboard. - - - All changes made in this screen will disconnect all your connected clients to ensure Realtime starts with the appropriate settings and all changes are stored in Supabase middleware. diff --git a/apps/docs/features/directives/Show.test.ts b/apps/docs/features/directives/Show.test.ts index cbe3024a11472..3fea57f501206 100644 --- a/apps/docs/features/directives/Show.test.ts +++ b/apps/docs/features/directives/Show.test.ts @@ -59,18 +59,51 @@ Content after the show block. expect(isFeatureEnabled).toHaveBeenCalledWith('test-feature') }) - it('should remove entire $Show block and children when feature is disabled', async () => { + it('should keep children when negated feature is disabled', async () => { vi.mocked(isFeatureEnabled).mockReturnValue(false) const markdown = ` # Test content -<$Show if="disabled-feature"> -This content should NOT be visible when feature is disabled. +<$Show if="!negated-feature"> +This content should be visible when the feature is disabled. + +## Additional content + +More text that should remain. + + +Content after the show block. +`.trim() + + const mdast = fromDocsMarkdown(markdown) + const transformed = showRemark()(mdast) + const output = toMarkdown(transformed, { extensions: [mdxToMarkdown()] }) + + const expected = ` +# Test content -## This heading should also be removed +This content should be visible when the feature is disabled. -Some more content that should be hidden. +## Additional content + +More text that should remain. + +Content after the show block. +`.trimStart() + + expect(output).toEqual(expected) + expect(isFeatureEnabled).toHaveBeenCalledWith('negated-feature') + }) + + it('should remove $Show block when negated feature is enabled', async () => { + vi.mocked(isFeatureEnabled).mockReturnValue(true) + + const markdown = ` +# Test content + +<$Show if="!enabled-negated-feature"> +This content should NOT be visible because the feature is enabled. Content after the show block should remain. @@ -87,7 +120,7 @@ Content after the show block should remain. `.trimStart() expect(output).toEqual(expected) - expect(isFeatureEnabled).toHaveBeenCalledWith('disabled-feature') + expect(isFeatureEnabled).toHaveBeenCalledWith('enabled-negated-feature') }) it('should handle multiple $Show blocks with different feature flags', async () => { diff --git a/apps/docs/features/directives/Show.ts b/apps/docs/features/directives/Show.ts index 94401222466aa..5a1e0b32e4362 100644 --- a/apps/docs/features/directives/Show.ts +++ b/apps/docs/features/directives/Show.ts @@ -38,13 +38,24 @@ export function showRemark() { if (node.name !== '$Show') return const parent = ancestors[ancestors.length - 1] - const featureName = getAttributeValue(node, 'if') + const rawFeatureName = getAttributeValue(node, 'if') - if (typeof featureName !== 'string') { + if (typeof rawFeatureName !== 'string') { throw new Error('$Show directive requires a string value for the "if" attribute') } - const shouldShow = isFeatureEnabled(featureName as Feature) + const trimmedFeatureName = rawFeatureName.trim() + const isNegated = trimmedFeatureName.startsWith('!') + const normalizedFeatureName = ( + isNegated ? trimmedFeatureName.slice(1) : trimmedFeatureName + ).trim() + + if (!normalizedFeatureName) { + throw new Error('$Show directive requires a non-empty feature name for the "if" attribute') + } + + const isEnabled = isFeatureEnabled(normalizedFeatureName as Feature) + const shouldShow = isNegated ? !isEnabled : isEnabled nodesToProcess.push({ node, diff --git a/apps/docs/internals/files/cli.ts b/apps/docs/internals/files/cli.ts index eb3b18f8f977e..818a425de84af 100644 --- a/apps/docs/internals/files/cli.ts +++ b/apps/docs/internals/files/cli.ts @@ -13,11 +13,11 @@ const cliSpec = yaml.load(fs.readFileSync(`spec/cli_v1_commands.yaml`, 'utf8')) * @returns {Array<{link: string}>} - An array of CLI page links. */ export function generateCLIPages() { - let cliPages: Array<{ link: string }> = [] + let cliPages: Array<{ link: string; priority: number }> = [] cliSpec.commands.map((section: any) => { const slug = (flatCLISections as any[]).find((item: any) => item.id === section.id)?.slug - if (slug) cliPages.push({ link: `reference/cli/${slug}` }) + if (slug) cliPages.push({ link: `reference/cli/${slug}`, priority: 0.8 }) }) return cliPages } diff --git a/apps/docs/internals/files/reference-lib.ts b/apps/docs/internals/files/reference-lib.ts index 5fbc1dea2db78..f1dbafbf5cc11 100644 --- a/apps/docs/internals/files/reference-lib.ts +++ b/apps/docs/internals/files/reference-lib.ts @@ -33,6 +33,7 @@ export async function generateReferencePages() { link: isLatestVersion ? `reference/${libPath}/${section.slug}` : `reference/${libPath}/${version}/${section.slug}`, + priority: 0.8, })) ?? [] ) }) diff --git a/apps/docs/internals/generate-sitemap.ts b/apps/docs/internals/generate-sitemap.ts index f1d37d6d8b5f0..5e90098ba67bf 100644 --- a/apps/docs/internals/generate-sitemap.ts +++ b/apps/docs/internals/generate-sitemap.ts @@ -25,7 +25,7 @@ async function generate() { return { link: filePath.replace(/^content\//, '').replace(/\.mdx$/, ''), - priority: sitemapPriority, + priority: sitemapPriority ?? 0.8, } }) ) @@ -40,7 +40,7 @@ async function generate() { }) ) - const allPages = (contentPages as Array<{ link: string; priority?: number }>).concat( + const allPages = (contentPages as Array<{ link: string; priority: number }>).concat( troubleshootingPages, referencePages, cliPages @@ -51,11 +51,12 @@ async function generate() { ${allPages .map(({ link, priority }) => { + const finalPriority = priority ?? 0.8 return ` ${`https://supabase.com/docs/${link}`} weekly - ${priority ? `${priority}` : ''} + ${finalPriority} ` }) diff --git a/apps/docs/spec/api_v1_openapi.json b/apps/docs/spec/api_v1_openapi.json index 658a292fa0665..36e0d4f4a64a7 100644 --- a/apps/docs/spec/api_v1_openapi.json +++ b/apps/docs/spec/api_v1_openapi.json @@ -449,7 +449,10 @@ "required": false, "in": "query", "description": "Resource indicator for MCP (Model Context Protocol) clients", - "schema": { "type": "string", "enum": ["https://api.supabase.io/mcp"] } + "schema": { + "type": "string", + "enum": ["https://api.supabase.com/mcp", "https://mcp.supabase.com/mcp"] + } } ], "responses": { "204": { "description": "" } }, @@ -4790,7 +4793,7 @@ "refresh_token": { "type": "string" }, "resource": { "type": "string", - "enum": ["https://api.supabase.io/mcp"], + "enum": ["https://api.supabase.com/mcp", "https://mcp.supabase.com/mcp"], "description": "Resource indicator for MCP (Model Context Protocol) clients" } }, @@ -5615,7 +5618,17 @@ { "type": "object", "properties": { + "kid": { "type": "string", "format": "uuid" }, + "use": { "type": "string", "enum": ["sig"] }, + "key_ops": { + "type": "array", + "items": { "type": "string", "enum": ["sign", "verify"] }, + "minItems": 2, + "maxItems": 2 + }, + "ext": { "type": "boolean", "enum": [true] }, "kty": { "type": "string", "enum": ["RSA"] }, + "alg": { "type": "string", "enum": ["RS256"] }, "n": { "type": "string" }, "e": { "type": "string", "enum": ["AQAB"] }, "d": { "type": "string" }, @@ -5631,7 +5644,17 @@ { "type": "object", "properties": { + "kid": { "type": "string", "format": "uuid" }, + "use": { "type": "string", "enum": ["sig"] }, + "key_ops": { + "type": "array", + "items": { "type": "string", "enum": ["sign", "verify"] }, + "minItems": 2, + "maxItems": 2 + }, + "ext": { "type": "boolean", "enum": [true] }, "kty": { "type": "string", "enum": ["EC"] }, + "alg": { "type": "string", "enum": ["ES256"] }, "crv": { "type": "string", "enum": ["P-256"] }, "x": { "type": "string" }, "y": { "type": "string" }, @@ -5643,7 +5666,17 @@ { "type": "object", "properties": { + "kid": { "type": "string", "format": "uuid" }, + "use": { "type": "string", "enum": ["sig"] }, + "key_ops": { + "type": "array", + "items": { "type": "string", "enum": ["sign", "verify"] }, + "minItems": 2, + "maxItems": 2 + }, + "ext": { "type": "boolean", "enum": [true] }, "kty": { "type": "string", "enum": ["OKP"] }, + "alg": { "type": "string", "enum": ["EdDSA"] }, "crv": { "type": "string", "enum": ["Ed25519"] }, "x": { "type": "string" }, "d": { "type": "string" } @@ -5654,7 +5687,17 @@ { "type": "object", "properties": { + "kid": { "type": "string", "format": "uuid" }, + "use": { "type": "string", "enum": ["sig"] }, + "key_ops": { + "type": "array", + "items": { "type": "string", "enum": ["sign", "verify"] }, + "minItems": 2, + "maxItems": 2 + }, + "ext": { "type": "boolean", "enum": [true] }, "kty": { "type": "string", "enum": ["oct"] }, + "alg": { "type": "string", "enum": ["HS256"] }, "k": { "type": "string", "minLength": 16 } }, "required": ["kty", "k"], diff --git a/apps/docs/spec/transforms/api_v1_openapi_deparsed.json b/apps/docs/spec/transforms/api_v1_openapi_deparsed.json index 4001882bb25ae..2d2a2ccae8014 100644 --- a/apps/docs/spec/transforms/api_v1_openapi_deparsed.json +++ b/apps/docs/spec/transforms/api_v1_openapi_deparsed.json @@ -1134,7 +1134,7 @@ "description": "Resource indicator for MCP (Model Context Protocol) clients", "schema": { "type": "string", - "enum": ["https://api.supabase.io/mcp"] + "enum": ["https://api.supabase.com/mcp", "https://mcp.supabase.com/mcp"] } } ], @@ -1188,7 +1188,7 @@ }, "resource": { "type": "string", - "enum": ["https://api.supabase.io/mcp"], + "enum": ["https://api.supabase.com/mcp", "https://mcp.supabase.com/mcp"], "description": "Resource indicator for MCP (Model Context Protocol) clients" } }, @@ -5930,10 +5930,35 @@ { "type": "object", "properties": { + "kid": { + "type": "string", + "format": "uuid" + }, + "use": { + "type": "string", + "enum": ["sig"] + }, + "key_ops": { + "type": "array", + "items": { + "type": "string", + "enum": ["sign", "verify"] + }, + "minItems": 2, + "maxItems": 2 + }, + "ext": { + "type": "boolean", + "enum": [true] + }, "kty": { "type": "string", "enum": ["RSA"] }, + "alg": { + "type": "string", + "enum": ["RS256"] + }, "n": { "type": "string" }, @@ -5966,10 +5991,35 @@ { "type": "object", "properties": { + "kid": { + "type": "string", + "format": "uuid" + }, + "use": { + "type": "string", + "enum": ["sig"] + }, + "key_ops": { + "type": "array", + "items": { + "type": "string", + "enum": ["sign", "verify"] + }, + "minItems": 2, + "maxItems": 2 + }, + "ext": { + "type": "boolean", + "enum": [true] + }, "kty": { "type": "string", "enum": ["EC"] }, + "alg": { + "type": "string", + "enum": ["ES256"] + }, "crv": { "type": "string", "enum": ["P-256"] @@ -5990,10 +6040,35 @@ { "type": "object", "properties": { + "kid": { + "type": "string", + "format": "uuid" + }, + "use": { + "type": "string", + "enum": ["sig"] + }, + "key_ops": { + "type": "array", + "items": { + "type": "string", + "enum": ["sign", "verify"] + }, + "minItems": 2, + "maxItems": 2 + }, + "ext": { + "type": "boolean", + "enum": [true] + }, "kty": { "type": "string", "enum": ["OKP"] }, + "alg": { + "type": "string", + "enum": ["EdDSA"] + }, "crv": { "type": "string", "enum": ["Ed25519"] @@ -6011,10 +6086,35 @@ { "type": "object", "properties": { + "kid": { + "type": "string", + "format": "uuid" + }, + "use": { + "type": "string", + "enum": ["sig"] + }, + "key_ops": { + "type": "array", + "items": { + "type": "string", + "enum": ["sign", "verify"] + }, + "minItems": 2, + "maxItems": 2 + }, + "ext": { + "type": "boolean", + "enum": [true] + }, "kty": { "type": "string", "enum": ["oct"] }, + "alg": { + "type": "string", + "enum": ["HS256"] + }, "k": { "type": "string", "minLength": 16 @@ -15906,7 +16006,7 @@ }, "resource": { "type": "string", - "enum": ["https://api.supabase.io/mcp"], + "enum": ["https://api.supabase.com/mcp", "https://mcp.supabase.com/mcp"], "description": "Resource indicator for MCP (Model Context Protocol) clients" } }, @@ -17216,10 +17316,35 @@ { "type": "object", "properties": { + "kid": { + "type": "string", + "format": "uuid" + }, + "use": { + "type": "string", + "enum": ["sig"] + }, + "key_ops": { + "type": "array", + "items": { + "type": "string", + "enum": ["sign", "verify"] + }, + "minItems": 2, + "maxItems": 2 + }, + "ext": { + "type": "boolean", + "enum": [true] + }, "kty": { "type": "string", "enum": ["RSA"] }, + "alg": { + "type": "string", + "enum": ["RS256"] + }, "n": { "type": "string" }, @@ -17252,10 +17377,35 @@ { "type": "object", "properties": { + "kid": { + "type": "string", + "format": "uuid" + }, + "use": { + "type": "string", + "enum": ["sig"] + }, + "key_ops": { + "type": "array", + "items": { + "type": "string", + "enum": ["sign", "verify"] + }, + "minItems": 2, + "maxItems": 2 + }, + "ext": { + "type": "boolean", + "enum": [true] + }, "kty": { "type": "string", "enum": ["EC"] }, + "alg": { + "type": "string", + "enum": ["ES256"] + }, "crv": { "type": "string", "enum": ["P-256"] @@ -17276,10 +17426,35 @@ { "type": "object", "properties": { + "kid": { + "type": "string", + "format": "uuid" + }, + "use": { + "type": "string", + "enum": ["sig"] + }, + "key_ops": { + "type": "array", + "items": { + "type": "string", + "enum": ["sign", "verify"] + }, + "minItems": 2, + "maxItems": 2 + }, + "ext": { + "type": "boolean", + "enum": [true] + }, "kty": { "type": "string", "enum": ["OKP"] }, + "alg": { + "type": "string", + "enum": ["EdDSA"] + }, "crv": { "type": "string", "enum": ["Ed25519"] @@ -17297,10 +17472,35 @@ { "type": "object", "properties": { + "kid": { + "type": "string", + "format": "uuid" + }, + "use": { + "type": "string", + "enum": ["sig"] + }, + "key_ops": { + "type": "array", + "items": { + "type": "string", + "enum": ["sign", "verify"] + }, + "minItems": 2, + "maxItems": 2 + }, + "ext": { + "type": "boolean", + "enum": [true] + }, "kty": { "type": "string", "enum": ["oct"] }, + "alg": { + "type": "string", + "enum": ["HS256"] + }, "k": { "type": "string", "minLength": 16 diff --git a/apps/studio/components/interfaces/App/FeaturePreview/AdvisorRulesPreview.tsx b/apps/studio/components/interfaces/App/FeaturePreview/AdvisorRulesPreview.tsx index 42dc1d41ad5a4..254b84f5be021 100644 --- a/apps/studio/components/interfaces/App/FeaturePreview/AdvisorRulesPreview.tsx +++ b/apps/studio/components/interfaces/App/FeaturePreview/AdvisorRulesPreview.tsx @@ -3,11 +3,11 @@ import Image from 'next/image' import { useParams } from 'common' import { InlineLink } from 'components/ui/InlineLink' import { BASE_PATH } from 'lib/constants' -import { useIsRealtimeSettingsEnabled } from './FeaturePreviewContext' +import { useIsAdvisorRulesEnabled } from './FeaturePreviewContext' export const AdvisorRulesPreview = () => { const { ref } = useParams() - const isRealtimeSettingsEnabled = useIsRealtimeSettingsEnabled() + const isAdvisorRulesEnabled = useIsAdvisorRulesEnabled() return (
@@ -30,9 +30,9 @@ export const AdvisorRulesPreview = () => { Allow you to disable advisor rules for your project from the{' '} Advisors section. diff --git a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreview.constants.tsx b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreview.constants.tsx index 067fa678f46ad..f7448acba9807 100644 --- a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreview.constants.tsx +++ b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreview.constants.tsx @@ -15,13 +15,6 @@ export const FEATURE_PREVIEWS = [ isNew: true, isPlatformOnly: true, }, - { - key: LOCAL_STORAGE_KEYS.UI_PREVIEW_REALTIME_SETTINGS, - name: 'Realtime settings', - discussionsUrl: undefined, - isNew: true, - isPlatformOnly: true, - }, { key: LOCAL_STORAGE_KEYS.UI_PREVIEW_ADVISOR_RULES, name: 'Disable Advisor rules', diff --git a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx index 76301d74f3356..e33348dd18dc4 100644 --- a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx +++ b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewContext.tsx @@ -11,7 +11,6 @@ import { } from 'react' import { FeatureFlagContext, LOCAL_STORAGE_KEYS, useFlag } from 'common' -import { useIsRealtimeSettingsFFEnabled } from 'hooks/ui/useFlag' import { EMPTY_OBJ } from 'lib/void' import { FEATURE_PREVIEWS } from './FeaturePreview.constants' @@ -101,11 +100,6 @@ export const useUnifiedLogsPreview = () => { return { isEnabled, enable, disable } } -export const useIsRealtimeSettingsEnabled = () => { - const { flags } = useFeaturePreviewContext() - return flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_REALTIME_SETTINGS] -} - export const useIsBranching2Enabled = () => { const { flags } = useFeaturePreviewContext() return flags[LOCAL_STORAGE_KEYS.UI_PREVIEW_BRANCHING_2_0] @@ -119,7 +113,6 @@ export const useIsAdvisorRulesEnabled = () => { export const useFeaturePreviewModal = () => { const [featurePreviewModal, setFeaturePreviewModal] = useQueryState('featurePreviewModal') - const isRealtimeSettingsEnabled = useIsRealtimeSettingsFFEnabled() const gitlessBranchingEnabled = useFlag('gitlessBranching') const advisorRulesEnabled = useFlag('advisorRules') const isUnifiedLogsPreviewAvailable = useFlag('unifiedLogs') @@ -131,8 +124,6 @@ export const useFeaturePreviewModal = () => { const isFeaturePreviewReleasedToPublic = useCallback( (feature: (typeof FEATURE_PREVIEWS)[number]) => { switch (feature.key) { - case 'supabase-ui-realtime-settings': - return isRealtimeSettingsEnabled case 'supabase-ui-branching-2-0': return gitlessBranchingEnabled case 'supabase-ui-advisor-rules': @@ -143,12 +134,7 @@ export const useFeaturePreviewModal = () => { return true } }, - [ - isRealtimeSettingsEnabled, - gitlessBranchingEnabled, - advisorRulesEnabled, - isUnifiedLogsPreviewAvailable, - ] + [gitlessBranchingEnabled, advisorRulesEnabled, isUnifiedLogsPreviewAvailable] ) const selectedFeatureKey = ( diff --git a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx index cdb7a69129e89..751c08f92c961 100644 --- a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx +++ b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx @@ -14,14 +14,12 @@ import { CLSPreview } from './CLSPreview' import { FEATURE_PREVIEWS } from './FeaturePreview.constants' import { useFeaturePreviewContext, useFeaturePreviewModal } from './FeaturePreviewContext' import { InlineEditorPreview } from './InlineEditorPreview' -import { RealtimeSettingsPreview } from './RealtimeSettingsPreview' import { UnifiedLogsPreview } from './UnifiedLogsPreview' const FEATURE_PREVIEW_KEY_TO_CONTENT: { [key: string]: ReactNode } = { [LOCAL_STORAGE_KEYS.UI_PREVIEW_BRANCHING_2_0]: , - [LOCAL_STORAGE_KEYS.UI_PREVIEW_REALTIME_SETTINGS]: , [LOCAL_STORAGE_KEYS.UI_PREVIEW_ADVISOR_RULES]: , [LOCAL_STORAGE_KEYS.UI_PREVIEW_INLINE_EDITOR]: , [LOCAL_STORAGE_KEYS.UI_PREVIEW_API_SIDE_PANEL]: , diff --git a/apps/studio/components/interfaces/App/FeaturePreview/RealtimeSettingsPreview.tsx b/apps/studio/components/interfaces/App/FeaturePreview/RealtimeSettingsPreview.tsx deleted file mode 100644 index 43a9c283f25a0..0000000000000 --- a/apps/studio/components/interfaces/App/FeaturePreview/RealtimeSettingsPreview.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import Image from 'next/image' - -import { useParams } from 'common' -import { InlineLink } from 'components/ui/InlineLink' -import { BASE_PATH } from 'lib/constants' -import { useIsRealtimeSettingsEnabled } from './FeaturePreviewContext' - -export const RealtimeSettingsPreview = () => { - const { ref } = useParams() - const isRealtimeSettingsEnabled = useIsRealtimeSettingsEnabled() - - return ( -
-

- Allows you to setup several configurations for Realtime, including configuration channel - restrictions where you can enable or disable public channels from being able to connect. - Learn more about how Realtime Authorization works{' '} - - in our documentation - - . -

- api-docs-side-panel-preview -
-

Enabling this preview will:

-
    -
  • - Allow you to configure realtime settings for your project from the{' '} - - realtime section. - -
  • -
-
-
- ) -} diff --git a/apps/studio/components/interfaces/Database/Backups/RestoreToNewProject/ConfirmRestoreDialog.tsx b/apps/studio/components/interfaces/Database/Backups/RestoreToNewProject/ConfirmRestoreDialog.tsx index 3df3aa095cb4b..ddeaf156a59f5 100644 --- a/apps/studio/components/interfaces/Database/Backups/RestoreToNewProject/ConfirmRestoreDialog.tsx +++ b/apps/studio/components/interfaces/Database/Backups/RestoreToNewProject/ConfirmRestoreDialog.tsx @@ -61,7 +61,6 @@ export const ConfirmRestoreDialog = ({
  • Storage objects & settings
  • Edge Functions
  • Auth settings & API keys
  • -
  • Realtime settings
  • Database extensions and settings
  • Read replicas
  • diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryDetail.tsx b/apps/studio/components/interfaces/QueryPerformance/QueryDetail.tsx index 768d10a996d25..78f7b8291285e 100644 --- a/apps/studio/components/interfaces/QueryPerformance/QueryDetail.tsx +++ b/apps/studio/components/interfaces/QueryPerformance/QueryDetail.tsx @@ -1,9 +1,11 @@ -import { Lightbulb } from 'lucide-react' +import { Lightbulb, ChevronsUpDown, Expand } from 'lucide-react' import { useEffect, useState } from 'react' import dynamic from 'next/dynamic' +import dayjs from 'dayjs' import { formatSql } from 'lib/formatSql' import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, Button, cn } from 'ui' +import { ButtonTooltip } from 'components/ui/ButtonTooltip' import { QueryPanelContainer, QueryPanelSection } from './QueryPanel' import { QUERY_PERFORMANCE_COLUMNS, @@ -37,52 +39,182 @@ export const QueryDetail = ({ selectedRow, onClickViewSuggestion }: QueryDetailP } }, [selectedRow]) + const [isExpanded, setIsExpanded] = useState(false) + + const formatDuration = (seconds: number) => { + const dur = dayjs.duration(seconds, 'seconds') + + const minutes = Math.floor(dur.asMinutes()) + const remainingSeconds = dur.seconds() + dur.milliseconds() / 1000 + + const parts = [] + if (minutes > 0) parts.push(`${minutes}m`) + if (remainingSeconds > 0) { + const formattedSeconds = remainingSeconds.toFixed(2) + parts.push(`${formattedSeconds}s`) + } + + return parts.join(' ') + } + return ( - -

    Query pattern

    - - {isLinterWarning && ( - +

    Query pattern

    +
    + + {isLinterWarning && ( + + + Suggested optimization: Add an index + + Adding an index will help this query execute faster + + + + + + )} +
    +
    +
    + - - - )} + {isExpanded ? 'Collapse' : 'Expand'} + +
    -
    - - {report - .filter((x) => x.id !== 'query') - .map((x) => { - const rawValue = selectedRow?.[x.id] - const isTime = x.name.includes('time') - - const formattedValue = isTime - ? typeof rawValue === 'number' && !isNaN(rawValue) && isFinite(rawValue) - ? `${rawValue.toFixed(2)}ms` - : 'N/A' - : rawValue != null - ? String(rawValue) - : 'N/A' - - return ( -
    -

    {x.name}

    -

    {formattedValue}

    -
    - ) - })} + +

    Metadata

    +
      + {report + .filter((x) => x.id !== 'query') + .map((x) => { + const rawValue = selectedRow?.[x.id] + const isTime = x.name.includes('time') + + const formattedValue = isTime + ? typeof rawValue === 'number' && !isNaN(rawValue) && isFinite(rawValue) + ? `${Math.round(rawValue).toLocaleString()}ms` + : 'n/a' + : rawValue != null + ? String(rawValue) + : 'n/a' + + if (x.id === 'prop_total_time') { + const percentage = selectedRow?.prop_total_time || 0 + const totalTime = selectedRow?.total_time || 0 + + return ( +
    • +

      {x.name}

      + {percentage && totalTime ? ( +

      + + {percentage.toFixed(1)}% + {' '} + /{' '} + + {formatDuration(totalTime / 1000)} + +

      + ) : ( +

      + )} +
    • + ) + } + + if (x.id == 'rows_read') { + return ( +
    • +

      {x.name}

      + {typeof rawValue === 'number' && !isNaN(rawValue) && isFinite(rawValue) ? ( +

      + {rawValue.toLocaleString()} +

      + ) : ( +

      + )} +
    • + ) + } + + const cacheHitRateToNumber = (value: number | string) => { + if (typeof value === 'number') return value + return parseFloat(value.toString().replace('%', '')) || 0 + } + + if (x.id === 'cache_hit_rate') { + return ( +
    • +

      {x.name}

      + {typeof rawValue === 'string' ? ( +

      + {cacheHitRateToNumber(rawValue).toLocaleString(undefined, { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + })} + % +

      + ) : ( +

      + )} +
    • + ) + } + + return ( +
    • +

      {x.name}

      +

      + {formattedValue} +

      +
    • + ) + })} +
    ) diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryPanel.tsx b/apps/studio/components/interfaces/QueryPerformance/QueryPanel.tsx index 4cee87a3ece2c..57c33a87f5910 100644 --- a/apps/studio/components/interfaces/QueryPerformance/QueryPanel.tsx +++ b/apps/studio/components/interfaces/QueryPerformance/QueryPanel.tsx @@ -7,14 +7,14 @@ export const QueryPanelContainer = ({ children, className, }: PropsWithChildren<{ className?: string }>) => ( -
    {children}
    +
    {children}
    ) export const QueryPanelSection = ({ children, className, }: PropsWithChildren<{ className?: string }>) => ( -
    {children}
    +
    {children}
    ) export const QueryPanelScoreSection = ({ diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.constants.ts b/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.constants.ts index a09da9a32b1df..4b96763edfa4d 100644 --- a/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.constants.ts +++ b/apps/studio/components/interfaces/QueryPerformance/QueryPerformance.constants.ts @@ -14,8 +14,7 @@ export const QUERY_PERFORMANCE_PRESET_MAP = { export const QUERY_PERFORMANCE_COLUMNS = [ { id: 'query', name: 'Query', description: undefined, minWidth: 500 }, - { id: 'prop_total_time', name: 'Time consumed', description: undefined, minWidth: 130 }, - { id: 'total_time', name: 'Total time', description: 'latency', minWidth: 150 }, + { id: 'prop_total_time', name: 'Time consumed', description: undefined, minWidth: 150 }, { id: 'calls', name: 'Count', description: undefined, minWidth: 100 }, { id: 'max_time', name: 'Max time', description: undefined, minWidth: 100 }, { id: 'mean_time', name: 'Mean time', description: undefined, minWidth: 100 }, diff --git a/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx b/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx index 36cadf22a8600..62e3bb4973204 100644 --- a/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx +++ b/apps/studio/components/interfaces/QueryPerformance/QueryPerformanceGrid.tsx @@ -141,51 +141,48 @@ export const QueryPerformanceGrid = ({ queryPerformanceQuery }: QueryPerformance ) } + const isTime = col.name.includes('time') + const formattedValue = + !!value && typeof value === 'number' && !isNaN(value) && isFinite(value) + ? isTime + ? `${value.toFixed(0).toLocaleString()}ms` + : value.toLocaleString() + : '' + if (col.id === 'prop_total_time') { const percentage = props.row.prop_total_time || 0 + const totalTime = props.row.total_time || 0 const fillWidth = Math.min(percentage, 100) return (
    - {value ? ( -

    - {value.toFixed(1)}% -

    - ) : ( -

    - )} -
    - ) - } - - const isTime = col.name.includes('time') - const formattedValue = - !!value && typeof value === 'number' && !isNaN(value) && isFinite(value) - ? isTime - ? `${value.toFixed(0).toLocaleString()}ms` - : value.toLocaleString() - : '' - - if (col.id === 'total_time') { - return ( -
    - {isTime && typeof value === 'number' && !isNaN(value) && isFinite(value) ? ( -

    - {(value / 1000).toLocaleString(undefined, { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - })} - s -

    + {percentage && totalTime ? ( + + + {percentage.toFixed(1)}% + {' '} + / + + {(totalTime / 1000).toLocaleString(undefined, { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + })} + s + + ) : (

    )} diff --git a/apps/studio/components/interfaces/Realtime/RealtimeSettings.tsx b/apps/studio/components/interfaces/Realtime/RealtimeSettings.tsx index c755fb5118572..b7b4b05a9e520 100644 --- a/apps/studio/components/interfaces/Realtime/RealtimeSettings.tsx +++ b/apps/studio/components/interfaces/Realtime/RealtimeSettings.tsx @@ -40,11 +40,12 @@ const formId = 'realtime-configuration-form' export const RealtimeSettings = () => { const { ref: projectRef } = useParams() const { data: project } = useSelectedProjectQuery() - const { data: organization } = useSelectedOrganizationQuery() - const { can: canUpdateConfig } = useAsyncCheckPermissions( - PermissionAction.REALTIME_ADMIN_READ, - '*' - ) + const { data: organization, isSuccess: isSuccessOrganization } = useSelectedOrganizationQuery() + const { + can: canUpdateConfig, + isLoading: isLoadingPermissions, + isSuccess: isPermissionsLoaded, + } = useAsyncCheckPermissions(PermissionAction.REALTIME_ADMIN_READ, '*') const { data: maxConn } = useMaxConnectionsQuery({ projectRef: project?.ref, @@ -134,7 +135,11 @@ export const RealtimeSettings = () => { className="!p-0 !pt-2" header={Channel restrictions} > - + { /> - {!isUsageBillingEnabled && ( + {isSuccessOrganization && !isUsageBillingEnabled && ( { */}
    - {!canUpdateConfig && ( + {isPermissionsLoaded && !canUpdateConfig && (

    You need additional permissions to update realtime settings

    diff --git a/apps/studio/components/layouts/RealtimeLayout/RealtimeLayout.tsx b/apps/studio/components/layouts/RealtimeLayout/RealtimeLayout.tsx index e4cef928a7312..004a528e6b431 100644 --- a/apps/studio/components/layouts/RealtimeLayout/RealtimeLayout.tsx +++ b/apps/studio/components/layouts/RealtimeLayout/RealtimeLayout.tsx @@ -1,11 +1,9 @@ import { useRouter } from 'next/router' import { PropsWithChildren } from 'react' -import { useIsRealtimeSettingsEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext' import { ProductMenu } from 'components/ui/ProductMenu' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import { withAuth } from 'hooks/misc/withAuth' -import { useIsRealtimeSettingsFFEnabled } from 'hooks/ui/useFlag' import ProjectLayout from '../ProjectLayout/ProjectLayout' import { generateRealtimeMenu } from './RealtimeMenu.utils' @@ -15,10 +13,6 @@ export interface RealtimeLayoutProps { const RealtimeLayout = ({ title, children }: PropsWithChildren) => { const { data: project } = useSelectedProjectQuery() - const enableRealtimeSettingsFF = useIsRealtimeSettingsFFEnabled() - const enableRealtimeSettingsFP = useIsRealtimeSettingsEnabled() - - const enableRealtimeSettings = enableRealtimeSettingsFF && enableRealtimeSettingsFP const router = useRouter() const page = router.pathname.split('/')[4] @@ -27,12 +21,7 @@ const RealtimeLayout = ({ title, children }: PropsWithChildren - } + productMenu={} > {children} diff --git a/apps/studio/components/layouts/RealtimeLayout/RealtimeMenu.utils.ts b/apps/studio/components/layouts/RealtimeLayout/RealtimeMenu.utils.ts index 1aa33aeb64ae7..2c961d277098d 100644 --- a/apps/studio/components/layouts/RealtimeLayout/RealtimeMenu.utils.ts +++ b/apps/studio/components/layouts/RealtimeLayout/RealtimeMenu.utils.ts @@ -2,13 +2,9 @@ import type { ProductMenuGroup } from 'components/ui/ProductMenu/ProductMenu.typ import type { Project } from 'data/projects/project-detail-query' import { IS_PLATFORM } from 'lib/constants' -export const generateRealtimeMenu = ( - project: Project, - flags?: { enableRealtimeSettings: boolean } -): ProductMenuGroup[] => { +export const generateRealtimeMenu = (project: Project): ProductMenuGroup[] => { const ref = project?.ref ?? 'default' - const { enableRealtimeSettings } = flags || {} - const showRealtimeSettings = IS_PLATFORM && enableRealtimeSettings + const showRealtimeSettings = IS_PLATFORM return [ { diff --git a/apps/studio/hooks/ui/useFlag.ts b/apps/studio/hooks/ui/useFlag.ts index 635c54343a482..979334882d3c0 100644 --- a/apps/studio/hooks/ui/useFlag.ts +++ b/apps/studio/hooks/ui/useFlag.ts @@ -44,23 +44,3 @@ export function usePHFlag(name: string) { return flagValue as T } - -export const useIsRealtimeSettingsFFEnabled = () => { - const { data: project } = useSelectedProjectQuery() - - // This flag is used to enable/disable the realtime settings for specific projects. - const approvedProjects = useFlag('isRealtimeSettingsEnabledOnProjects') - // This flag is used to enable/disable the realtime settings for all projects. - // Will override isRealtimeSettingsEnabledOnProjects if enabled - const enableRealtimeSettingsFlag = useFlag('enableRealtimeSettings') - - const isEnabledOnProject = - !!project?.ref && - typeof approvedProjects === 'string' && - (approvedProjects ?? '') - .split(',') - .map((it) => it.trim()) - .includes(project?.ref) - - return enableRealtimeSettingsFlag || isEnabledOnProject -} diff --git a/apps/ui-library/components/command.tsx b/apps/ui-library/components/command.tsx index 1956537604e00..752d33bcc6ad8 100644 --- a/apps/ui-library/components/command.tsx +++ b/apps/ui-library/components/command.tsx @@ -67,7 +67,7 @@ export function Command({ name, highlight }: CommandCopyProps) {
    - $ + $ {commands[manager]}
    diff --git a/packages/common/constants/local-storage.ts b/packages/common/constants/local-storage.ts index 03d597f036654..11e8d32ce6ad6 100644 --- a/packages/common/constants/local-storage.ts +++ b/packages/common/constants/local-storage.ts @@ -13,7 +13,6 @@ export const LOCAL_STORAGE_KEYS = { UI_PREVIEW_INLINE_EDITOR: 'supabase-ui-preview-inline-editor', UI_PREVIEW_UNIFIED_LOGS: 'supabase-ui-preview-unified-logs', UI_ONBOARDING_NEW_PAGE_SHOWN: 'supabase-ui-onboarding-new-page-shown', - UI_PREVIEW_REALTIME_SETTINGS: 'supabase-ui-realtime-settings', UI_PREVIEW_BRANCHING_2_0: 'supabase-ui-branching-2-0', UI_PREVIEW_ADVISOR_RULES: 'supabase-ui-advisor-rules', @@ -109,7 +108,6 @@ const LOCAL_STORAGE_KEYS_ALLOWLIST = [ LOCAL_STORAGE_KEYS.HIDE_PROMO_TOAST, LOCAL_STORAGE_KEYS.BLOG_VIEW, LOCAL_STORAGE_KEYS.AI_ASSISTANT_MCP_OPT_IN, - LOCAL_STORAGE_KEYS.UI_PREVIEW_REALTIME_SETTINGS, LOCAL_STORAGE_KEYS.UI_PREVIEW_BRANCHING_2_0, LOCAL_STORAGE_KEYS.LINTER_SHOW_FOOTER, ] diff --git a/packages/common/enabled-features/enabled-features.json b/packages/common/enabled-features/enabled-features.json index 5b3494a78500d..3221a0c981284 100644 --- a/packages/common/enabled-features/enabled-features.json +++ b/packages/common/enabled-features/enabled-features.json @@ -80,6 +80,8 @@ "project_settings:legacy_jwt_keys": true, "project_settings:log_drains": true, + "quickstarts:hide_nimbus": true, + "reports:all": true, "sdk:csharp": true, diff --git a/packages/common/enabled-features/enabled-features.schema.json b/packages/common/enabled-features/enabled-features.schema.json index 9943b890297ee..cbb99e6bfb782 100644 --- a/packages/common/enabled-features/enabled-features.schema.json +++ b/packages/common/enabled-features/enabled-features.schema.json @@ -269,6 +269,11 @@ "description": "Enable the log drains page in project settings" }, + "quickstarts:hide_nimbus": { + "type": "boolean", + "description": "Whether to show the Nimbus quickstart" + }, + "reports:all": { "type": "boolean", "description": "Enable the project reports page" @@ -362,6 +367,7 @@ "project_connection:show_app_frameworks", "project_connection:show_mobile_frameworks", "project_connection:show_orms", + "quickstarts:hide_nimbus", "reports:all", "sdk:csharp", "sdk:dart",