I've been wanting to dip my toes into the Web 3.0 verse for a while now, specifically with an integration into my side project, a Desktop environment in the browser (daedalOS).
I've decided as a first step I would add ipfs native url support. I've added this support to the Browser (Demo), Run Dialog, Shortcuts & Terminal. Most of the main ipfs functions I've migrated to utils/ipfs.ts.
The basic concept for how this works is by taking the ipfs native url and converting it to one that uses an ipfs gateway, in my case I've added the official ipfs gateways as the fallback and the Cloudflare gateways as the primary.
const IPFS_GATEWAY_URLS = [
"https://<CID>.ipfs.cf-ipfs.com/",
"https://<CID>.ipfs.dweb.link/",
"https://cloudflare-ipfs.com/ipfs/<CID>/",
"https://gateway.ipfs.io/ipfs/<CID>/",
];
When I make the URL I attempt to use the newer CID v1 format, not to be confused with CID v0 which is less CORS friendly. To convert into this format which uses a case-insensitive base32 format, I used the library multiformats.
let IPFS_GATEWAY_URL = "";
const getIpfsGatewayUrl = async (
ipfsUrl: string,
notCurrent?: boolean
): Promise<string> => {
if (!IPFS_GATEWAY_URL || notCurrent) {
const urlList = notCurrent
? IPFS_GATEWAY_URLS.filter((url) => url !== IPFS_GATEWAY_URL)
: IPFS_GATEWAY_URLS;
for (const url of urlList) {
if (await isIpfsGatewayAvailable(url)) {
IPFS_GATEWAY_URL = url;
break;
}
}
if (!IPFS_GATEWAY_URL) return "";
}
const { pathname, protocol, search } = new URL(ipfsUrl);
if (protocol !== "ipfs:") return "";
const [cid = "", ...path] = pathname.split("/").filter(Boolean);
const { CID } = await import("multiformats/cid");
return `${IPFS_GATEWAY_URL.replace(
"<CID>",
CID.parse(cid).toV1().toString()
)}${path.join("/")}${search}`;
};
When doing the initial check to find the gateway to use, I've based the availability checker on code taken from public-gateway-checker.
const isIpfsGatewayAvailable = (gatewayUrl: string): Promise<boolean> =>
new Promise((resolve) => {
const timeoutId = window.setTimeout(
() => resolve(false),
1000
);
const img = new Image();
img.addEventListener("load", () => {
window.clearTimeout(timeoutId);
resolve(true);
});
img.addEventListener("error", () => {
window.clearTimeout(timeoutId);
resolve(false);
});
img.src = `${gatewayUrl.replace(
"<CID>",
// https://github.com/ipfs/public-gateway-checker/blob/master/src/constants.ts
"bafybeibwzifw52ttrkqlikfzext5akxu7lz4xiwjgwzmqcpdzmp3n5vnbe"
)}?now=${Date.now()}&filename=1x1.png#x-ipfs-companion-no-redirect`;
});
When it comes to actually doing the request, a normal fetch()
command is all that is needed with the new ipfs gateway url.
const getIpfsResource = async (ipfsUrl: string): Promise<Buffer> => {
let response: Response | null = null;
const requestOptions = {
cache: "no-cache",
credentials: "omit",
keepalive: false,
mode: "cors",
priority: "high",
referrerPolicy: "no-referrer",
window: null,
} as RequestInit;
try {
response = await fetch(await getIpfsGatewayUrl(ipfsUrl), requestOptions);
} catch (error) {
if ((error as Error).message === "Failed to fetch") {
response = await fetch(
await getIpfsGatewayUrl(ipfsUrl, true),
requestOptions
);
}
}
return response instanceof Response
? Buffer.from(await response.arrayBuffer())
: Buffer.from("");
};
I've also added file type detection so it can try to open the url in the correct app. This is done using the library file-type.
const getIpfsFileName = async (
ipfsUrl: string,
ipfsData: Buffer
): Promise<string> => {
const { pathname, searchParams } = new URL(ipfsUrl);
const fileName = searchParams.get("filename");
if (fileName) return fileName;
const { fileTypeFromBuffer } = await import("file-type");
const { ext = "" } = (await fileTypeFromBuffer(ipfsData)) || {};
return `${pathname.split("/").filter(Boolean).join("_")}${
ext ? `.${ext}` : ""
}`;
};
I've found the reliability of the gateway's to be pretty good, but sometimes there was a large amount of lag before a request would begin.
Thanks for reading my article and feel free to try out some ipfs urls on daedalOS which is also my personal website, dustinbrett.com.
If you want to know more about daedalOS which is the client of all this ipfs magic, feel free to check out my YouTube video where I go over the various features, including ipfs. Thanks!
Top comments (0)