diff --git a/apps/studio/components/interfaces/Connect/ConnectionIcon.tsx b/apps/studio/components/interfaces/Connect/ConnectionIcon.tsx index 50f1e2ef68daf..b2de0e2079571 100644 --- a/apps/studio/components/interfaces/Connect/ConnectionIcon.tsx +++ b/apps/studio/components/interfaces/Connect/ConnectionIcon.tsx @@ -2,14 +2,23 @@ import { useTheme } from 'next-themes' import Image from 'next/image' import { BASE_PATH } from 'lib/constants' +import { cn } from 'ui' interface ConnectionIconProps { icon: string iconFolder?: string supportsDarkMode?: boolean + size?: number + className?: string } -export const ConnectionIcon = ({ icon, iconFolder, supportsDarkMode }: ConnectionIconProps) => { +export const ConnectionIcon = ({ + icon, + iconFolder, + supportsDarkMode, + size = 14, + className, +}: ConnectionIconProps) => { const { resolvedTheme } = useTheme() const imageFolder = @@ -29,11 +38,11 @@ export const ConnectionIcon = ({ icon, iconFolder, supportsDarkMode }: Connectio return ( {`${icon} ) } diff --git a/apps/studio/components/interfaces/Home/NewProjectPanel/APIKeys.tsx b/apps/studio/components/interfaces/Home/NewProjectPanel/APIKeys.tsx index bf90128392f39..b793e13256ccd 100644 --- a/apps/studio/components/interfaces/Home/NewProjectPanel/APIKeys.tsx +++ b/apps/studio/components/interfaces/Home/NewProjectPanel/APIKeys.tsx @@ -1,69 +1,51 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' import { JwtSecretUpdateStatus } from '@supabase/shared-types/out/events' -import { AlertCircle, Loader } from 'lucide-react' -import Link from 'next/link' -import { useState } from 'react' +import { Loader } from 'lucide-react' import { useParams } from 'common' -import Panel from 'components/ui/Panel' +import { Connect } from 'components/interfaces/Connect/Connect' +import { ConnectionIcon } from 'components/interfaces/Connect/ConnectionIcon' +import { AlertError } from 'components/ui/AlertError' +import { InlineLink } from 'components/ui/InlineLink' import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query' +import { useLegacyAPIKeysStatusQuery } from 'data/api-keys/legacy-api-keys-status-query' import { useJwtSecretUpdatingStatusQuery } from 'data/config/jwt-secret-updating-status-query' import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query' import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions' -import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' -import { Input, SimpleCodeBlock } from 'ui' - -const generateInitSnippet = (endpoint: string) => ({ - js: ` -import { createClient } from '@supabase/supabase-js' - -const supabaseUrl = '${endpoint}' -const supabaseKey = process.env.SUPABASE_KEY -const supabase = createClient(supabaseUrl, supabaseKey)`, - dart: ` -const supabaseUrl = '${endpoint}'; -const supabaseKey = String.fromEnvironment('SUPABASE_KEY'); - -Future main() async { - await Supabase.initialize(url: supabaseUrl, anonKey: supabaseKey); - runApp(MyApp()); -}`, -}) +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from 'ui' +import { Admonition, GenericSkeletonLoader } from 'ui-patterns' +import { Input } from 'ui-patterns/DataInputs/Input' +import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' export const APIKeys = () => { const { ref: projectRef } = useParams() - - const { - projectConnectionJavascriptExample: javascriptExampleEnabled, - projectConnectionDartExample: dartExampleEnabled, - } = useIsFeatureEnabled([ - 'project_connection:javascript_example', - 'project_connection:dart_example', - ]) - - const availableLanguages = [ - { - name: javascriptExampleEnabled ? 'Javascript' : 'Typescript', - key: 'js', - }, - ...(dartExampleEnabled ? [{ name: 'Dart', key: 'dart' }] : []), - ] - const [selectedLanguage, setSelectedLanguage] = useState(availableLanguages[0]) + const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*') const { data: settings, + error: projectSettingsError, isError: isProjectSettingsError, isPending: isProjectSettingsLoading, } = useProjectSettingsV2Query({ projectRef }) - const { can: canReadAPIKeys } = useAsyncCheckPermissions(PermissionAction.SECRETS_READ, '*') - const { data: apiKeys } = useAPIKeysQuery({ projectRef }, { enabled: canReadAPIKeys }) - const { anonKey, serviceKey } = getKeys(apiKeys) + const { data: legacyAPIKeysStatusData, isPending: isLoadingAPIKeysStatus } = + useLegacyAPIKeysStatusQuery({ projectRef }, { enabled: canReadAPIKeys }) - const isApiKeysEmpty = !anonKey && !serviceKey + const { + data: apiKeys, + error: errorAPIKeys, + isError: isErrorAPIKeys, + isPending: isLoadingAPIKeys, + } = useAPIKeysQuery({ projectRef }, { enabled: canReadAPIKeys }) + const { anonKey, serviceKey, publishableKey, secretKey } = getKeys(apiKeys) + + const hasNewAPIKeys = !!publishableKey && !!secretKey + const isLegacyKeysEnabled = legacyAPIKeysStatusData?.enabled ?? false + const isApiKeysEmpty = !hasNewAPIKeys && !anonKey && !serviceKey const { data, + error: jwtSecretUpdateError, isError: isJwtSecretUpdateStatusError, isPending: isJwtSecretUpdateStatusLoading, } = useJwtSecretUpdatingStatusQuery( @@ -71,6 +53,9 @@ export const APIKeys = () => { { enabled: !isProjectSettingsLoading && isApiKeysEmpty } ) + const isLoading = isLoadingAPIKeys || isLoadingAPIKeysStatus || isProjectSettingsLoading + const isError = isErrorAPIKeys || isProjectSettingsError || isJwtSecretUpdateStatusError + // Only show JWT loading state if the query is actually enabled const showJwtLoading = isJwtSecretUpdateStatusLoading && !isProjectSettingsLoading && isApiKeysEmpty @@ -84,136 +69,150 @@ export const APIKeys = () => { const endpoint = settings?.app_config?.endpoint const apiUrl = `${protocol}://${endpoint ?? '-'}` - const clientInitSnippet: any = generateInitSnippet(apiUrl) - const selectedLanguageSnippet = !!selectedLanguage - ? clientInitSnippet[selectedLanguage.key] - : 'No snippet available' - return ( - -
Project API
-

- Your API is secured behind an API gateway which requires an API Key for every request. -
- You can use the parameters below to use Supabase client libraries. -

- - } - > - {isProjectSettingsError || isJwtSecretUpdateStatusError ? ( -
- -

- {isProjectSettingsError ? 'Failed to retrieve API keys' : 'Failed to update JWT secret'} -

-
- ) : isProjectSettingsLoading ? ( -
- -

Retrieving API keys

-
- ) : isApiKeysEmpty ? ( -
- -

Retrieving API keys

-
+ + + Project API + + Your API is secured behind an API gateway which requires an API Key for every request. +
+ You can use the parameters below to use Supabase client libraries. +
+
+ + {isLoading ? ( + + + + ) : isError ? ( + ) : showJwtLoading ? ( -
- -

JWT secret is being updated

-
+ +
+ +

JWT secret is being updated

+
+
+ ) : !isLegacyKeysEnabled && !hasNewAPIKeys ? ( + + Create a set of API keys from your{' '} + + project settings + {' '} + to connect to your project + + } + /> ) : ( <> - - + - - - + + + + + + -

API Key

-
- {anonKey?.name} - public + hasNewAPIKeys ? ( + 'Publishable API Key' + ) : ( +
+

API Key

+
+ {anonKey?.name} + public +
-
+ ) } - copy={canReadAPIKeys && isNotUpdatingJwtSecret} - reveal={anonKey?.name !== 'anon' && canReadAPIKeys && isNotUpdatingJwtSecret} - value={ - !canReadAPIKeys - ? 'You need additional permissions to view API keys' - : jwtSecretUpdateStatus === JwtSecretUpdateStatus.Failed - ? 'JWT secret update failed, new API key may have issues' - : jwtSecretUpdateStatus === JwtSecretUpdateStatus.Updating - ? 'Updating JWT secret...' - : anonKey?.api_key - } - onChange={() => {}} - descriptionText={ + description={

This key is safe to use in a browser if you have enabled Row Level Security (RLS) - for your tables and configured policies. You may also use the service key which - can be found{' '} - here - {' '} + {' '} to bypass RLS.

} + > + +
+
+ + +
- - {availableLanguages.length > 0 && ( -
-
- {availableLanguages.map((language) => { - const isSelected = selectedLanguage.key === language.key - return ( -
setSelectedLanguage(language)} - > - {language.name} -
- ) - })} -
-
- - {selectedLanguageSnippet} - +
+
+ + + + +
+

Choose your preferred framework

+

+ Connect to your project from a variety of frameworks, ORMs, an MCP server, or even + directly via connection string. +

+
- )} + )} - + ) } diff --git a/apps/studio/components/interfaces/Home/NewProjectPanel/NewProjectPanel.tsx b/apps/studio/components/interfaces/Home/NewProjectPanel/NewProjectPanel.tsx index 1cee26d71212e..55b77743b2155 100644 --- a/apps/studio/components/interfaces/Home/NewProjectPanel/NewProjectPanel.tsx +++ b/apps/studio/components/interfaces/Home/NewProjectPanel/NewProjectPanel.tsx @@ -1,7 +1,9 @@ -import { ExternalLink } from 'lucide-react' +import { ExternalLink, Settings } from 'lucide-react' import Link from 'next/link' import { useParams } from 'common' +import { DocsButton } from 'components/ui/DocsButton' +import { InlineLink } from 'components/ui/InlineLink' import Panel from 'components/ui/Panel' import { EditorIndexPageLink } from 'data/prefetchers/project.$ref.editor' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' @@ -37,7 +39,7 @@ export const NewProjectPanel = () => {
-

Get started by building out your database

+

Build out your database

Start building your app by creating tables and inserting data. Our Table Editor makes Postgres as easy to use as a spreadsheet, but there's also our SQL Editor if @@ -211,28 +213,18 @@ export const NewProjectPanel = () => {

-

Connecting to your new project

-

+

Connect to your project

+

Interact with your database through the{' '} - - Supabase client libraries - {' '} - with your API keys. -

-

- More information about your project's keys can be found in your project's API - settings. + Supabase client libraries and + your API keys.

- - +
diff --git a/apps/studio/components/layouts/ProjectLayout/BuildingState.tsx b/apps/studio/components/layouts/ProjectLayout/BuildingState.tsx index 2287fe84dd601..f869fa1e7adc6 100644 --- a/apps/studio/components/layouts/ProjectLayout/BuildingState.tsx +++ b/apps/studio/components/layouts/ProjectLayout/BuildingState.tsx @@ -6,9 +6,8 @@ import { useParams } from 'common' import { ClientLibrary } from 'components/interfaces/Home/ClientLibrary' import { ExampleProject } from 'components/interfaces/Home/ExampleProject' import { EXAMPLE_PROJECTS } from 'components/interfaces/Home/Home.constants' +import { APIKeys } from 'components/interfaces/Home/NewProjectPanel/APIKeys' import { SupportLink } from 'components/interfaces/Support/SupportLink' -import { DisplayApiSettings } from 'components/ui/ProjectSettings/DisplayApiSettings' -import { DisplayConfigSettings } from 'components/ui/ProjectSettings/DisplayConfigSettings' import { useInvalidateProjectsInfiniteQuery } from 'data/projects/org-projects-infinite-query' import { useInvalidateProjectDetailsQuery } from 'data/projects/project-detail-query' import { useProjectStatusQuery } from 'data/projects/project-status-query' @@ -135,8 +134,7 @@ const BuildingState = () => {
- - +
diff --git a/apps/studio/data/config/jwt-secret-updating-status-query.ts b/apps/studio/data/config/jwt-secret-updating-status-query.ts index c28dd8863e310..f5f429ffefdd6 100644 --- a/apps/studio/data/config/jwt-secret-updating-status-query.ts +++ b/apps/studio/data/config/jwt-secret-updating-status-query.ts @@ -3,7 +3,7 @@ import { useQuery, useQueryClient } from '@tanstack/react-query' import { useEffect } from 'react' import { get, handleError } from 'data/fetchers' -import { UseCustomQueryOptions } from 'types' +import { ResponseError, UseCustomQueryOptions } from 'types' import { configKeys } from './keys' export type JwtSecretUpdatingStatusVariables = { @@ -45,7 +45,7 @@ export async function getJwtSecretUpdatingStatus( } export type JwtSecretUpdatingStatusData = Awaited> -export type JwtSecretUpdatingStatusError = unknown +export type JwtSecretUpdatingStatusError = ResponseError export const useJwtSecretUpdatingStatusQuery = ( { projectRef }: JwtSecretUpdatingStatusVariables, diff --git a/docker/CHANGELOG.md b/docker/CHANGELOG.md index dfd45ac9b001e..9e6cf8806020c 100644 --- a/docker/CHANGELOG.md +++ b/docker/CHANGELOG.md @@ -13,7 +13,35 @@ Check updates for each service to learn more. ## Unreleased -- Updated self-hosting installation and configuration guide - PR [#40901](https://github.com/supabase/supabase/pull/40901) +--- + +## [2025-12-18] + +### Documentation +- Updated self-hosting installation and configuration guide - PR [#40901](https://github.com/supabase/supabase/pull/40901), PR [#41438](https://github.com/supabase/supabase/pull/41438) + +### Utils +- Added `generate-keys.sh` - PR [#41363](https://github.com/supabase/supabase/pull/41363) +- Added `db-passwd.sh` - PR [#41432](https://github.com/supabase/supabase/pull/41432) +- Changed `reset.sh` to POSIX and added more checks - PR [#41361](https://github.com/supabase/supabase/pull/41361) + +### Studio +- Updated to `2025.12.17-sha-43f4f7f` +- ⚠️ Fixed additional potential issues related to [React2Shell](https://vercel.com/kb/bulletin/react2shell) +- Fixed an issue with the Users page not being updated on changes - PR [#41254](https://github.com/supabase/supabase/pull/41254) + +### MCP Server +- Updated to `v0.5.10` - [Release](https://github.com/supabase-community/supabase-mcp/releases/tag/v0.5.10) + +### Auth +- Updated to `v2.184.0` - [Changelog](https://github.com/supabase/auth/blob/master/CHANGELOG.md) | [Release](https://github.com/supabase/auth/releases/tag/v2.184.0) + +### Postgres Meta +- Updated to `v0.95.1` - [Release](https://github.com/supabase/postgres-meta/releases/tag/v0.95.1) + +### Analytics (Logflare) +- Updated to `v1.27.0` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.27.0) +- Fixed multiple issues, including a race condition --- @@ -23,7 +51,11 @@ Check updates for each service to learn more. - Updated to `2025.12.09-sha-434634f` - ⚠️ Fixed potential issues related to [React2Shell](https://vercel.com/kb/bulletin/react2shell) -- ⚠️ Changed MCP tool `get_anon_key` to `get_publishable_keys` - [MCP Server Releases](https://github.com/supabase-community/supabase-mcp/releases) + +### MCP Server + +- Updated to `v0.5.9` - [Release](https://github.com/supabase-community/supabase-mcp/releases/tag/v0.5.9) +- ⚠️ Changed MCP tool `get_anon_key` to `get_publishable_keys` ### PostgREST @@ -42,9 +74,9 @@ Check updates for each service to learn more. - Updated to `v1.69.28` - [Release](https://github.com/supabase/edge-runtime/releases/tag/v1.69.28) -### Logflare +### Analytics (Logflare) -- Updated to `1.26.25` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.26.25) +- Updated to `v1.26.25` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.26.25) --- @@ -80,7 +112,7 @@ Check updates for each service to learn more. - Updated to `2025.11.24-sha-d990ae8` - [Dashboard updates](https://github.com/orgs/supabase/discussions/40734) - Fixed Queues configuration UI and added [documentation for exposed queue schema](https://supabase.com/docs/guides/queues/expose-self-hosted-queues) - PR [#40078](https://github.com/supabase/supabase/pull/40078) - Fixed parameterized SQL queries in MCP tools - PR [#40499](https://github.com/supabase/supabase/pull/40499) -- Fixed Studio showing paid options for log drains - [PR #40510](https://github.com/supabase/supabase/pull/40510) +- Fixed Studio showing paid options for log drains - PR [#40510](https://github.com/supabase/supabase/pull/40510) - Fixed AI Assistant authentication - PR [#40654](https://github.com/supabase/supabase/pull/40654) ### Auth diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 302e884e8a481..6e9b1caf75af5 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -11,7 +11,7 @@ services: studio: container_name: supabase-studio - image: supabase/studio:2025.12.09-sha-434634f + image: supabase/studio:2025.12.17-sha-43f4f7f restart: unless-stopped healthcheck: test: @@ -90,7 +90,7 @@ services: auth: container_name: supabase-auth - image: supabase/gotrue:v2.183.0 + image: supabase/gotrue:v2.184.0 restart: unless-stopped healthcheck: test: @@ -301,7 +301,7 @@ services: meta: container_name: supabase-meta - image: supabase/postgres-meta:v0.93.1 + image: supabase/postgres-meta:v0.95.1 restart: unless-stopped depends_on: db: @@ -344,7 +344,7 @@ services: analytics: container_name: supabase-analytics - image: supabase/logflare:1.26.25 + image: supabase/logflare:1.27.0 restart: unless-stopped ports: - 4000:4000 diff --git a/docker/versions.md b/docker/versions.md index 6c44818674977..a26aef02f5e0a 100644 --- a/docker/versions.md +++ b/docker/versions.md @@ -1,5 +1,11 @@ # Docker Image Versions +## 2025-12-18 +- supabase/studio:2025.12.17-sha-43f4f7f (prev supabase/studio:2025.12.09-sha-434634f) +- supabase/gotrue:v2.184.0 (prev supabase/gotrue:v2.183.0) +- supabase/postgres-meta:v0.95.1 (prev supabase/postgres-meta:v0.93.1) +- supabase/logflare:1.27.0 (prev supabase/logflare:1.26.25) + ## 2025-12-10 - supabase/studio:2025.12.09-sha-434634f (prev supabase/studio:2025.11.26-sha-8f096b5) - postgrest/postgrest:v14.1 (prev postgrest/postgrest:v13.0.7) diff --git a/packages/common/enabled-features/enabled-features.json b/packages/common/enabled-features/enabled-features.json index 38902316e0cd1..c862597b29c40 100644 --- a/packages/common/enabled-features/enabled-features.json +++ b/packages/common/enabled-features/enabled-features.json @@ -90,8 +90,6 @@ "profile:show_analytics_and_marketing": true, "profile:show_account_deletion": true, - "project_connection:javascript_example": true, - "project_connection:dart_example": true, "project_connection:show_app_frameworks": true, "project_connection:show_mobile_frameworks": true, "project_connection:show_orms": true, diff --git a/packages/common/enabled-features/enabled-features.schema.json b/packages/common/enabled-features/enabled-features.schema.json index 2f6462306169a..bde7a6887c0ac 100644 --- a/packages/common/enabled-features/enabled-features.schema.json +++ b/packages/common/enabled-features/enabled-features.schema.json @@ -309,14 +309,6 @@ "description": "Shows the account deletion section in account preferences" }, - "project_connection:javascript_example": { - "type": "boolean", - "description": "Show the javascript example in the project connection settings. If off, it'll show up as Typescript example instead (both are the same code example)" - }, - "project_connection:dart_example": { - "type": "boolean", - "description": "Show the dart example in the project connection settings" - }, "project_connection:show_app_frameworks": { "type": "boolean", "description": "Show the app frameworks tab in the connect modal" @@ -510,8 +502,6 @@ "project_settings:log_drains", "project_settings:restart_project", "project_settings:database_upgrades", - "project_connection:javascript_example", - "project_connection:dart_example", "project_connection:show_app_frameworks", "project_connection:show_mobile_frameworks", "project_connection:show_orms",