securityos/utils/ipfs.ts

120 lines
3.1 KiB
TypeScript

import {
HIGH_PRIORITY_REQUEST,
IPFS_GATEWAY_URLS,
MILLISECONDS_IN_SECOND,
ONE_TIME_PASSIVE_EVENT,
} from "utils/constants";
let IPFS_GATEWAY_URL = "";
const isIpfsGatewayAvailable = (gatewayUrl: string): Promise<boolean> =>
new Promise((resolve) => {
const timeoutId = window.setTimeout(
() => resolve(false),
MILLISECONDS_IN_SECOND
);
const img = new Image();
img.addEventListener(
"load",
() => {
window.clearTimeout(timeoutId);
resolve(true);
},
ONE_TIME_PASSIVE_EVENT
);
img.addEventListener(
"error",
() => {
window.clearTimeout(timeoutId);
resolve(false);
},
ONE_TIME_PASSIVE_EVENT
);
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`;
});
export 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) {
// eslint-disable-next-line no-await-in-loop
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");
return `${IPFS_GATEWAY_URL.replace(
"<CID>",
CID.parse(cid).toV1().toString()
)}${path.join("/")}${search}`;
};
export 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}` : ""
}`;
};
export const getIpfsResource = async (ipfsUrl: string): Promise<Buffer> => {
let response: Response | undefined;
const requestOptions = {
...HIGH_PRIORITY_REQUEST,
cache: "no-cache",
credentials: "omit",
keepalive: false,
mode: "cors",
referrerPolicy: "no-referrer",
// eslint-disable-next-line unicorn/no-null
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("");
};