UNPKG

@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
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; };