/** * @packageDocumentation * @module MediaManager/Drivers */ import { S3Client } from "@bradenmacdonald/s3-lite-client"; import type { Config } from "~/packages/config-manager/config.type"; import { getMediaHash } from "../media-hasher"; import type { UploadedFileMetadata } from "../media-manager"; import type { MediaDriver } from "./media-driver"; /** * Implements the MediaDriver interface for S3 storage. */ export class S3MediaDriver implements MediaDriver { private s3Client: S3Client; /** * Creates a new S3MediaDriver instance. * @param config - The configuration object. */ constructor(config: Config) { this.s3Client = new S3Client({ endPoint: config.s3.endpoint, useSSL: true, region: config.s3.region || "auto", bucket: config.s3.bucket_name, accessKey: config.s3.access_key, secretKey: config.s3.secret_access_key, }); } /** * @inheritdoc */ public async addFile( file: File, ): Promise> { // Sometimes the file name is not available, so we generate a random name const fileName = file.name ?? crypto.randomUUID(); const hash = await getMediaHash(file); const path = `${hash}/${fileName}`; await this.s3Client.putObject(path, file.stream(), { size: file.size, }); return { uploadedFile: file, path, hash, }; } /** * @inheritdoc */ public async getFileByHash( hash: string, databaseHashFetcher: (sha256: string) => Promise, ): Promise { const filename = await databaseHashFetcher(hash); if (!filename) { return null; } return this.getFile(filename); } /** * @inheritdoc */ public async getFile(filename: string): Promise { try { await this.s3Client.statObject(filename); const file = await this.s3Client.getObject(filename); const arrayBuffer = await file.arrayBuffer(); return new File([arrayBuffer], filename, { type: file.headers.get("Content-Type") || undefined, }); } catch { return null; } } /** * @inheritdoc */ public async deleteFileByUrl(url: string): Promise { const urlObj = new URL(url); const path = urlObj.pathname.slice(1); // Remove leading slash await this.s3Client.deleteObject(path); } }