Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"test": "yarn hardhat:test",
"vercel": "yarn workspace @se-2/nextjs vercel",
"vercel:yolo": "yarn workspace @se-2/nextjs vercel:yolo",
"ipfs": "yarn workspace @se-2/nextjs ipfs",
"verify": "yarn hardhat:verify"
},
"packageManager": "yarn@3.2.3",
Expand Down
10 changes: 10 additions & 0 deletions packages/nextjs/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,14 @@ const nextConfig = {
},
};

const isIpfs = process.env.npm_lifecycle_event === "ipfs";

if (isIpfs) {
nextConfig.output = "export";
nextConfig.trailingSlash = true;
nextConfig.images = {
unoptimized: true,
};
}

module.exports = nextConfig;
3 changes: 2 additions & 1 deletion packages/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"serve": "next start",
"start": "next dev",
"vercel": "vercel --build-env YARN_ENABLE_IMMUTABLE_INSTALLS=false --build-env ENABLE_EXPERIMENTAL_COREPACK=1 --build-env VERCEL_TELEMETRY_DISABLED=1",
"vercel:yolo": "vercel --build-env YARN_ENABLE_IMMUTABLE_INSTALLS=false --build-env ENABLE_EXPERIMENTAL_COREPACK=1 --build-env NEXT_PUBLIC_IGNORE_BUILD_ERROR=true --build-env VERCEL_TELEMETRY_DISABLED=1"
"vercel:yolo": "vercel --build-env YARN_ENABLE_IMMUTABLE_INSTALLS=false --build-env ENABLE_EXPERIMENTAL_COREPACK=1 --build-env NEXT_PUBLIC_IGNORE_BUILD_ERROR=true --build-env VERCEL_TELEMETRY_DISABLED=1",
"ipfs": "yarn build && node scripts/deploy-ipfs.js"
},
"dependencies": {
"@heroicons/react": "^2.1.5",
Expand Down
96 changes: 96 additions & 0 deletions packages/nextjs/scripts/deploy-ipfs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { exec } from "child_process";
import { dirname, join } from "path";
import { fileURLToPath } from "url";
import { promisify } from "util";

const execAsync = promisify(exec);
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

async function checkIpfsDaemon() {
try {
await execAsync("ipfs --version");
// Check if daemon is running and has peers
const { stdout } = await execAsync("ipfs swarm peers");
const peerCount = stdout.split("\n").filter(Boolean).length;

if (peerCount < 1) {
console.log("⚠️ Warning: Your IPFS node has no peers. Content might not be accessible immediately.");
console.log("Waiting for peers to connect...");
// Wait for peers to connect
await new Promise(resolve => setTimeout(resolve, 10000));
const { stdout: newStdout } = await execAsync("ipfs swarm peers");
const newPeerCount = newStdout.split("\n").filter(Boolean).length;
if (newPeerCount < 1) {
console.log("Still no peers connected. You might want to:");
console.log("1. Check your internet connection");
console.log("2. Ensure your IPFS daemon is not behind a firewall");
console.log("3. Try running 'ipfs daemon --enable-pubsub-experiment' for better connectivity");
} else {
console.log(`✓ Connected to ${newPeerCount} peers`);
}
} else {
console.log(`✓ Connected to ${peerCount} peers`);
}
} catch (error) {
console.log(error);
console.error("❌ IPFS is not installed or daemon is not running.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to avoid daemon? For example using https://web3.storage/docs/w3up-client/ or https://docs.storacha.network/w3up-client/ ? Asking without trying, but probably it simplifies DX

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that is a valid concern, but I wanted to keep everything manual just for the sake of showing that it can be done this way.

console.log("Please install IPFS and start the daemon:");
console.log("1. Install IPFS: https://docs.ipfs.tech/install/");
console.log("2. Start the daemon: ipfs daemon");
process.exit(1);
}
}

async function addDirectoryToIpfs(path) {
console.log("📦 Adding directory to IPFS...");

try {
// Add the entire directory to IPFS and get the hash
const { stdout } = await execAsync(`ipfs add -r -Q "${path}"`);
const cid = stdout.trim();

// Announce the content to the network
try {
await execAsync(`ipfs dht provide ${cid}`);
console.log("✓ Announced content to the IPFS network");
} catch (error) {
console.log(error);
console.log("⚠️ Warning: Could not announce content to the network. Content might take longer to be available.");
}

return cid;
} catch (error) {
console.error("Error adding directory to IPFS:", error);
throw error;
}
}

async function main() {
// First check if IPFS is installed and running
await checkIpfsDaemon();

// Get the path to the out directory
const outDir = join(__dirname, "..", "out");

console.log("🚀 Uploading to IPFS...");
const cid = await addDirectoryToIpfs(outDir);

// Give the network some time to propagate the content
console.log("\n⏳ Waiting for network propagation...");
await new Promise(resolve => setTimeout(resolve, 5000));

console.log("\n✨ Upload complete! Your site is now available at:");
console.log(`🔗 IPFS Gateway: https://ipfs.io/ipfs/${cid}`);
console.log("\n💡 To ensure your site stays available:");
console.log("1. Keep your IPFS daemon running");
console.log("2. Pin the CID on other nodes: ipfs pin add " + cid);
console.log("\n💡 If the gateway times out, you can:");
console.log("1. Wait a few minutes and try again");
console.log("2. Install the IPFS Companion browser extension");
}

main().catch(err => {
console.error("Error uploading to IPFS:", err);
process.exit(1);
});
Loading