Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions components/providers/ArweaveFallbackSwRegistration.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"use client";

import { useEffect } from "react";

const SW_PATH = "/arweave-fallback-sw.js";

export default function ArweaveFallbackSwRegistration() {
useEffect(() => {
if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) {
return;
}
navigator.serviceWorker
.register(SW_PATH, { scope: "/" })
.catch((e) => {
if (process.env.NODE_ENV !== "production") {
console.error(`[ArweaveFallbackSW] Failed to register ${SW_PATH}:`, e);
}
});
}, []);
return null;
}
2 changes: 2 additions & 0 deletions components/providers/Providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { WaveEligibilityProvider } from "@/contexts/wave/WaveEligibilityContext"
import { AppWebSocketProvider } from "@/services/websocket/AppWebSocketProvider";
import { LayoutProvider } from "../brain/my-stream/layout/LayoutContext";
import { ViewProvider } from "../navigation/ViewContext";
import ArweaveFallbackSwRegistration from "./ArweaveFallbackSwRegistration";
import CapacitorSetup from "./CapacitorSetup";
import IpfsImageSetup from "./IpfsImageSetup";
import QueryClientSetup from "./QueryClientSetup";
Expand All @@ -34,6 +35,7 @@ export default function Providers({
<AppWalletsProvider>
<WagmiSetup>
<CapacitorSetup />
<ArweaveFallbackSwRegistration />
<IpfsImageSetup />
<ReactQueryWrapper>
<RefreshProvider>
Expand Down
1 change: 1 addition & 0 deletions config/nextConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export function sharedConfig(
{ protocol: "https", hostname: "6529.io" },
{ protocol: "https", hostname: "staging.6529.io" },
{ protocol: "https", hostname: "arweave.net" },
{ protocol: "https", hostname: "ar-io.net" },
{ protocol: "http", hostname: "localhost" },
{ protocol: "https", hostname: "media.generator.seize.io" },
{ protocol: "https", hostname: "d3lqz0a4bldqgf.cloudfront.net" },
Expand Down
2 changes: 1 addition & 1 deletion config/securityHeaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function createSecurityHeaders(apiEndpoint: string | undefined = "") {
},
{
key: "Content-Security-Policy",
value: `default-src 'none'; script-src 'self' 'unsafe-inline' https://dnclu2fna0b2b.cloudfront.net https://www.google-analytics.com https://www.googletagmanager.com/ https://dataplane.rum.us-east-1.amazonaws.com 'unsafe-eval'; connect-src * 'self' blob: ${apiEndpoint} https://registry.walletconnect.com/api/v2/wallets wss://*.bridge.walletconnect.org wss://*.walletconnect.com wss://www.walletlink.org/rpc https://explorer-api.walletconnect.com/v3/wallets https://www.googletagmanager.com https://*.google-analytics.com https://cloudflare-eth.com/ https://arweave.net/* https://rpc.walletconnect.com/v1/ https://sts.us-east-1.amazonaws.com https://sts.us-west-2.amazonaws.com; font-src 'self' data: https://fonts.gstatic.com https://fonts.reown.com https://dnclu2fna0b2b.cloudfront.net https://cdnjs.cloudflare.com; img-src 'self' data: blob: ipfs: https://artblocks.io https://*.artblocks.io *; media-src 'self' blob: https://*.cloudfront.net https://videos.files.wordpress.com https://arweave.net https://*.arweave.net https://cf-ipfs.com/ipfs/* https://*.twimg.com https://artblocks.io https://*.artblocks.io; frame-src 'self' https://ipfs.io https://ipfs.io/ipfs/ https://cf-ipfs.com https://cf-ipfs.com/ipfs/ https://media.generator.seize.io https://media.generator.6529.io https://generator.seize.io https://arweave.net https://*.arweave.net https://nftstorage.link https://*.ipfs.nftstorage.link https://verify.walletconnect.com https://verify.walletconnect.org https://secure.walletconnect.com https://d3lqz0a4bldqgf.cloudfront.net https://www.youtube.com https://www.youtube-nocookie.com https://*.youtube.com https://artblocks.io https://*.artblocks.io https://docs.google.com https://drive.google.com https://*.google.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com/css2 https://dnclu2fna0b2b.cloudfront.net https://cdnjs.cloudflare.com http://cdnjs.cloudflare.com https://cdn.jsdelivr.net; object-src data:;`,
value: `default-src 'none'; script-src 'self' 'unsafe-inline' https://dnclu2fna0b2b.cloudfront.net https://www.google-analytics.com https://www.googletagmanager.com/ https://dataplane.rum.us-east-1.amazonaws.com 'unsafe-eval'; connect-src * 'self' blob: ${apiEndpoint} https://registry.walletconnect.com/api/v2/wallets wss://*.bridge.walletconnect.org wss://*.walletconnect.com wss://www.walletlink.org/rpc https://explorer-api.walletconnect.com/v3/wallets https://www.googletagmanager.com https://*.google-analytics.com https://cloudflare-eth.com/ https://arweave.net/* https://ar-io.net https://ar-io.net/* https://rpc.walletconnect.com/v1/ https://sts.us-east-1.amazonaws.com https://sts.us-west-2.amazonaws.com; font-src 'self' data: https://fonts.gstatic.com https://fonts.reown.com https://dnclu2fna0b2b.cloudfront.net https://cdnjs.cloudflare.com; img-src 'self' data: blob: ipfs: https://artblocks.io https://*.artblocks.io *; media-src 'self' blob: https://*.cloudfront.net https://videos.files.wordpress.com https://arweave.net https://*.arweave.net https://ar-io.net https://*.ar-io.net https://cf-ipfs.com/ipfs/* https://*.twimg.com https://artblocks.io https://*.artblocks.io; frame-src 'self' https://ipfs.io https://ipfs.io/ipfs/ https://cf-ipfs.com https://cf-ipfs.com/ipfs/ https://media.generator.seize.io https://media.generator.6529.io https://generator.seize.io https://arweave.net https://*.arweave.net https://ar-io.net https://*.ar-io.net https://nftstorage.link https://*.ipfs.nftstorage.link https://verify.walletconnect.com https://verify.walletconnect.org https://secure.walletconnect.com https://d3lqz0a4bldqgf.cloudfront.net https://www.youtube.com https://www.youtube-nocookie.com https://*.youtube.com https://artblocks.io https://*.artblocks.io https://docs.google.com https://drive.google.com https://*.google.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com/css2 https://dnclu2fna0b2b.cloudfront.net https://cdnjs.cloudflare.com http://cdnjs.cloudflare.com https://cdn.jsdelivr.net; object-src data:;`,
},
{ key: "X-Frame-Options", value: "SAMEORIGIN" },
{ key: "X-Content-Type-Options", value: "nosniff" },
Expand Down
23 changes: 23 additions & 0 deletions lib/arweave-fallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export const ARWEAVE_HOST = "arweave.net";
export const FALLBACK_HOST = "ar-io.net";

export function isArweaveUrl(url: string): boolean {
try {
const u = new URL(url);
const h = u.hostname.toLowerCase();
return h === ARWEAVE_HOST || h.endsWith("." + ARWEAVE_HOST);
} catch {
return false;
}
}

export function getArweaveFallbackUrl(url: string): string | null {
if (!isArweaveUrl(url)) return null;
try {
const u = new URL(url);
u.host = FALLBACK_HOST + (u.port ? ":" + u.port : "");
return u.toString();
} catch {
return null;
}
}
30 changes: 30 additions & 0 deletions public/arweave-fallback-sw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const FALLBACK_HOST = "ar-io.net";

function isArweaveRequest(url) {
try {
const h = new URL(url).hostname.toLowerCase();
return h === "arweave.net" || h.endsWith(".arweave.net");
} catch {
return false;
}
}

self.addEventListener("fetch", (event) => {
if (!isArweaveRequest(event.request.url) || event.request.mode === "navigate") return;

event.respondWith(
(async () => {
const opts = { method: event.request.method, headers: event.request.headers, credentials: "omit" };
const method = event.request.method.toUpperCase();
if (method !== "GET" && method !== "HEAD") {
return fetch(event.request);
}
try {
const res = await fetch(event.request, opts);
if (res?.ok) return res;
} catch {}
const fallback = event.request.url.replace(/^(https?:\/\/)([^/]+)/, `$1${FALLBACK_HOST}`);
return Response.redirect(fallback, 302);
})()
);
});