Hey is a decentralized and permissionless social media app built with Lens Protocol 馃尶
1import { S3 } from "@aws-sdk/client-s3";
2import { Upload } from "@aws-sdk/lib-storage";
3import { CHAIN, EVER_API, EVER_BUCKET, EVER_REGION } from "@hey/data/constants";
4import generateUUID from "@hey/helpers/generateUUID";
5import { immutable } from "@lens-chain/storage-client";
6import { hono } from "./fetcher";
7import { storageClient } from "./storageClient";
8
9interface UploadResult {
10 mimeType: string;
11 uri: string;
12}
13
14const FALLBACK_TYPE = "image/jpeg";
15const FILE_SIZE_LIMIT_MB = 8 * 1024 * 1024; // 8MB in bytes
16
17const getS3Client = async (): Promise<S3> => {
18 const data = await hono.metadata.sts();
19
20 if (!data) {
21 throw new Error("Failed to get S3 client");
22 }
23
24 const client = new S3({
25 credentials: {
26 accessKeyId: data.accessKeyId ?? "",
27 secretAccessKey: data.secretAccessKey ?? "",
28 sessionToken: data.sessionToken ?? ""
29 },
30 endpoint: EVER_API,
31 maxAttempts: 10,
32 region: EVER_REGION
33 });
34
35 return client;
36};
37
38const uploadToIPFS = async (
39 data: FileList | File[]
40): Promise<UploadResult[]> => {
41 try {
42 const files = Array.from(data) as File[];
43 const s3Files = files.filter(
44 (file: File) => file.size > FILE_SIZE_LIMIT_MB
45 );
46 const client = s3Files.length > 0 ? await getS3Client() : null;
47
48 const attachments = await Promise.all(
49 files.map(async (file: File) => {
50 // If the file is less than FILE_SIZE_LIMIT_MB, upload it to the Grove
51 if (file.size <= FILE_SIZE_LIMIT_MB) {
52 const storageNodeResponse = await storageClient.uploadFile(file, {
53 acl: immutable(CHAIN.id)
54 });
55
56 return {
57 mimeType: file.type || FALLBACK_TYPE,
58 uri: storageNodeResponse.uri
59 };
60 }
61
62 // For files larger than FILE_SIZE_LIMIT_MB, use the S3 client
63 if (client) {
64 const currentDate = new Date()
65 .toLocaleDateString("en-GB")
66 .replace(/\//g, "-");
67
68 const params = {
69 Body: file,
70 Bucket: EVER_BUCKET,
71 ContentType: file.type,
72 Key: `${currentDate}/${generateUUID()}`
73 };
74 const task = new Upload({ client, params });
75 await task.done();
76 const result = await client.headObject(params);
77 const metadata = result.Metadata;
78 const cid = metadata?.["ipfs-hash"];
79
80 return { mimeType: file.type || FALLBACK_TYPE, uri: `ipfs://${cid}` };
81 }
82
83 return { mimeType: file.type || FALLBACK_TYPE, uri: "" };
84 })
85 );
86
87 return attachments;
88 } catch {
89 return [];
90 }
91};
92
93export const uploadFileToIPFS = async (file: File): Promise<UploadResult> => {
94 try {
95 const ipfsResponse = await uploadToIPFS([file]);
96 const metadata = ipfsResponse[0];
97
98 return { mimeType: file.type || FALLBACK_TYPE, uri: metadata.uri };
99 } catch {
100 return { mimeType: file.type || FALLBACK_TYPE, uri: "" };
101 }
102};
103
104export default uploadToIPFS;