This release is 12 versions behind 0.0.265 — the latest version of @bureaudouble/bureau. Jump to latest
@bureaudouble/bureau@0.0.253
It is unknown whether this package works with Cloudflare Workers, Node.js, Deno, Bun, Browsers




JSR Score
0%
Published
a month ago (0.0.253)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148"use server"; import type { S3ObjectMetadata } from "jsr:@bradenmacdonald/s3-lite-client@^0.7.6"; import { s3Client } from "../utils/storage.ts"; import { basename } from "jsr:/@std/path@^1.0.8/basename"; import { join } from "jsr:/@std/path@^1.0.8/join"; import { lookup } from "npm:mrmime@^2.0.0"; interface S3Object { type: "Object"; key: string; lastModified: Date; etag: string; size: number; "content-type"?: string; } export type MediaItem = S3Object & { name: string; alt?: string }; const sessionFolders: Set<string> = new Set(); export interface ListMedia { prefix?: string; breadcrumb?: { prefix: string; name: string }[]; folders: { prefix: string; name: string | undefined }[]; objects: MediaItem[]; } export async function listMedia( _state: ListMedia, options?: { prefix?: string; filter?: string } | undefined, ): Promise<ListMedia> { const objects: MediaItem[] = []; const basePrefix = join("medias", "/"); const folders = new Set<string>( [...sessionFolders] .filter((path) => { if (options?.prefix) { // Include folders that are direct children of the current prefix return ( path.startsWith(options.prefix) && path.slice(options.prefix.length).split("/").filter(Boolean) .length === 1 ); } // Include only top-level folders when no prefix is specified return path.split("/").filter(Boolean).length === 1; }) .map((path) => (path.endsWith("/") ? path : `${path}/`)), ); for await ( const result of s3Client.listObjectsGrouped({ prefix: join(basePrefix, options?.prefix ?? "", "/"), delimiter: "/", }) ) { if (result.type === "CommonPrefix") { if (!result.prefix.includes(options?.filter ?? "")) continue; folders.add(result.prefix.slice(basePrefix.length)); continue; } if (!result.key.includes(options?.filter ?? "")) continue; if (basename(result.key).toLowerCase() === ".ds_store") continue; objects.push({ ...result, name: basename(result.key), "content-type": lookup(result.key) ?? "application/octet-stream", }); } return { prefix: options?.prefix, folders: Array.from(folders) .filter((v) => v) .map((prefix) => ({ prefix, name: basename(prefix), })), breadcrumb: [ { prefix: "", name: "medias" }, ...(options?.prefix ?? "") .split("/") .filter((v) => v.length > 0) .map((name, index, array) => ({ name, prefix: join(...array.slice(0, index + 1), "/"), })), ], objects, }; } export const createFolder = async (prefix: string): Promise<string> => { await sessionFolders.add(prefix); return prefix; }; export async function getUploadUrl(key: string): Promise<string> { return await s3Client.getPresignedUrl("PUT", join("medias", key), { expirySeconds: 20, }); } export async function deleteMedia(keys: string[]): Promise<boolean> { for (const key of keys.filter((key) => key.startsWith("medias/"))) { await s3Client.deleteObject(key); } return true; } export type MediaActionResult<T> = | ({ success: true } & T) | { success: false; error: string; }; const formatAsyncResult = <T>( fn: () => Promise<T>, ): Promise<MediaActionResult<T>> => fn() .then((data) => ({ success: true as const, ...data })) .catch((error) => ({ success: false, error: error.message })); export const getMediaMetadata = ( sourceKey: string, ): Promise<MediaActionResult<{ data: S3ObjectMetadata }>> => formatAsyncResult(async () => { const objectStatus = await s3Client.statObject(sourceKey); return { data: objectStatus.metadata }; }); export const updateMediaMetadata = ( sourceKey: string, metadata: { name?: string; alt?: string }, ): Promise<MediaActionResult<{ etag: string }>> => formatAsyncResult(async () => { const objectStatus = await s3Client.statObject(sourceKey); const newMetadata = { ...objectStatus.metadata }; if (metadata.alt !== undefined) { newMetadata["x-amz-meta-alt"] = metadata.alt; } const result = await s3Client.copyObject({ sourceKey }, sourceKey, { metadata: newMetadata, }); return result; });