Files
supabase/apps/studio/components/interfaces/ConnectSheet/ConnectionString.utils.ts
T
Saxon Fletcher d8fadcc1c8 makes it possible to reset password from connect dialog (#46866)
Makes it possible to reset password from the connect sheet. Once reset
the password is shown temporarily in the connection string for copy. The
copy prompt action does not copy the password.

<img width="1043" height="953" alt="image"
src="https://github.com/user-attachments/assets/fe1a33bb-839f-47e3-b07f-7a5fa1df2b8d"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Session Pooler notice for direct connections on IPv4 networks
* Option to show a temporary database password during direct-connection
setup

* **Improvements**
  * New password reset dialog with strength checks and generation
* Connection-copy behavior now redacts temporary passwords and produces
cleaner copy prompts

* **Tests**
* Added tests covering connection-string password insertion/replacement
and copy-prompt behavior
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-12 07:38:18 -06:00

118 lines
3.0 KiB
TypeScript

import type { ConnectionStringMethod } from './Connect.constants'
import type { ConnectionStringPooler } from './Connect.types'
export const DEFAULT_PORT = '5432'
export const PASSWORD_PLACEHOLDER = '[YOUR-PASSWORD]'
export type ConnectionParams = {
host: string
port: string
user: string
database: string
}
export const resolveConnectionString = ({
connectionMethod,
useSharedPooler,
connectionStringPooler,
}: {
connectionMethod: ConnectionStringMethod
useSharedPooler: boolean
connectionStringPooler: ConnectionStringPooler | undefined
}) => {
if (!connectionStringPooler) return ''
if (connectionMethod === 'direct') {
return connectionStringPooler.direct ?? ''
}
if (connectionMethod === 'session') {
return connectionStringPooler.sessionShared ?? ''
}
if (useSharedPooler || !connectionStringPooler.transactionDedicated) {
return connectionStringPooler.transactionShared ?? ''
}
return connectionStringPooler.transactionDedicated ?? ''
}
export const parseConnectionParams = (connectionString: string): ConnectionParams => {
if (!connectionString) {
return {
host: 'hidden',
port: DEFAULT_PORT,
user: 'hidden',
database: 'hidden',
}
}
try {
const parsed = new URL(connectionString)
// The URL parser percent-encodes characters that aren't valid in user-info
// (e.g. brackets in the self-hosted `postgres.[POOLER_TENANT_ID]` placeholder).
// Decode so the displayed string matches the literal we wrote.
const decode = (value: string) => {
try {
return decodeURIComponent(value)
} catch {
return value
}
}
return {
host: parsed.hostname || 'hidden',
port: parsed.port || DEFAULT_PORT,
user: parsed.username ? decode(parsed.username) : 'hidden',
database: parsed.pathname?.replace(/^\//, '') || 'hidden',
}
} catch (error) {
return {
host: 'hidden',
port: DEFAULT_PORT,
user: 'hidden',
database: 'hidden',
}
}
}
export const buildSafeConnectionString = (
connectionString: string,
params: ConnectionParams
): string => {
if (!connectionString) return ''
const search = (() => {
try {
return new URL(connectionString).search
} catch (error) {
return ''
}
})()
return `postgresql://${params.user}:${PASSWORD_PLACEHOLDER}@${params.host}:${params.port}/${params.database}${search}`
}
export const buildConnectionStringWithPassword = (
connectionString: string,
password: string
): string => {
if (!connectionString || !password) return connectionString
const encodedPassword = (() => {
try {
return encodeURIComponent(password)
} catch {
return password
}
})()
return connectionString.split(PASSWORD_PLACEHOLDER).join(encodedPassword)
}
export const buildConnectionParameters = (params: ConnectionParams) => [
{ key: 'host', value: params.host },
{ key: 'port', value: params.port },
{ key: 'database', value: params.database },
{ key: 'user', value: params.user },
]