Reputation: 93
Currently making a rudimentary DApp for posting chats, similar to twitter except built on a smart contract. I am using hardhat and running my app on localhost. When creating a profile I want users to be able to upload a profile picture, however currently whenever I try to do so I get the following error:
POST https://ipfs.infura.io:5001/api/v0/add?stream-channels=true&progress=false 401 (Unauthorized)
Accompanied by the error message:
Error uploading file: HTTPError: project id required
at Object.errorHandler [as handleError] (core.js?edc8:103:1)
at async Client.fetch (http.js?8f3e:149:1)
at async addAll (add-all.js?93f2:36:1)
at async last (index.js?7e49:13:1)
at async Object.add (add.js?6672:22:1)
The console says that the error is occurring in this function:
const uploadToInfura = async (file) => {
try {
const added = await client.add({ content: file });
const url = `https://ipfs.infura.io/ipfs/${added.path}`;
setFileUrl(url);
} catch (error) {
console.log('Error uploading file: ', error);
}
};
I will attach the entire code for this page below, if you can please let me know what I need to fix in order to get this error to stop happening. Any other tips on what I could improve in general would also be appreciated :)
import { useState, useEffect, useContext, useCallback, useMemo } from 'react'
import { useRouter } from 'next/router';
import { useDropzone } from 'react-dropzone';
import Image from 'next/image';
import { useTheme } from 'next-themes';
import { ethers } from "ethers";
import Web3Modal from 'web3modal';
import { Input, Button, Banner, SearchBar, PostCard, PostCardNFT, SmallInput } from '../components';
import images from '../assets';
import DecentratwitterAbi from './contractsData/decentratwitter.json';
import DecentratwitterAddress from './contractsData/decentratwitter-address.json';
import { Home } from './index'
import { create as ipfsHttpClient } from 'ipfs-http-client';
const client = ipfsHttpClient('https://ipfs.infura.io:5001/api/v0');
const Profile = () => {
const [profile, setProfile] = useState('');
const [posts, setPosts] = useState('');
const [nfts, setNfts] = useState('');
const [fileUrl, setFileUrl] = useState(null);
const [isloading, setIsLoading] = useState(true);
const { theme } = useTheme();
const [files] = useState([]);
const [formInput, updateFormInput] = useState({ username: '' });
const router = useRouter();
const uploadToInfura = async (file) => {
try {
const added = await client.add({ content: file });
const url = `https://ipfs.infura.io/ipfs/${added.path}`;
setFileUrl(url);
} catch (error) {
console.log('Error uploading file: ', error);
}
};
const createProfile = async () => {
const web3Modal = new Web3Modal();
const connection = await web3Modal.connect();
const provider = new ethers.providers.Web3Provider(connection);
const signer = provider.getSigner();
const contract = new ethers.Contract(
DecentratwitterAddress.address,
DecentratwitterAbi.abi,
signer
);
const { username } = formInput;
if (!username || !fileUrl) return;
/* first, upload to IPFS */
const data = JSON.stringify({ username, avatar: fileUrl });
try {
const added = await client.add(data);
const url = `https://ipfs.infura.io/ipfs/${added.path}`;
/* after file is uploaded to IPFS, pass the URL to save it on Polygon */
await contract.mint(url);
fetchMyNFTs();
} catch (error) {
console.log('Error uploading file: ', error);
}
};
const fetchProfile = async (nfts) => {
const web3Modal = new Web3Modal();
const connection = await web3Modal.connect();
const provider = new ethers.providers.Web3Provider(connection);
const signer = provider.getSigner();
const contract = new ethers.Contract(
DecentratwitterAddress.address,
DecentratwitterAbi.abi,
signer
);
const address = await contract.signer.getAddress();
const id = await contract.profiles(address);
const profile = nfts.find((i) => i.id.toString() === id.toString());
setProfile(profile);
setIsLoading(false);
};
const loadPosts = async () => {
const web3Modal = new Web3Modal();
const connection = await web3Modal.connect();
const provider = new ethers.providers.Web3Provider(connection);
const signer = provider.getSigner();
const contract = new ethers.Contract(
DecentratwitterAddress.address,
DecentratwitterAbi.abi,
signer
);
// Get user's address
let address = await contract.signer.getAddress()
setAddress(address)
// Check if user owns an nft
// and if they do set profile to true
const balance = await contract.balanceOf(address)
setHasProfile(() => balance > 0)
// Get all posts
let results = await contract.getAllPosts()
// Fetch metadata of each post and add that to post object.
let posts = await Promise.all(results.map(async i => {
// use hash to fetch the post's metadata stored on ipfs
let response = await fetch(`https://ipfs.infura.io/ipfs/${i.hash}`)
const metadataPost = await response.json()
// get authors nft profile
const nftId = await contract.profiles(i.author)
// get uri url of nft profile
const uri = await contract.tokenURI(nftId)
// fetch nft profile metadata
response = await fetch(uri)
const metadataProfile = await response.json()
// define author object
const author = {
address: i.author,
username: metadataProfile.username,
avatar: metadataProfile.avatar
}
// define post object
let post = {
id: i.id,
content: metadataPost.post,
tipAmount: i.tipAmount,
author
}
return post
}))
posts = posts.sort((a, b) => b.tipAmount - a.tipAmount)
// Sort posts from most tipped to least tipped.
setPosts(posts)
setLoading(false)
};
const fetchMyNFTs = async () => {
const web3Modal = new Web3Modal();
const connection = await web3Modal.connect();
const provider = new ethers.providers.Web3Provider(connection);
const signer = provider.getSigner();
const contract = new ethers.Contract(
DecentratwitterAddress.address,
DecentratwitterAbi.abi,
signer
);
const results = await contract.getMyNfts();
let nfts = await Promise.all(results.map(async i => {
const uri = await contract.tokenURI(i);
const response = await fetch(uri);
const metadata = await response.json();
return ({
id: i,
username: metadata.username,
avatar: metadata.avatar
});
}));
setNfts(nfts);
fetchProfile(nfts);
};
const tip = async (post) => {
const web3Modal = new Web3Modal();
const connection = await web3Modal.connect();
const provider = new ethers.providers.Web3Provider(connection);
const signer = provider.getSigner();
const contract = new ethers.Contract(
DecentratwitterAddress.address,
DecentratwitterAbi.abi,
signer
);
// tip post owner
await (await contract.tipPostOwner(post.id, { value: ethers.utils.parseEther(tipAmount) })).wait()
fetchMyNFTs()
}
useEffect(() => {
fetchMyNFTs()
.then((nfts) => {
setNfts(nfts);
setIsLoading(false);
});
}, []);
const onDrop = useCallback(async (acceptedFile) => {
await uploadToInfura(acceptedFile[0]);
}, []);
const { getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject } = useDropzone({
onDrop,
accept: 'image/*',
maxSize: 5000000,
});
const fileStyle = useMemo(
() => (
`dark:bg-nft-black-1 bg-white border dark:border-white border-nft-gray-2 flex flex-col items-center p-5 rounded-sm border-dashed
${isDragActive ? ' border-file-active ' : ''}
${isDragAccept ? ' border-file-accept ' : ''}
${isDragReject ? ' border-file-reject ' : ''}`),
[isDragActive, isDragReject, isDragAccept],
);
return (
<div className="w-full flex justify-start items-center flex-col min-h-screen">
<div className="w-full flexCenter flex-col">
<Banner
name={
profile ? (
<div>{profile.username}</div>
) : (
"No profile, please create one"
)
}
childStyles="text-center mb-4"
parentStyles="h-80 justify-center"
/>
<div className="flexCenter flex-col -mt-20 z-0">
<div className="flexCenter w-40 h-40 sm:w-36 sm:h-36 p-1 bg-nft-black-2 rounded-full">
{profile ? (
<Image src={profile.avatar} className="rounded-full object-cover" objectFit="cover" width="200%" height="200%" alt='avatar' />
) : (
"No Profile"
)
}
</div>
<p className="font-poppins dark:text-white text-nft-black-1 font-semibold text-2xl mt-6"></p>
</div>
</div>
{profile ? (
<div>
{nfts ? (
nfts.map((nft, key) => {
<div key={key} >
<PostCardNFT
image={<Image src={nft.author.avatar} layout="fixed" width='60' height='40' alt='post' className='rounded-[6px]' />}
content={nft.content}
tipAmount={ethers.utils.formatEther(post.tipAmount)}
address={shortenAddress(post.author.address)}
tipInput={
<div className='pb-2'>
<SmallInput
inputType='number'
title='Tip'
placeholder='ETH'
handleClick={(e) => setTipAmount(e.target.value)}
/>
</div>
}
button={
<Button
btnName="Tip"
btnType="primary"
classStyles="rounded-xl"
handleClick={() => tip(post)}
/>
}
/>
</div>
})
) : (
<div className="text-2xl font-bold pt-20">
No Posts, Create One ...
</div>
)}
</div>
) : (
<div className="w-full px-20">
<Input
inputType="input"
title="Username"
placeholder="Input Username"
handleClick={(e) => updateFormInput({ ...formInput, username: e.target.value })}
/>
<div className="mt-16">
<p className="font-poppins dark:text-white text-nft-black-1 font-semibold text-xl">Profile Avatar</p>
<div className="mt-4">
<div {...getRootProps()} className={fileStyle}>
<>
{fileUrl ? (
<div>
<Image
src={fileUrl}
className="object-cover"
objectFit="cover"
width="200%"
height="200%"
alt="Asset_file" />
</div>
) : (
<div>
<input {...getInputProps()} /><div className="flexCenter flex-col text-center">
<p className="font-poppins dark:text-white text-nft-black-1 font-semibold text-xl">JPG, PNG, GIF, SVG, WEBM, MP3, MP4. Max 100mb.</p>
<div className="my-12 w-full flex justify-center">
<Image
src={images.upload}
width={100}
height={100}
objectFit="contain"
alt="file upload"
className={theme === 'light' ? 'filter invert' : undefined}
/>
</div>
<p className="font-poppins dark:text-white text-nft-black-1 font-semibold text-sm">Drag and Drop File</p>
<p className="font-poppins dark:text-white text-nft-black-1 font-semibold text-sm mt-2">Or browse media on your device</p>
</div>
</div>
)}
</>
</div>
</div>
</div>
<div className="mt-7 w-full flex justify-end">
<Button
btnName="Mint Profile"
btnType="primary"
classStyles="rounded-xl"
handleClick={createProfile} />
</div>
</div>
)}
</div>
);
};
export default Profile;
Upvotes: 7
Views: 9027
Reputation: 447
I wasted my whole day to resolve this issue, update code in different ways, finally I create new app on infura and test code with new project id and secret, it is working for me :
JAVASCRIPT CODE :
const projectId = process.env.IPFS_PROVIDER_PROJECTID;
const projectSecret = process.env.IPFS_PROVIDER_PROJECTSECRET;
const ipfsClient = require(‘ipfs-http-client’);
async function main() {
const auth = 'Basic ’ + Buffer.from(projectId + ‘:’ + projectSecret).toString(‘base64’);
const client = await ipfsClient.create({
host: ‘ipfs.infura.io’,
port: 5001,
protocol: ‘https’,
apiPath: ‘/api/v0’,
headers: {
authorization: auth
}
})
try {
const file = await client.add(‘test.png’)
console.log(file)
} catch (error) {
console.log(error)
}
}
main();
CURL :
curl -X POST -F [email protected] -u “KKKKKKKEY:SSSSSSECRET” “https://ipfs.infura.io:5001/api/v0/add” --insecure -A “curl”
it seems not working with old details!
thanks
Upvotes: 0
Reputation: 31
Step 1 : Create a IPFS Infura project by putting card details
Step 2: Enable Dedicated Gateway give any subdomain name.
Step 3 : npm install --save buffer
Step 4 : Add below code in your project
import { Buffer } from 'buffer';
const ipfsClient = require('ipfs-http-client');
const projectId = '---Enter projectID from infura.io project---';
const projectSecret = '---Enter project secrate key from infura.io project---';
const auth =
'Basic ' + Buffer.from(projectId + ':' + projectSecret).toString('base64');
const client = ipfsClient.create({
host: 'ipfs.infura.io',
port: 5001,
protocol: 'https',
headers: {
authorization: auth,
},
});
Step 5: use similar dedicated Gateway to see your uploaded image
https://(your gateway name).infura-ipfs.io/ipfs/${result.path}
https://github.com/dappuniversity/nft_marketplace/issues/5#issuecomment-1219131315
Upvotes: 3
Reputation: 31
I was also having this problem this week, August 2022.
Authorization credentials are a recent requirement. Infura has articles on their site from 2020-2021 that do not include any mention of authorization credentials. They also have a recent article and video that do require auth to access IPFS through their API endpoint. Link below for deprecation of the public API, relevant info towards the end of the article.
https://blog.infura.io/post/introducing-ipfs-dedicated-gateways
Storing auth tokens in .env is always considered best practice so you don’t leak them when committing to your public git repo.
Unfortunately this is still not enough to secure your tokens if you deploy your app online. Lots of resources online to address this once you get there.
Upvotes: 2
Reputation: 1515
You are missing authorization headers that tell Infura who you are, and that you're authorized to access the API.
Here's the snippet from the docs:
const ipfsClient = require('ipfs-http-client');
const projectId = '1qmt...XXX'; // <---------- your Infura Project ID
const projectSecret = 'c920...XXX'; // <---------- your Infura Secret
// (for security concerns, consider saving these values in .env files)
const auth = 'Basic ' + Buffer.from(projectId + ':' + projectSecret).toString('base64');
const client = ipfsClient.create({
host: 'ipfs.infura.io',
port: 5001,
protocol: 'https',
headers: {
authorization: auth,
},
});
You just have to add an options object with your credentials to
const client = ipfsHttpClient('https://ipfs.infura.io:5001/api/v0');
as shown above.
Source: https://docs.infura.io/infura/networks/ipfs/how-to/make-requests#ipfs-http-client
Upvotes: 6