1 changed files with 113 additions and 0 deletions
@ -0,0 +1,113 @@ |
|||||
|
/** |
||||
|
* In-Memory Image Store |
||||
|
* |
||||
|
* Temporary storage for images that need to be served via URL to external providers. |
||||
|
* Images are stored in memory and served via /api/images/[id] endpoint. |
||||
|
* |
||||
|
* Features: |
||||
|
* - Store base64 data URLs as binary buffers |
||||
|
* - Retrieve images by unique ID |
||||
|
* - Explicit cleanup (no TTL - callers handle lifecycle) |
||||
|
* |
||||
|
* Note: Store is cleared on server restart (no persistence). |
||||
|
*/ |
||||
|
|
||||
|
import { randomUUID } from "crypto"; |
||||
|
|
||||
|
/** |
||||
|
* Stored image data with parsed content |
||||
|
*/ |
||||
|
interface StoredImage { |
||||
|
data: Buffer; |
||||
|
mimeType: string; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* In-memory image storage |
||||
|
*/ |
||||
|
const imageStore: Map<string, StoredImage> = new Map(); |
||||
|
|
||||
|
/** |
||||
|
* Parse a base64 data URL into its components |
||||
|
* |
||||
|
* @param base64DataUrl - Data URL in format: data:{mimeType};base64,{data} |
||||
|
* @returns Parsed mimeType and Buffer, or null if invalid format |
||||
|
*/ |
||||
|
function parseBase64DataUrl( |
||||
|
base64DataUrl: string |
||||
|
): { mimeType: string; data: Buffer } | null { |
||||
|
const match = base64DataUrl.match(/^data:([^;]+);base64,(.+)$/); |
||||
|
if (!match) { |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
const [, mimeType, base64Data] = match; |
||||
|
const data = Buffer.from(base64Data, "base64"); |
||||
|
|
||||
|
return { mimeType, data }; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Store an image and return its unique ID |
||||
|
* |
||||
|
* @param base64DataUrl - Image as base64 data URL (data:{mimeType};base64,{data}) |
||||
|
* @returns Unique ID for retrieving the image |
||||
|
* @throws Error if data URL format is invalid |
||||
|
*/ |
||||
|
export function storeImage(base64DataUrl: string): string { |
||||
|
const parsed = parseBase64DataUrl(base64DataUrl); |
||||
|
if (!parsed) { |
||||
|
throw new Error( |
||||
|
"Invalid base64 data URL format. Expected: data:{mimeType};base64,{data}" |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const id = randomUUID(); |
||||
|
imageStore.set(id, { |
||||
|
data: parsed.data, |
||||
|
mimeType: parsed.mimeType, |
||||
|
}); |
||||
|
|
||||
|
return id; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Retrieve a stored image by ID |
||||
|
* |
||||
|
* @param id - Image ID returned from storeImage |
||||
|
* @returns Image data and mimeType, or null if not found |
||||
|
*/ |
||||
|
export function getImage(id: string): StoredImage | null { |
||||
|
return imageStore.get(id) ?? null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Delete a stored image |
||||
|
* |
||||
|
* @param id - Image ID to delete |
||||
|
* @returns true if image existed and was deleted, false if not found |
||||
|
*/ |
||||
|
export function deleteImage(id: string): boolean { |
||||
|
return imageStore.delete(id); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Delete multiple stored images (for batch cleanup) |
||||
|
* |
||||
|
* @param ids - Array of image IDs to delete |
||||
|
*/ |
||||
|
export function deleteImages(ids: string[]): void { |
||||
|
for (const id of ids) { |
||||
|
imageStore.delete(id); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* Get store statistics (for debugging) |
||||
|
*/ |
||||
|
export function getStoreStats(): { size: number; ids: string[] } { |
||||
|
return { |
||||
|
size: imageStore.size, |
||||
|
ids: Array.from(imageStore.keys()), |
||||
|
}; |
||||
|
} |
||||
Loading…
Reference in new issue