uploaders: allow deleting files by CID

This commit is contained in:
Alex Gleason 2023-09-08 15:01:30 -05:00
parent c40f10539d
commit 014b9f6d29
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7
4 changed files with 66 additions and 28 deletions

View File

@ -20,7 +20,7 @@ const mediaController: AppController = async (c) => {
try { try {
const { file } = result.data; const { file } = result.data;
const { cid } = await s3Uploader(file); const { cid } = await s3Uploader.upload(file);
return c.json({ return c.json({
id: cid, id: cid,

View File

@ -3,28 +3,48 @@ import { z } from '@/deps.ts';
import type { Uploader } from './types.ts'; import type { Uploader } from './types.ts';
const ipfsAddResultSchema = z.object({ /** Response schema for POST `/api/v0/add`. */
const ipfsAddResponseSchema = z.object({
Name: z.string(), Name: z.string(),
Hash: z.string(), Hash: z.string(),
Size: z.string(), Size: z.string(),
}); });
const ipfsUploader: Uploader = async (file) => { /**
const url = new URL('/api/v0/add', Conf.ipfs.apiUrl); * IPFS uploader. It expects an IPFS node up and running.
* It will try to connect to `http://localhost:5001` by default,
* and upload the file using the REST API.
*/
const ipfsUploader: Uploader = {
async upload(file) {
const url = new URL('/api/v0/add', Conf.ipfs.apiUrl);
const formData = new FormData(); const formData = new FormData();
formData.append('file', file); formData.append('file', file);
const response = await fetch(url, { const response = await fetch(url, {
method: 'POST', method: 'POST',
body: formData, body: formData,
}); });
const { Hash } = ipfsAddResultSchema.parse(await response.json()); const { Hash } = ipfsAddResponseSchema.parse(await response.json());
return { return {
cid: Hash, cid: Hash,
}; };
},
async delete(cid) {
const url = new URL('/api/v0/pin/rm', Conf.ipfs.apiUrl);
const query = new URLSearchParams();
query.set('arg', cid);
url.search = query.toString();
await fetch(url, {
method: 'POST',
});
},
}; };
export { ipfsUploader }; export { ipfsUploader };

View File

@ -5,19 +5,29 @@ import type { Uploader } from './types.ts';
const s3 = new S3Client({ ...Conf.s3 }); const s3 = new S3Client({ ...Conf.s3 });
const s3Uploader: Uploader = async (file) => { /**
const cid = await IpfsHash.of(file.stream()) as string; * S3-compatible uploader for AWS, Wasabi, DigitalOcean Spaces, and more.
* Files are named by their IPFS CID and exposed at `/ipfs/<cid>`, letting it
* take advantage of IPFS features while not really using IPFS.
*/
const s3Uploader: Uploader = {
async upload(file) {
const cid = await IpfsHash.of(file.stream()) as string;
await s3.putObject(`ipfs/${cid}`, file.stream(), { await s3.putObject(`ipfs/${cid}`, file.stream(), {
metadata: { metadata: {
'Content-Type': file.type, 'Content-Type': file.type,
'x-amz-acl': 'public-read', 'x-amz-acl': 'public-read',
}, },
}); });
return { return {
cid, cid,
}; };
},
async delete(cid) {
await s3.deleteObject(`ipfs/${cid}`);
},
}; };
export { s3Uploader }; export { s3Uploader };

View File

@ -1,7 +1,15 @@
/** Modular uploader interface, to support uploading to different backends. */
interface Uploader {
/** Upload the file to the backend. */
upload(file: File): Promise<UploadResult>;
/** Delete the file from the backend. */
delete(cid: string): Promise<void>;
}
/** Return value from the uploader after uploading a file. */
interface UploadResult { interface UploadResult {
/** IPFS CID for the file. */
cid: string; cid: string;
} }
type Uploader = (file: File) => Promise<UploadResult>; export type { Uploader };
export type { Uploader, UploadResult };