@taqueria/plugin-ipfs-pinata
Version:
A plugin for Taqueria providing ipfs publishing and pinning using the Pinata service
152 lines (133 loc) • 3.42 kB
text/typescript
import FormData from 'form-data';
import fs from 'fs';
import { readFile } from 'fs/promises';
import fetch from 'node-fetch';
import { getFileIPFSHash } from './ipfsHash';
export type PinataAuth = {
pinataJwtToken: string;
};
export type PublishFileResult = {
ipfsHash: string;
};
export const publishFileToIpfs = async ({
auth,
item,
}: {
auth: PinataAuth;
item: {
name: string;
filePath: string;
};
}): Promise<PublishFileResult> => {
// The data api to check for existing file is limited to 30 requests per minute
// While uploading allows 180 requests per minute
// i.e. it's faster to just upload again
// // Skip if already pinned
// const { isPinned, ipfsHash } = await checkIfFileIsPinned({ auth, item });
// if (isPinned) {
// return {
// ipfsHash,
// };
// }
const data = new FormData();
data.append('file', fs.createReadStream(item.filePath));
data.append(
'pinataMetadata',
JSON.stringify({
name: item.name,
}),
);
const response = await fetch(`https://api.pinata.cloud/pinning/pinFileToIPFS`, {
headers: {
Authorization: `Bearer ${auth.pinataJwtToken}`,
'Content-Type': `multipart/form-data; boundary=${(data as unknown as { _boundary: string })._boundary}`,
},
body: data,
method: 'post',
});
if (!response.ok) {
throw new Error(`Failed to upload '${item.name}' to ipfs ${response.statusText}`);
}
const uploadResult = await response.json() as {
IpfsHash: string; // This is the IPFS multi-hash provided back for your content,
PinSize: string; // This is how large (in bytes) the content you just pinned is,
Timestamp: string; // This is the timestamp for your content pinning (represented in ISO 8601 format)
};
return {
ipfsHash: uploadResult.IpfsHash,
};
};
const checkIfFileIsPinned = async ({
auth,
item,
}: {
auth: PinataAuth;
item: {
name: string;
filePath: string;
};
}) => {
const contents = await readFile(item.filePath);
const ipfsHash = await getFileIPFSHash(item.filePath);
const response = await fetch(`https://api.pinata.cloud/data/pinList?status=pinned&hashContains=${ipfsHash}`, {
headers: {
Authorization: `Bearer ${auth.pinataJwtToken}`,
},
method: 'get',
});
if (!response.ok) {
throw new Error(`Failed to query '${item.name}' status from pinata ${response.statusText}`);
}
const pinResult = await response.json() as {
count: number;
rows: {
id: string;
ipfs_pin_hash: string;
size: number;
user_id: string;
date_pinned: null | string;
date_unpinned: null | string;
metadata: {
name: string;
keyvalues: null | string;
};
regions: {
regionId: string;
currentReplicationCount: number;
desiredReplicationCount: number;
}[];
}[];
};
const isPinned = pinResult.rows.some(x =>
x.ipfs_pin_hash === ipfsHash
&& x.date_pinned
&& !x.date_unpinned
);
return {
isPinned,
ipfsHash,
};
};
export const pinHash = async ({
auth,
ipfsHash,
}: {
auth: PinataAuth;
ipfsHash: string;
}) => {
const response = await fetch(`https://api.pinata.cloud/pinning/pinByHash`, {
headers: {
Authorization: `Bearer ${auth.pinataJwtToken}`,
'Content-Type': 'application/json',
},
method: 'post',
body: JSON.stringify({
hashToPin: ipfsHash,
}),
});
if (!response.ok) {
throw new Error(`Failed to pin '${ipfsHash}' with pinata: ${response.statusText}`);
}
// Ok is the only response if successful
return;
};