Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/supabase

src/create-signed-url.ts

91 lines · 2.9 KB · TypeScript
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'),
	})
}