Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 27 additions & 7 deletions apps/design-system/registry/default/block/chart-composed-basic.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,38 @@ export default function ComposedChartBasic() {
},
]

const data = Array.from({ length: 46 }, (_, i) => {
const data = Array.from({ length: 40 }, (_, i) => {
const date = new Date()
date.setMinutes(date.getMinutes() - i * 5) // Each point 5 minutes apart
date.setMinutes(date.getMinutes() - i * 3) // Each point 3 minutes apart

const progress = i / 40
const standard_score = Math.floor(55 + progress * 55 + (Math.random() - 0.5) * 12)
const performance = Math.floor(35 + progress * 35 + (Math.random() - 0.5) * 10)
const efficiency = Math.floor(25 + progress * 25 + (Math.random() - 0.5) * 12)

return {
timestamp: date.toISOString(),
standard_score: Math.floor(Math.random() * 100),
standard_score: Math.max(0, Math.min(100, standard_score)),
performance: Math.max(0, Math.min(100, performance)),
efficiency: Math.max(0, Math.min(100, efficiency)),
}
}).reverse()

const chartConfig = {
standard_score: {
label: 'Standard Score',
color: 'hsl(var(--brand-default))',
},
performance: {
label: 'Performance',
color: 'hsl(var(--chart-2))',
},
efficiency: {
label: 'Efficiency',
color: 'hsl(var(--chart-5))',
},
}

useEffect(() => {
setTimeout(() => {
setIsLoading(false)
Expand All @@ -50,10 +72,8 @@ export default function ComposedChartBasic() {
<ChartCard>
<ChartHeader>
<ChartTitle tooltip="This is a tooltip">Standard Bar Chart</ChartTitle>

<ChartActions actions={actions} />
</ChartHeader>

<ChartContent
isEmpty={data.length === 0}
emptyState={
Expand Down Expand Up @@ -86,10 +106,8 @@ export default function ComposedChartBasic() {
<ChartCard>
<ChartHeader>
<ChartTitle tooltip="This is a tooltip">Standard Line Chart</ChartTitle>

<ChartActions actions={actions} />
</ChartHeader>

<ChartContent
isEmpty={data.length === 0}
emptyState={
Expand All @@ -105,6 +123,8 @@ export default function ComposedChartBasic() {
<ChartLine
data={data}
dataKey="standard_score"
dataKeys={['standard_score', 'performance', 'efficiency']}
config={chartConfig}
showGrid={true}
showYAxis={true}
YAxisProps={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ This error typically happens when the database is overloaded and causing an outa

- Check your database health in [Database reports](/dashboard/project/_/observability/database).
- If needed, increase resources in [Compute and Disk](/dashboard/project/_/settings/compute-and-disk).
- Alternatively, you can restart the database in [Project Settings](/dashboard/project/_/settings/general) but this may be only a temporary fix if the project is undersized / unoptimized.

Review the appropriate guides based on your scenario:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,45 @@ keywords = [ "jwt", "secrets", "key", "security" ]
database_id = "caa72a34-696c-47c6-8976-e50cf7fb396e"
---

<Admonition type="deprecation">

This troubleshooting guide is about rotating **Legacy anon, service_role API keys**. We are deprecating Legacy
, and recommend migrating to New API keys. To learn more about API
keys, refer to [the API documentation](/docs/guides/api/api-keys).

</Admonition>

<Admonition type="tip">

Once the JWT secret is regenerated, all current API secrets will be immediately invalidated, and
all connections using them will be severed. You will need to deploy the new secrets for
connections to begin working again. You can avoid downtime by migrating to new API Keys.

</Admonition>

Have you ever accidentally committed a service key to a public repo? Or maybe rotating keys is just something you regularly do for security compliance.
Whatever the reason, here's how to rotate the keys for your Supabase project.

1. Go to the [API Settings page](/dashboard/project/_/settings/api-keys/new) in the Supabase Dashboard
2. Find the JWT Secrets section
If you haven’t migrated to asymmetric JWT signing keys:

{/* supa-mdx-lint-disable Rule004ExcludeWords */}

1. Go to [**Project Settings** → **JWT Keys**](/dashboard/project/_/settings/jwt) in the Supabase Dashboard
2. Navigate to the **Legacy JWT Secret** tab
3. Click on **Change Legacy Secret**
- Click on **Generate a random secret** to let Supabase decide the JWT secret.
- Click on **Create my own secret** to enter a custom JWT secret
4. You will see a Confirmation dialog. Read it through and confirm to proceed.

<img
width="1072"
alt="Screenshot 2023-12-27 at 08 39 41"
src="https://github.com/supabase/supabase/assets/1923424/bdcab8a2-7007-496c-a845-d331ee883a0a"
/>
If you have migrated to new symmetric JWT signing keys:

3. Click the `Generate new secret` button and choose either a random secret, or custom if you'd like to supply one of your own.
4. NOTE: Once regenerated, all current API secrets will be immediately invalidated, and all connections using them will be severed. You will need to deploy the new secrets for connections to begin working again.
5. Confirm the changes in the warning that pops up by clicking `Generate New Secret` again.
1. Go to [**Project Settings** → **JWT Keys**](/dashboard/project/_/settings/jwt)s in the Supabase Dashboard
2. Navigate to the **JWT Signing Keys** tab.
3. Click on Rotate Keys. This will move the current key to “Previously used keys”
4. Select the three-dot icon (action icon) of your previously used key and click “Revoke”. If you do not “Revoke” the key, older keys will still be valid.

<img
width="517"
alt="Screenshot 2023-12-27 at 08 39 59"
src="https://github.com/supabase/supabase/assets/1923424/1f5cf876-ff65-41f0-b92e-37826773041c"
/>
## Further readings

6. After confirming, the secret will be generated, and Supabase will start rolling that out across our services. Postgres will restart, the API gateways will be updated, etc. Once the process is complete, you will be able to see your new JWT secret as well as the new anon and service keys.
- [How to rotate the service role key](/docs/guides/api/api-keys#i-am-using-jwt-based-anon-key-in-a-mobile-desktop-or-cli-application-and-need-to-rotate-my-servicerole-jwt-secret)
- [What to do if a secret key or service_role has been leaked or compromised?](/docs/guides/api/api-keys#what-to-do-if-a-secret-key-or-servicerole-has-been-leaked-or-compromised)
- [JWT Signing Keys](/docs/guides/auth/signing-keys)
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { useApiUrlCommand } from './ApiUrl'
import { useProjectSwitchCommand, useConfigureOrganizationCommand } from './OrgProjectSwitcher'
import { useSupportCommands } from './Support'
import { orderCommandSectionsByPriority } from './ordering'
import { useContextSearchCommands } from './ContextSearchCommands'
import { useCreateCommands } from './CreateCommands'

export default function StudioCommandMenu() {
Expand All @@ -40,6 +41,7 @@ export default function StudioCommandMenu() {
useChangelogCommand({ enabled: IS_PLATFORM })
useThemeSwitcherCommands()
useCreateCommands()
useContextSearchCommands()

return (
<CommandMenu>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
'use client'

import { useMemo } from 'react'
import { Database } from 'lucide-react'
import { Auth, EdgeFunctions, Storage } from 'icons'
import type { ICommand } from 'ui-patterns/CommandMenu'
import {
CommandHeader,
CommandInput,
CommandWrapper,
PageType,
useRegisterCommands,
useRegisterPage,
useSetPage,
useQuery,
} from 'ui-patterns/CommandMenu'
import { COMMAND_MENU_SECTIONS } from './CommandMenu.utils'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { orderCommandSectionsByPriority } from './ordering'
import { ContextSearchResults } from './ContextSearchResults'
import { useFlag, IS_PLATFORM } from 'common'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
import type { SearchContextValue } from './SearchContext.types'

interface SearchContextOption {
value: SearchContextValue
label: string
pageName: string
placeholder: string
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>
}

const SEARCH_CONTEXT_OPTIONS: SearchContextOption[] = [
{
value: 'database-tables',
label: 'Database Tables',
pageName: 'Search Database Tables',
placeholder: 'Search database tables...',
icon: Database,
},
{
value: 'auth-policies',
label: 'RLS Policies',
pageName: 'Search RLS Policies',
placeholder: 'Search rls policies...',
icon: Auth,
},
{
value: 'edge-functions',
label: 'Edge Functions',
pageName: 'Search Edge Functions',
placeholder: 'Search edge functions...',
icon: EdgeFunctions,
},
{
value: 'storage',
label: 'Storage',
pageName: 'Search Storage',
placeholder: 'Search buckets...',
icon: Storage,
},
]

function ContextSearchPage({
context,
placeholder,
}: {
context: SearchContextValue
placeholder: string
}) {
const query = useQuery()

return (
<CommandWrapper>
<CommandHeader>
<CommandInput placeholder={placeholder} />
</CommandHeader>
<ContextSearchResults context={context} query={query} />
</CommandWrapper>
)
}

export function useContextSearchCommands() {
const enableSearchEntitiesCommandMenu = useFlag('enableSearchEntitiesCommandMenu')
const { data: project } = useSelectedProjectQuery()
const setPage = useSetPage()

const {
projectAuthAll: authEnabled,
projectEdgeFunctionAll: edgeFunctionsEnabled,
projectStorageAll: storageEnabled,
} = useIsFeatureEnabled(['project_auth:all', 'project_edge_function:all', 'project_storage:all'])

const pageDefinitions = [
{ title: 'Search Database Tables', context: 'database-tables' as const },
{ title: 'Search RLS Policies', context: 'auth-policies' as const },
{ title: 'Search Edge Functions', context: 'edge-functions' as const },
{ title: 'Search Storage', context: 'storage' as const },
]

// Register pages - pageDefinitions is constant, so hooks are called in consistent order
for (const { title, context } of pageDefinitions) {
const placeholder =
SEARCH_CONTEXT_OPTIONS.find((opt) => opt.value === context)?.placeholder ?? ''
// eslint-disable-next-line react-hooks/rules-of-hooks
useRegisterPage(title, {
type: PageType.Component,
component: () => <ContextSearchPage context={context} placeholder={placeholder} />,
})
}

const contextCommands = useMemo(() => {
return SEARCH_CONTEXT_OPTIONS.filter((option) => {
let isFeatureEnabled = false
switch (option.value) {
case 'database-tables':
isFeatureEnabled = true
break
case 'auth-policies':
isFeatureEnabled = authEnabled
break
case 'edge-functions':
isFeatureEnabled = edgeFunctionsEnabled
break
case 'storage':
isFeatureEnabled = storageEnabled
break
}

if (!isFeatureEnabled) return false

// If self-hosted, show if feature is enabled
if (!IS_PLATFORM) {
return true
}

// only show when inside a project if not self-hosted
return !!project
}).map((option) => ({
id: `search-${option.value}`,
name: `Search ${option.label}...`,
action: () => setPage(option.pageName),
icon: () => <option.icon className="h-4 w-4" strokeWidth={1.5} />,
})) as ICommand[]
}, [setPage, authEnabled, edgeFunctionsEnabled, storageEnabled, project])

useRegisterCommands(COMMAND_MENU_SECTIONS.QUERY, contextCommands, {
orderSection: orderCommandSectionsByPriority,
sectionMeta: { priority: 3 },
enabled: !IS_PLATFORM || (enableSearchEntitiesCommandMenu && !!project),
})
}
Loading
Loading