learning_ai_common_plat/packages/blob/src/blob.ts

95 lines
2.8 KiB
TypeScript

/**
* 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<StorageProvider> {
return getStorage();
}
/**
* Get a bucket (container) by name.
*/
export async function getBucket(containerName: string): Promise<StorageBucket> {
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<string> {
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();
}