Error uploading file to IPFS: HTTPError: project id required
Asked Answered
B

4

7

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;

Borlow answered 11/8, 2022 at 8:51 Comment(0)
M
6

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

Moorish answered 11/8, 2022 at 10:10 Comment(12)
I am still having error. Do I have to use the project id and project secret of an ipfs project in infura? But it needs to give payment info.Apc
Another question, I am getting this error from today. Is it a new update that is causing these errors ?Apc
@NishatAnjumLea Hey, I'm not sure why you didn't get the error before, but maybe you reached some quotas? But yeah, I'm pretty sure you will have to provide credit-card payment details even for the free tier (5 GB storage) of IPFS API access, which is understandable since Infura would be spammed otherwise.Moorish
When I create an IPFS project it instantly asks me for a credit card, so I don't know how you used the API before providing payment details 😅Moorish
I just used this line : const client = ipfsHttpClient('ipfs.infura.io:5001/api/v0') and did not even use any api key or project id . I was testing my project on hardhat testnet. Currently I am trying to develop it in testnet also.Apc
@NishatAnjumLea I'm pretty sure you cannot perform any (or most) actions without creating an Infura IPFS project with your own credentials.Moorish
I had it working last night with the above code but now it's giving the project ID needed error again too.Averse
same thing was happening with me 2 days ago. it was working fine but yesterday it is demanding project id and authorisation. is there a way to achieve this functionality using alchemy? since i am a student, it is difficult to convince my parents to submit the credit card credentialsHorseweed
I am planning on using filecoin instead of ipfs for this reason. @HorseweedApc
can you refer me some good resource to follow so i can learn more about it? @NishatAnjumLeaHorseweed
@Horseweed I have not started working with Filecoin yet. I will surely give you good resources once I start working.Apc
@Horseweed link.medium.com/Y6zW9vQOaubApc
S
3

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

Shabuoth answered 19/8, 2022 at 15:46 Comment(0)
R
2

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.

Riti answered 18/8, 2022 at 16:8 Comment(1)
have you found alternatives to ifura-gateways?Horseweed
T
0

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

Tennilletennis answered 14/12, 2022 at 3:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.