Sifa professional network API (Fastify, AT Protocol, Jetstream) sifa.id/
at main 48 lines 1.6 kB view raw
1import { randomUUID } from 'node:crypto'; 2import { mkdir, writeFile, unlink } from 'node:fs/promises'; 3import { join } from 'node:path'; 4import type { FastifyBaseLogger } from 'fastify'; 5 6export interface StorageService { 7 store(data: Buffer, mimeType: string, prefix: string): Promise<string>; 8 delete(url: string): Promise<void>; 9} 10 11const MIME_TO_EXT: Record<string, string> = { 12 'image/jpeg': '.jpg', 13 'image/png': '.png', 14 'image/webp': '.webp', 15 'image/gif': '.gif', 16}; 17 18export function createLocalStorage( 19 uploadDir: string, 20 baseUrl: string, 21 logger: Pick<FastifyBaseLogger, 'debug'>, 22): StorageService { 23 return { 24 async store(data: Buffer, mimeType: string, prefix: string): Promise<string> { 25 const ext = MIME_TO_EXT[mimeType] ?? '.bin'; 26 const filename = `${prefix}-${randomUUID()}${ext}`; 27 const dir = join(uploadDir, prefix); 28 await mkdir(dir, { recursive: true }); 29 const filepath = join(dir, filename); 30 await writeFile(filepath, data); 31 logger.debug({ filepath, size: data.length }, 'File stored'); 32 return `${baseUrl}/uploads/${prefix}/${filename}`; 33 }, 34 35 async delete(url: string): Promise<void> { 36 try { 37 const uploadsIdx = url.indexOf('/uploads/'); 38 if (uploadsIdx === -1) return; 39 const relativePath = url.slice(uploadsIdx + '/uploads/'.length); 40 const filepath = join(uploadDir, relativePath); 41 await unlink(filepath); 42 logger.debug({ filepath }, 'File deleted'); 43 } catch { 44 // Best-effort deletion 45 } 46 }, 47 }; 48}