-
Notifications
You must be signed in to change notification settings - Fork 127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve ipfs util #440
Improve ipfs util #440
Changes from all commits
a85af89
064ff53
b849de2
95b2427
d814fe1
2a5fa78
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,128 @@ | ||
import { cid } from 'is-ipfs'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. General thinking, I like a lot what's going on here. Why some of these utility don't live in Or should any of those live inside OnchainKit? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Yuripetusko thoughts on this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I actually have all of this code in a library already. Just need to add docs. Perhaps instead of having is-ipfs and all this code explicitly, our library will work? Otherwise I can just duplicate some of it in Onchainkit There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's probably move part of it inside OnchainKit. We can proabably create a new section called IPFS. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok will do tomorrow. I'll close this PR then and later will create a new one where onchainkit utils are used |
||
|
||
export const DEFAULT_IPFS_GATEWAY_KEYS = { | ||
cloudflare: 'cloudflare', | ||
ipfsIo: 'ipfsIo', | ||
pinata: 'pinata', | ||
nftStorage: 'nftStorage', | ||
}; | ||
|
||
export type DefaultIpfsGatewayKeys = | ||
(typeof DEFAULT_IPFS_GATEWAY_KEYS)[keyof typeof DEFAULT_IPFS_GATEWAY_KEYS]; | ||
|
||
export type IpfsGateways = Record<DefaultIpfsGatewayKeys, string>; | ||
|
||
export const DEFAULT_IPFS_GATEWAY_HOSTNAMES: IpfsGateways = { | ||
cloudflare: 'cloudflare-ipfs.com', | ||
ipfsIo: 'ipfs.io', | ||
pinata: 'gateway.pinata.cloud', | ||
nftStorage: 'nftstorage.link', | ||
}; | ||
|
||
/** | ||
* Checks if a string contains an IPFS CID | ||
* @param ipfsURI | ||
* @returns an object with a boolean indicating if the string contains a CID and the CID if it does | ||
*/ | ||
export const containsCID = (ipfsURI?: string | null) => { | ||
if (typeof ipfsURI !== 'string') { | ||
throw new Error('url is not string'); | ||
} | ||
const splitUrl = ipfsURI.split(/\/|\?/); | ||
for (const split of splitUrl) { | ||
if (cid(split)) { | ||
return { | ||
containsCid: true, | ||
cid: split, | ||
}; | ||
} | ||
const splitOnDot = split.split('.')[0]; | ||
if (cid(splitOnDot)) { | ||
return { | ||
containsCid: true, | ||
cid: splitOnDot, | ||
}; | ||
} | ||
} | ||
|
||
return { | ||
containsCid: false, | ||
cid: '', | ||
}; | ||
}; | ||
|
||
/** | ||
* Transforms an IPFS URL to a desired gateway. | ||
* The input URL can either already be a http url or an ipfs uri. | ||
* Supports ipns and ipfs uris with folder paths | ||
* @param ipfsURI - ipfs uri or http url (ipfs://${cid} or http://{gatewayHostname | 'ipfs.io'}/ipfs/${cid}) | ||
* @param gatewayHostname - preferred gateway provider | ||
*/ | ||
export const transformIpfsUrlToUrlGateway = ( | ||
ipfsURI?: string | null, | ||
gatewayHostname = DEFAULT_IPFS_GATEWAY_HOSTNAMES[DEFAULT_IPFS_GATEWAY_KEYS.ipfsIo], | ||
) => { | ||
const results = containsCID(ipfsURI); | ||
if (!ipfsURI || !results.containsCid || !results.cid) { | ||
throw new Error('url does not contain CID'); | ||
} | ||
|
||
if (gatewayHostname?.startsWith('http')) { | ||
throw new Error('gatewayHostname should not start with http'); | ||
} | ||
|
||
//Get url parts after the cid, to append them back to the new gateway url | ||
const splitUrl = ipfsURI.split(results.cid); | ||
//case 1 - the ipfs://cid path | ||
if (ipfsURI.includes(`ipfs://${results.cid}`)) { | ||
return `https://${gatewayHostname}/ipfs/${results.cid}${splitUrl[1]}`; | ||
} | ||
|
||
//case 2 - the /ipfs/cid path (this should cover ipfs://ipfs/cid as well | ||
if (ipfsURI.includes(`/ipfs/${results.cid}`)) { | ||
return `https://${gatewayHostname}/ipfs/${results.cid}${splitUrl[1]}`; | ||
} | ||
|
||
//case 3 - the /ipns/cid path | ||
if (ipfsURI.includes(`/ipns/${results.cid}`)) { | ||
return `https://${gatewayHostname}/ipns/${results.cid}${splitUrl[1]}`; | ||
} | ||
|
||
if (!ipfsURI.includes('ipfs') && ipfsURI === results.cid) { | ||
return `https://${gatewayHostname}/ipfs/${results.cid}${splitUrl[1]}`; | ||
} | ||
|
||
console.warn(`Unsupported URL pattern: ${ipfsURI}. Attempting a default fallback.`); | ||
return `https://${gatewayHostname}/ipfs/${results.cid}${splitUrl[1]}`; | ||
}; | ||
|
||
/** | ||
* Convert IPFS URI to HTTPS URI. | ||
* Convert IPFS or HTTP URI to HTTPS URI with an ipfs gateway provider (if provided). | ||
* | ||
* @param ipfsURI An ipfs protocol URI. | ||
* @param gateway The IPFS gateway to use. Defaults to ipfs.io, a free public gateway. | ||
* @param ipfsURI A value from contract. Can be an ipfs or http URI. | ||
* @param gatewayHostname The IPFS gateway to use. Defaults to ipfs.io, a free public gateway. | ||
* For production use, you'll likely want a paid provider. | ||
* @returns An HTTPS URI that points to the data represented by the cid | ||
* embedded in the ipfs URI. | ||
*/ | ||
export const ipfsToHTTP = function (ipfsURI: string, gateway = 'ipfs.io') { | ||
// IPNS Name is a Multihash of a serialized PublicKey. | ||
const cid = ipfsURI.replace('ipfs://', ''); | ||
|
||
// Addresses using a gateway use the following form, | ||
// where <gateway> is the gateway address, | ||
// and <CID> is the content identifier. | ||
return `https://${gateway}/ipfs/${cid}`; | ||
export const ipfsToHTTP = (ipfsURI?: string | null, gatewayHostname?: string) => { | ||
if (!ipfsURI) return ''; | ||
|
||
if (ipfsURI.startsWith('http') && !containsCID(ipfsURI).containsCid) { | ||
return ipfsURI.replace('http://', 'https://'); | ||
} | ||
|
||
// If no gateway url is passed, and url contains cid, and url already starts with http, just return it as is, making sure it's https | ||
if (ipfsURI.startsWith('http') && containsCID(ipfsURI).containsCid && !gatewayHostname) { | ||
return ipfsURI.replace('http://', 'https://'); | ||
} | ||
|
||
if (containsCID(ipfsURI).containsCid) { | ||
return transformIpfsUrlToUrlGateway( | ||
ipfsURI, | ||
gatewayHostname ?? DEFAULT_IPFS_GATEWAY_HOSTNAMES[DEFAULT_IPFS_GATEWAY_KEYS.ipfsIo], | ||
); | ||
} | ||
|
||
return ''; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh that's dope. Teach me a bit, how do they work together?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is mostly telling nextjs to allow to load images from these domains in nextjs NextImage component