UNPKG

@adventurelabs/scout-core

Version:

Core utilities and helpers for Adventure Labs Scout applications

110 lines (109 loc) 4.25 kB
"use server"; import { newServerClient } from "../supabase/server"; import { SIGNED_URL_EXPIRATION_SECONDS, } from "../constants/db"; /** * Splits file path into bucket name and path. Must be at leaast one slash in your file path * @param filePath * @returns IFilePathParts | null - null if invalid file path */ function getBucketFromFilePath(filePath) { // delete any start or end slashes/whitespace filePath = filePath.replace(/^\/+|\/+$/g, ""); const parts = filePath.split("/"); if (parts.length < 2) { return null; } const bucket_name = parts[0]; return bucket_name; } /** * Extracts the short path from a file path * @param filePath * @returns string | null - null if invalid file path */ // for example if the input is /artifacts/10/52/test.mp4 - the output shld be 10/52/test.mp4 function getFormattedPath(filePath) { const cleaned = cleanPath(filePath); const parts = cleaned.split("/"); if (parts.length < 2) { return null; } // Remove the first part (bucket name) and return the rest return parts.slice(1).join("/"); } /** Removes leading and trailing slashes and leading/trailing whitespace */ function cleanPath(filePath) { // delete leading/trailing slash and whitespace return filePath.trim().replace(/^\/+|\/+$/g, ""); } /** * Generates a signed URL for a file in Supabase storage * @param filePath - The path to the file in storage (e.g., "events/123/image.jpg") * @param expiresIn - Number of seconds until the URL expires (default: 12 hours) * @param supabaseClient - Optional Supabase client (will create new one if not provided) * @returns Promise<string | null> - The signed URL or null if error */ export async function generateSignedUrl(filePath, expiresIn = SIGNED_URL_EXPIRATION_SECONDS, supabaseClient) { try { const supabase = supabaseClient || (await newServerClient()); const bucket_name = getBucketFromFilePath(filePath); if (!bucket_name) { console.error("Invalid file path:", filePath); return null; } const formattedPath = getFormattedPath(filePath); if (!formattedPath) { console.error("Invalid formatted path:", formattedPath); return null; } const { data, error } = await supabase.storage .from(bucket_name) .createSignedUrl(formattedPath, expiresIn); if (error) { console.error("Error generating signed URL:", error.message); return null; } return data.signedUrl; } catch (error) { console.error("Error in generateSignedUrl:", error); return null; } } export async function generateSignedUrlsBatch(filePaths, expiresIn = SIGNED_URL_EXPIRATION_SECONDS, supabaseClient) { try { const supabase = supabaseClient || (await newServerClient()); const signedUrlPromises = filePaths.map(async (filePath) => { try { const bucket_name = getBucketFromFilePath(filePath); if (!bucket_name) { console.error("Invalid file path:", filePath); return null; } const formattedPath = getFormattedPath(filePath); if (!formattedPath) { console.error("Invalid formatted path:", formattedPath); return null; } const { data, error } = await supabase.storage .from(bucket_name) .createSignedUrl(formattedPath, expiresIn); if (error) { console.warn(`Error generating signed URL for ${filePath}:`, error.message); return null; } return data.signedUrl; } catch (error) { console.warn(`Exception generating signed URL for ${filePath}:`, error); return null; } }); const results = await Promise.all(signedUrlPromises); return results; } catch (error) { console.error("Error in generateSignedUrlsBatch:", error); return new Array(filePaths.length).fill(null); } }