import {
optionalString,
requireString,
type SupabaseAuthInput,
} from './types.ts'
const ORIGIN = 'https://kody.codes'
const PAT_GENERATE_URL = 'https://supabase.com/dashboard/account/tokens'
const API_KEYS_URL = 'https://supabase.com/dashboard/project/_/settings/api-keys/'
const MANAGEMENT_HOST = 'api.supabase.com'
export const DEFAULT_PAT_SECRET = 'supabasePat'
export const DEFAULT_SERVICE_ROLE_SECRET = 'supabaseServiceRoleKey'
export function projectHost(projectRef: string): string {
return `${projectRef}.supabase.co`
}
export function projectBaseUrl(projectRef: string): string {
return `https://${projectHost(projectRef)}`
}
export function encodeQuery(params: Record<string, string>): string {
return Object.entries(params)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
.join('&')
}
export function patSetupUrl(secretName = DEFAULT_PAT_SECRET): string {
return `${ORIGIN}/account/secrets/new?${encodeQuery({
name: secretName,
description:
'Supabase personal access token for the Management API (projects, orgs, generic /v1 paths)',
allowedHosts: MANAGEMENT_HOST,
scope: 'user',
})}`
}
export function serviceRoleSetupUrl(input: {
secretName?: string
projectRef?: string
projectHost?: string
} = {}): string {
const secretName = input.secretName ?? DEFAULT_SERVICE_ROLE_SECRET
const host = input.projectHost ?? (input.projectRef ? projectHost(input.projectRef) : undefined)
const params: Record<string, string> = {
name: secretName,
description:
'Supabase project service role or secret key for Auth admin, PostgREST, and Storage',
scope: 'user',
}
if (host) params.allowedHosts = host
return `${ORIGIN}/account/secrets/new?${encodeQuery(params)}`
}
export function setupUrls(input: SupabaseAuthInput = {}) {
const names = resolveSecretNames(input)
const host = input.projectUrl
? new URL(input.projectUrl).host
: input.projectRef
? projectHost(input.projectRef)
: undefined
return {
patGenerateUrl: PAT_GENERATE_URL,
apiKeysUrl: API_KEYS_URL,
patSetupUrl: patSetupUrl(names.patSecretName),
serviceRoleSetupUrl: serviceRoleSetupUrl({
secretName: names.serviceRoleSecretName,
projectHost: host,
projectRef: input.projectRef,
}),
managementHost: MANAGEMENT_HOST,
projectHost: host,
}
}
export function resolveAccountPurpose(account?: string): string | undefined {
const trimmed = account?.trim()
if (!trimmed || trimmed === 'default' || trimmed === 'supabase') return undefined
if (trimmed.startsWith('supabase-')) return trimmed.slice('supabase-'.length)
return trimmed
}
export function resolveSecretNames(input: SupabaseAuthInput): {
patSecretName: string
serviceRoleSecretName: string
purpose?: string
} {
const purpose = resolveAccountPurpose(input.account)
const suffix = purpose ? `-${purpose}` : ''
return {
purpose,
patSecretName:
optionalString(input.patSecretName, 'patSecretName') ??
optionalString(input.secretName, 'secretName') ??
`${DEFAULT_PAT_SECRET}${suffix}`,
serviceRoleSecretName:
optionalString(input.serviceRoleSecretName, 'serviceRoleSecretName') ??
`${DEFAULT_SERVICE_ROLE_SECRET}${suffix}`,
}
}
export function projectAuth(input: SupabaseAuthInput): SupabaseAuthInput {
return {
account: input.account,
projectRef: input.projectRef,
projectUrl: input.projectUrl,
patSecretName: input.patSecretName,
serviceRoleSecretName: input.serviceRoleSecretName ?? input.secretName,
}
}
export function managementAuth(input: SupabaseAuthInput): SupabaseAuthInput {
return {
account: input.account,
projectRef: input.projectRef,
projectUrl: input.projectUrl,
secretName: input.secretName,
patSecretName: input.patSecretName,
serviceRoleSecretName: input.serviceRoleSecretName,
}
}
export function assertSecretName(name: string, label: string): string {
const trimmed = requireString(name, label)
if (!/^[A-Za-z][A-Za-z0-9_-]{0,127}$/.test(trimmed)) {
throw new Error(
`${label} must be a secret identifier (letters, digits, underscore, hyphen).`,
)
}
return trimmed
}
export function secretPlaceholder(name: string): string {
return `{{secret:${assertSecretName(name, 'secretName')}}}`
}
/**
* Return prefilled Supabase secret setup URLs.
* @example
* import setup from 'kody:@kody/supabase/setup'
* const urls = setup({ account: 'work', projectRef: 'abcdefghijklmnop' })
*/
export default function setupEntrypoint(
input: SupabaseAuthInput & Record<string, unknown> = {},
) {
return setupUrls({
account: optionalString(input.account, 'account'),
secretName: optionalString(input.secretName, 'secretName'),
patSecretName: optionalString(input.patSecretName, 'patSecretName'),
serviceRoleSecretName: optionalString(
input.serviceRoleSecretName,
'serviceRoleSecretName',
),
projectRef: optionalString(input.projectRef, 'projectRef'),
projectUrl: optionalString(input.projectUrl, 'projectUrl'),
})
}