Barazo AppView backend
barazo.forum
1import { randomUUID } from 'node:crypto'
2import { mkdir, writeFile, unlink } from 'node:fs/promises'
3import { join } from 'node:path'
4import type { Logger } from './logger.js'
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: Logger
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}