/** * Shared Blob Storage utilities. * * Delegates to @bytelyst/storage for provider-agnostic blob operations. * Keeps the same exported API surface for backward compatibility. * * Expected env vars: * STORAGE_PROVIDER — 'azure' (default) | 'memory' * AZURE_BLOB_CONNECTION_STRING — full connection string (preferred, when provider=azure) * — OR — * AZURE_BLOB_ACCOUNT_NAME + AZURE_BLOB_ACCOUNT_KEY */ import { getStorage, _resetStorage, type StorageProvider, type StorageBucket, } from '@bytelyst/storage'; /** * Known blob containers and their purposes. * * Note: This is a convenience list (not enforced). Products can add their own * containers as needed. */ export const BLOB_CONTAINERS = { audio: 'audio', // Dictation audio recordings transcripts: 'transcripts', // Exported transcript files (PDF, DOCX, TXT) attachments: 'attachments', // Tracker item attachments (screenshots, docs) avatars: 'avatars', // User profile images releases: 'releases', // Desktop app update binaries backups: 'backups', // Cosmos DB JSON backups feedbackScreenshots: 'feedback-screenshots', // User feedback screenshot attachments } as const; export type BlobContainerName = (typeof BLOB_CONTAINERS)[keyof typeof BLOB_CONTAINERS]; /** * Get the storage provider singleton. */ export async function getStorageProvider(): Promise { return getStorage(); } /** * Get a bucket (container) by name. */ export async function getBucket(containerName: string): Promise { const storage = await getStorage(); return storage.getBucket(containerName); } /** * Generate a signed URL for direct browser upload (or download). * * @param containerName - Target container * @param blobName - Full blob path (e.g., "product/user123/audio/recording.wav") * @param permissions - SAS permissions (default: read) * @param expiresInMinutes - Token lifetime (default: 60) * @returns Full signed URL for the blob */ export async function generateSasUrl( containerName: string, blobName: string, permissions: 'r' | 'w' | 'rw' | 'rwc' | 'rwd' = 'r', expiresInMinutes = 60 ): Promise { const bucket = await getBucket(containerName); const perm = permissions.includes('w') ? ('write' as const) : ('read' as const); return bucket.getSignedUrl(blobName, { permissions: perm, expiresIn: expiresInMinutes * 60, }); } /** * Check if blob storage is configured. */ export function isBlobStorageConfigured(): boolean { const provider = process.env.STORAGE_PROVIDER || 'azure'; if (provider === 'memory') return true; return !!( process.env.AZURE_BLOB_CONNECTION_STRING || (process.env.AZURE_BLOB_ACCOUNT_NAME && process.env.AZURE_BLOB_ACCOUNT_KEY) ); } /** * Test helper: reset module singletons/caches. */ export function _resetBlobClient(): void { _resetStorage(); }