From 6090c4a6d9a0de134015ab8a02aaf8884631dc7f Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Sat, 18 May 2024 18:23:04 -0500 Subject: [PATCH] Make Uploaders return NIP-94 tags --- src/upload.ts | 30 ++++-------------------------- src/uploaders/config.ts | 4 ++-- src/uploaders/ipfs.ts | 11 ++++++----- src/uploaders/local.ts | 11 ++++++----- src/uploaders/nostrbuild.ts | 26 ++++++++++++++------------ src/uploaders/s3.ts | 12 +++++++----- src/uploaders/types.ts | 22 ++-------------------- 7 files changed, 41 insertions(+), 75 deletions(-) diff --git a/src/upload.ts b/src/upload.ts index 1da5a7d..cd9d2bb 100644 --- a/src/upload.ts +++ b/src/upload.ts @@ -9,46 +9,24 @@ interface FileMeta { /** Upload a file, track it in the database, and return the resulting media object. */ async function uploadFile(file: File, meta: FileMeta, signal?: AbortSignal): Promise { - const { type, size } = file; const { pubkey, description } = meta; if (file.size > Conf.maxUploadSize) { throw new Error('File size is too large.'); } - const { url, sha256, cid, blurhash, width, height } = await uploader.upload(file, { signal }); - - const data: string[][] = [ - ['url', url], - ['m', type], - ['size', size.toString()], - ]; - - if (typeof width === 'number' && typeof height === 'number') { - data.push(['dim', `${width}x${height}`]); - } - - if (sha256) { - data.push(['x', sha256]); - } - - if (cid) { - data.push(['cid', cid]); - } - - if (blurhash) { - data.push(['blurhash', blurhash]); - } + const tags = await uploader.upload(file, { signal }); + const url = tags[0][1]; if (description) { - data.push(['alt', description]); + tags.push(['alt', description]); } return insertUnattachedMedia({ id: crypto.randomUUID(), pubkey, url, - data, + data: tags, uploaded_at: Date.now(), }); } diff --git a/src/uploaders/config.ts b/src/uploaders/config.ts index 8ce22b6..3f3aac7 100644 --- a/src/uploaders/config.ts +++ b/src/uploaders/config.ts @@ -12,8 +12,8 @@ const configUploader: Uploader = { upload(file, opts) { return uploader().upload(file, opts); }, - delete(id, opts) { - return uploader().delete(id, opts); + async delete(id, opts) { + return await uploader().delete?.(id, opts); }, }; diff --git a/src/uploaders/ipfs.ts b/src/uploaders/ipfs.ts index 21619b5..b83dc2e 100644 --- a/src/uploaders/ipfs.ts +++ b/src/uploaders/ipfs.ts @@ -32,11 +32,12 @@ const ipfsUploader: Uploader = { const { Hash: cid } = ipfsAddResponseSchema.parse(await response.json()); - return { - id: cid, - cid, - url: new URL(`/ipfs/${cid}`, Conf.mediaDomain).toString(), - }; + return [ + ['url', new URL(`/ipfs/${cid}`, Conf.mediaDomain).toString()], + ['m', file.type], + ['cid', cid], + ['size', file.size.toString()], + ]; }, async delete(cid, opts) { const url = new URL('/api/v0/pin/rm', Conf.ipfs.apiUrl); diff --git a/src/uploaders/local.ts b/src/uploaders/local.ts index a2381a3..d5cd46a 100644 --- a/src/uploaders/local.ts +++ b/src/uploaders/local.ts @@ -22,11 +22,12 @@ const localUploader: Uploader = { const url = new URL(mediaDomain); const path = url.pathname === '/' ? filename : join(url.pathname, filename); - return { - id: filename, - sha256, - url: new URL(path, url).toString(), - }; + return [ + ['url', new URL(path, url).toString()], + ['m', file.type], + ['x', sha256], + ['size', file.size.toString()], + ]; }, async delete(id) { await Deno.remove(join(Conf.uploadsDir, id)); diff --git a/src/uploaders/nostrbuild.ts b/src/uploaders/nostrbuild.ts index d9d0865..8bca331 100644 --- a/src/uploaders/nostrbuild.ts +++ b/src/uploaders/nostrbuild.ts @@ -17,17 +17,19 @@ export const nostrbuildUploader: Uploader = { const json = await response.json(); const [data] = nostrbuildSchema.parse(json).data; - return { - id: data.url, - sha256: data.sha256, - url: data.url, - blurhash: data.blurhash, - width: data.dimensions?.width, - height: data.dimensions?.height, - }; - }, - // deno-lint-ignore require-await - async delete(): Promise { - return; + const tags: [['url', string], ...string[][]] = [ + ['url', data.url], + ['m', data.mime], + ['x', data.sha256], + ['ox', data.original_sha256], + ['size', file.size.toString()], + ['blurhash', data.blurhash], + ]; + + if (data.dimensions) { + tags.push(['dim', `${data.dimensions.width}x${data.dimensions.height}`]); + } + + return tags; }, }; diff --git a/src/uploaders/s3.ts b/src/uploaders/s3.ts index 267d817..aaff8c8 100644 --- a/src/uploaders/s3.ts +++ b/src/uploaders/s3.ts @@ -25,12 +25,14 @@ const s3Uploader: Uploader = { const { pathStyle, bucket } = Conf.s3; const path = (pathStyle && bucket) ? join(bucket, filename) : filename; + const url = new URL(path, Conf.mediaDomain).toString(); - return { - id: filename, - sha256, - url: new URL(path, Conf.mediaDomain).toString(), - }; + return [ + ['url', url], + ['m', file.type], + ['x', sha256], + ['size', file.size.toString()], + ]; }, async delete(id) { await client().deleteObject(id); diff --git a/src/uploaders/types.ts b/src/uploaders/types.ts index e423028..81b8a0a 100644 --- a/src/uploaders/types.ts +++ b/src/uploaders/types.ts @@ -1,27 +1,9 @@ /** Modular uploader interface, to support uploading to different backends. */ interface Uploader { /** Upload the file to the backend. */ - upload(file: File, opts?: { signal?: AbortSignal }): Promise; + upload(file: File, opts?: { signal?: AbortSignal }): Promise<[['url', string], ...string[][]]>; /** Delete the file from the backend. */ - delete(cid: string, opts?: { signal?: AbortSignal }): Promise; -} - -/** Return value from the uploader after uploading a file. */ -interface UploadResult { - /** File ID specific to the uploader, so it can later be referenced or deleted. */ - id: string; - /** URL where the file can be accessed. */ - url: string; - /** SHA-256 hash of the file. */ - sha256?: string; - /** Blurhash of the file. */ - blurhash?: string; - /** IPFS CID of the file. */ - cid?: string; - /** Width of the file, if applicable. */ - width?: number; - /** Height of the file, if applicable. */ - height?: number; + delete?(cid: string, opts?: { signal?: AbortSignal }): Promise; } export type { Uploader };