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 & Record = {}, ) { 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'), }) }