Make Uploaders return NIP-94 tags

This commit is contained in:
Alex Gleason 2024-05-18 18:23:04 -05:00
parent ff39dfc4ec
commit 6090c4a6d9
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7
7 changed files with 41 additions and 75 deletions

View File

@ -9,46 +9,24 @@ interface FileMeta {
/** Upload a file, track it in the database, and return the resulting media object. */ /** Upload a file, track it in the database, and return the resulting media object. */
async function uploadFile(file: File, meta: FileMeta, signal?: AbortSignal): Promise<UnattachedMedia> { async function uploadFile(file: File, meta: FileMeta, signal?: AbortSignal): Promise<UnattachedMedia> {
const { type, size } = file;
const { pubkey, description } = meta; const { pubkey, description } = meta;
if (file.size > Conf.maxUploadSize) { if (file.size > Conf.maxUploadSize) {
throw new Error('File size is too large.'); throw new Error('File size is too large.');
} }
const { url, sha256, cid, blurhash, width, height } = await uploader.upload(file, { signal }); const tags = await uploader.upload(file, { signal });
const url = tags[0][1];
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]);
}
if (description) { if (description) {
data.push(['alt', description]); tags.push(['alt', description]);
} }
return insertUnattachedMedia({ return insertUnattachedMedia({
id: crypto.randomUUID(), id: crypto.randomUUID(),
pubkey, pubkey,
url, url,
data, data: tags,
uploaded_at: Date.now(), uploaded_at: Date.now(),
}); });
} }

View File

@ -12,8 +12,8 @@ const configUploader: Uploader = {
upload(file, opts) { upload(file, opts) {
return uploader().upload(file, opts); return uploader().upload(file, opts);
}, },
delete(id, opts) { async delete(id, opts) {
return uploader().delete(id, opts); return await uploader().delete?.(id, opts);
}, },
}; };

View File

@ -32,11 +32,12 @@ const ipfsUploader: Uploader = {
const { Hash: cid } = ipfsAddResponseSchema.parse(await response.json()); const { Hash: cid } = ipfsAddResponseSchema.parse(await response.json());
return { return [
id: cid, ['url', new URL(`/ipfs/${cid}`, Conf.mediaDomain).toString()],
cid, ['m', file.type],
url: new URL(`/ipfs/${cid}`, Conf.mediaDomain).toString(), ['cid', cid],
}; ['size', file.size.toString()],
];
}, },
async delete(cid, opts) { async delete(cid, opts) {
const url = new URL('/api/v0/pin/rm', Conf.ipfs.apiUrl); const url = new URL('/api/v0/pin/rm', Conf.ipfs.apiUrl);

View File

@ -22,11 +22,12 @@ const localUploader: Uploader = {
const url = new URL(mediaDomain); const url = new URL(mediaDomain);
const path = url.pathname === '/' ? filename : join(url.pathname, filename); const path = url.pathname === '/' ? filename : join(url.pathname, filename);
return { return [
id: filename, ['url', new URL(path, url).toString()],
sha256, ['m', file.type],
url: new URL(path, url).toString(), ['x', sha256],
}; ['size', file.size.toString()],
];
}, },
async delete(id) { async delete(id) {
await Deno.remove(join(Conf.uploadsDir, id)); await Deno.remove(join(Conf.uploadsDir, id));

View File

@ -17,17 +17,19 @@ export const nostrbuildUploader: Uploader = {
const json = await response.json(); const json = await response.json();
const [data] = nostrbuildSchema.parse(json).data; const [data] = nostrbuildSchema.parse(json).data;
return { const tags: [['url', string], ...string[][]] = [
id: data.url, ['url', data.url],
sha256: data.sha256, ['m', data.mime],
url: data.url, ['x', data.sha256],
blurhash: data.blurhash, ['ox', data.original_sha256],
width: data.dimensions?.width, ['size', file.size.toString()],
height: data.dimensions?.height, ['blurhash', data.blurhash],
}; ];
},
// deno-lint-ignore require-await if (data.dimensions) {
async delete(): Promise<void> { tags.push(['dim', `${data.dimensions.width}x${data.dimensions.height}`]);
return; }
return tags;
}, },
}; };

View File

@ -25,12 +25,14 @@ const s3Uploader: Uploader = {
const { pathStyle, bucket } = Conf.s3; const { pathStyle, bucket } = Conf.s3;
const path = (pathStyle && bucket) ? join(bucket, filename) : filename; const path = (pathStyle && bucket) ? join(bucket, filename) : filename;
const url = new URL(path, Conf.mediaDomain).toString();
return { return [
id: filename, ['url', url],
sha256, ['m', file.type],
url: new URL(path, Conf.mediaDomain).toString(), ['x', sha256],
}; ['size', file.size.toString()],
];
}, },
async delete(id) { async delete(id) {
await client().deleteObject(id); await client().deleteObject(id);

View File

@ -1,27 +1,9 @@
/** Modular uploader interface, to support uploading to different backends. */ /** Modular uploader interface, to support uploading to different backends. */
interface Uploader { interface Uploader {
/** Upload the file to the backend. */ /** Upload the file to the backend. */
upload(file: File, opts?: { signal?: AbortSignal }): Promise<UploadResult>; upload(file: File, opts?: { signal?: AbortSignal }): Promise<[['url', string], ...string[][]]>;
/** Delete the file from the backend. */ /** Delete the file from the backend. */
delete(cid: string, opts?: { signal?: AbortSignal }): Promise<void>; delete?(cid: string, opts?: { signal?: AbortSignal }): Promise<void>;
}
/** 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;
} }
export type { Uploader }; export type { Uploader };