import { projectAuth } from './setup.ts'
import { assertMutationAllowed, readMutationFlags } from './safety.ts'
import { asLive, projectRequest } from './client.ts'
import {
clampInt,
isRecord,
optionalBoolean,
optionalString,
requireString,
type DryRunResult,
type SupabaseAuthInput,
} from './types.ts'
/**
* Create a signed Storage URL. Requires `confirm: true`, or use `dryRun: true`.
* @example
* import createSignedUrl from 'kody:@kody/supabase/create-signed-url'
* const preview = await createSignedUrl({
* projectRef: 'abcdefghijklmnop',
* bucket: 'docs',
* path: 'notes/hello.txt',
* expiresIn: 60,
* dryRun: true,
* })
*/
export async function createSignedUrl(
input: SupabaseAuthInput & {
bucket: string
path: string
expiresIn?: number
dryRun?: boolean
confirm?: boolean
},
) {
const bucket = requireString(input.bucket, 'bucket')
const objectPath = requireString(input.path, 'path').replace(/^\/+/, '')
const expiresIn = clampInt(input.expiresIn, 1, 60 * 60 * 24 * 7, 3600, 'expiresIn')
const allowed = assertMutationAllowed(readMutationFlags(input), 'create-signed-url')
const apiPath = `/storage/v1/object/sign/${encodeURIComponent(bucket)}/${objectPath
.split('/')
.map((part) => encodeURIComponent(part))
.join('/')}`
if (allowed.dryRun) {
const preview: DryRunResult = {
dryRun: true,
method: 'POST',
path: apiPath,
body: { expiresIn },
}
return preview
}
const live = asLive<{ data: unknown }>(
await projectRequest({
auth: projectAuth(input),
method: 'POST',
path: apiPath,
body: { expiresIn },
}),
)
const signedUrl = isRecord(live.data)
? optionalString(live.data.signedURL, 'signedURL') ??
optionalString(live.data.signedUrl, 'signedUrl')
: undefined
return { bucket, path: objectPath, expiresIn, signedUrl, result: live.data }
}
/**
* Create a signed Storage URL. Requires `confirm: true`, or use `dryRun: true`.
* @example
* import createSignedUrl from 'kody:@kody/supabase/create-signed-url'
* const preview = await createSignedUrl({ projectRef: 'abcdefghijklmnop', bucket: 'docs', path: 'notes/hello.txt', dryRun: true })
*/
export default async function createSignedUrlEntrypoint(
params: Partial<SupabaseAuthInput> & Record<string, unknown> = {},
) {
return await createSignedUrl({
account: optionalString(params.account, 'account'),
secretName: optionalString(params.secretName, 'secretName'),
serviceRoleSecretName: optionalString(
params.serviceRoleSecretName,
'serviceRoleSecretName',
),
projectRef: optionalString(params.projectRef, 'projectRef'),
projectUrl: optionalString(params.projectUrl, 'projectUrl'),
bucket: requireString(params.bucket, 'bucket'),
path: requireString(params.path, 'path'),
expiresIn: typeof params.expiresIn === 'number' ? params.expiresIn : undefined,
dryRun: optionalBoolean(params.dryRun, 'dryRun'),
confirm: optionalBoolean(params.confirm, 'confirm'),
})
}