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
77 changes: 77 additions & 0 deletions apps/studio/components/interfaces/Connect/ApiKeysTabContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import { AlertCircle, ExternalLink, Loader2 } from 'lucide-react'
import Link from 'next/link'
import type { ReactNode } from 'react'

import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { Button } from 'ui'
import { Input } from 'ui-patterns/DataInputs/Input'
import type { projectKeys } from './Connect.types'

function KeyRow({ label, value }: { label: ReactNode; value: string }) {
return (
<div className="flex flex-col gap-5 lg:grid lg:grid-cols-12">
<div className="col-span-4">
<h3 className="text-sm text-foreground">{label}</h3>
</div>
<div className="col-span-8">
<Input readOnly copy className="font-mono" value={value} />
</div>
</div>
)
}

export function ApiKeysTabContent({ projectKeys }: { projectKeys: projectKeys }) {
const { ref: projectRef } = useParams()

const { isLoading: isLoadingPermissions, can: canReadAPIKeys } = useAsyncCheckPermissions(
PermissionAction.SECRETS_READ,
'*'
)

if (isLoadingPermissions) {
return (
<div className="flex items-center justify-center py-8 space-x-2">
<Loader2 className="animate-spin" size={16} strokeWidth={1.5} />
<p className="text-sm text-foreground-light">Retrieving API keys</p>
</div>
)
}

if (!canReadAPIKeys) {
return (
<div className="flex items-center py-8 space-x-2">
<AlertCircle size={16} strokeWidth={1.5} />
<p className="text-sm text-foreground-light">You don't have permission to view API keys.</p>
</div>
)
}

return (
<div className="flex flex-col gap-8">
<KeyRow label="Project URL" value={projectKeys.apiUrl ?? ''} />

<KeyRow label="Publishable Key" value={projectKeys.publishableKey ?? ''} />

<KeyRow
label={
<>
Anon Key <span className="text-foreground-lighter font-normal">(Legacy)</span>
</>
}
value={projectKeys.anonKey ?? ''}
/>

{/* Footer */}
<div className="gap-5 lg:grid lg:grid-cols-12">
<div className="col-start-5 col-span-8 pl-2 flex items-center justify-between">
<p className="text-xs text-foreground-lighter">For secret keys, see API settings.</p>
<Button asChild type="default" icon={<ExternalLink size={14} />}>
<Link href={`/project/${projectRef}/settings/api-keys`}>API settings</Link>
</Button>
</div>
</div>
</div>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ export const CONNECTION_TYPES = [
{ key: 'frameworks', label: 'App Frameworks', obj: FRAMEWORKS },
{ key: 'mobiles', label: 'Mobile Frameworks', obj: MOBILES },
{ key: 'orms', label: 'ORMs', obj: ORMS },
{ key: 'api-keys', label: 'API Keys', obj: [] },
{ key: 'mcp', label: 'MCP', obj: [] },
]

Expand Down
13 changes: 13 additions & 0 deletions apps/studio/components/interfaces/Connect/Connect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ExternalLink, Plug } from 'lucide-react'
import { parseAsBoolean, parseAsString, useQueryState } from 'nuqs'
import { useEffect, useMemo, useState } from 'react'

import { ApiKeysTabContent } from 'components/interfaces/Connect/ApiKeysTabContent'
import { DatabaseConnectionString } from 'components/interfaces/Connect/DatabaseConnectionString'
import { McpTabContent } from 'components/interfaces/Connect/McpTabContent'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
Expand Down Expand Up @@ -412,6 +413,18 @@ export const Connect = () => {
)
}

if (type.key === 'api-keys') {
return (
<TabsContent_Shadcn_
key="api-keys"
value="api-keys"
className={cn(DIALOG_PADDING_X, DIALOG_PADDING_Y, '!mt-0')}
>
<ApiKeysTabContent projectKeys={projectKeys} />
</TabsContent_Shadcn_>
)
}

const connectionTabMap: Record<
string,
'App Frameworks' | 'Mobile Frameworks' | 'ORMs'
Expand Down
3 changes: 2 additions & 1 deletion docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,9 @@ services:
"curl -sSfL --head -o /dev/null -H \"Authorization: Bearer ${ANON_KEY}\" http://localhost:4000/api/tenants/realtime-dev/health"
]
timeout: 5s
interval: 5s
interval: 30s
retries: 3
start_period: 10s
environment:
PORT: 4000
DB_HOST: ${POSTGRES_HOST}
Expand Down
4 changes: 2 additions & 2 deletions docker/volumes/api/kong.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ acls:
###
basicauth_credentials:
- consumer: DASHBOARD
username: $DASHBOARD_USERNAME
password: $DASHBOARD_PASSWORD
username: '$DASHBOARD_USERNAME'
password: '$DASHBOARD_PASSWORD'

###
### API Routes
Expand Down
8 changes: 7 additions & 1 deletion docker/volumes/logs/vector.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,17 @@ transforms:
.timestamp = to_timestamp!(parsed.time)
.metadata.host = .project
}
# Filter out healthcheck logs from Realtime
realtime_logs_filtered:
type: filter
inputs:
- router.realtime
condition: '!contains(string!(.event_message), "/health")'
# Realtime logs are structured so we parse the severity level using regex (ignore time because it has no date)
realtime_logs:
type: remap
inputs:
- router.realtime
- realtime_logs_filtered
source: |-
.metadata.project = del(.project)
.metadata.external_id = .metadata.project
Expand Down
Loading