Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

P2p/rework ipfs #112

Merged
merged 7 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
190 changes: 190 additions & 0 deletions p2p/10.IPFS_or_HTTP/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# 10. Learn the differences between HTTP and IPFS

## 💫 Table of contents

* [Step 0 - Setup](README.md#🔧-step-0---setup)
* [Step 1 - HTTP](README.md#step-1---http)
* [Discover the basics](README.md#✏️-10-discover-the-basics)
* [Storage](README.md#💾-11-storage)
* [Step 2 - IPFS](README.md#step-2---ipfs)
* [Improve the storage](README.md#🕸️-20-improve-the-storage)
* [Retrieve](README.md#📥-22-retrieve)
* [Going further](README.md#🚀-going-further)

In this Workshop, you will learn :

✔️ The basics of HTTP

✔️ The basics of IPFS, and why in some cases it is better than HTTP

✔️ How to change a centralized storage into a distributed one via IPFS with Infura !

## 🔧 Step 0 - Setup

Please follow each instruction on the [SETUP.md](SETUP.md) file.

## Step 1 - HTTP

### ✏️ 1.0 Discover the basics

Wanna launch the back-end? It's very simple, just run the container you pulled in the setup:

```
docker run -p 8080:8080 sacharbon/workshop-ipfs
```

you should have this log :

```bash
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET /images --> main.getImages (4 handlers)
[GIN-debug] GET /images/:id --> main.getImageByID (4 handlers)
[GIN-debug] POST /upload --> main.uploadImage (4 handlers)
[GIN-debug] GET /uploads/*filepath --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (4 handlers)
[GIN-debug] HEAD /uploads/*filepath --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (4 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on 0.0.0.0:8080
```

As you can see, there is different route for the back-end of our website. You can open your favorite browser like Firefox or Chrome (or Opera,
no discrimination here) and go to this URL: [http://0.0.0.0:8080/images](http://0.0.0.0:8080/images).

You should see an empty array !!

If you want to shut down the server, use `Ctrl` + `C` and then run `docker stop ${id}`.

Now it's time to wake the front-end up.

Go to [the source of the front-end](./openocean/frontend/) and type `bun i` to download the dependencies. Then, type `bun dev` to launch the front-side of our web-app.

Now visit this URL: [localhost:5173](localhost:5173). You should see a pretty UI made by two genius.

### 💾 1.1 Storage

Go to [Unsplash](https://unsplash.com/photos/a-woman-sitting-at-a-table-using-a-cell-phone-nplkFSNschY) website and download the image.
By the way, you can look for any other image you prefer on this website, it is free and open source. For the example, we are going to stick with this image.
Go back to [http://localhost:5173/](http://localhost:5173), scroll down and click on the button on the `up right`.
Fill the form correctly and validates it.

Now, look at your terminal, you should see those strange logs appear:

```bash
[GIN] 2024/12/02 - 19:10:29 | 200 | 85.347µs | 172.17.0.1 | GET "/images"
[GIN] 2024/12/02 - 19:10:29 | 200 | 140.871µs | 172.17.0.1 | GET "/images"
[GIN] 2024/12/02 - 19:10:37 | 200 | 2.93712ms | 172.17.0.1 | POST "/upload"
[GIN] 2024/12/02 - 19:10:37 | 200 | 30.521µs | 172.17.0.1 | GET "/images/08245a6c-0843-4f68-bb41-c1dea72369c7"
[GIN] 2024/12/02 - 19:10:37 | 200 | 15.784µs | 172.17.0.1 | GET "/images/08245a6c-0843-4f68-bb41-c1dea72369c7"
[GIN] 2024/12/02 - 19:10:37 | 200 | 5.895332ms | 172.17.0.1 | GET "/uploads/65fb793f-a8b5-4c88-9fbc-6432d40961ba.png"

```

Let me explain:
The first part of the message is obviously the date-time. The second one is two numbers. The `200` is the most interesting : it is a status, preview code. `200` means that the server is OK to give us that page from the `/images` route, and it has been delivered correctly.
Then, the time it took to respond to the request. Afterward, the address which requested. And finally, the method, `GET`, because we want to just get the page ; we are asking the server to give us the `/images` route which is the home page.

![](http_request_flowchart.png)
*Scheme of a HTTP request*

>💡 What does `POST` means ?

`POST` is another **HTTP method** than `GET`. When you fill the form earlier, it was **you** that was giving the server
some information :that is the main difference between `POST` and `GET`.

> 💡 Learn more about HTTP methods [here](https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol).

Then, go to look at our `uploads` folder : you have the file you just downloaded in here !
>💡 This is how HTTP works. When retrieving data, HTTP focuses on **location**.

## Step 2 - IPFS

> 💡 HTTP is cool but has its limits : if the server is down, you won't be able to retrieve the data stored. Furthermore, your government can easily block access to certain servers by their IPs that host particular website for censure purposes.
Let's see how IPFS answers this issues.

At its core, IPFS is a [distributed system](https://en.wikipedia.org/wiki/Distributed_computing) for storing and accessing files, websites, applications, and data.
Instead of referring to data (photos, articles, videos) by **location**, or which server they are stored on, IPFS refers
to everything by that data’s [hash](https://docs.ipfs.io/concepts/hashing/#hashes-are-important), meaning the **content itself.**

The idea is that if you want to access a particular page from your browser, IPFS will ask the entire network, “does anyone
have the data that corresponds to this hash?” A node on IPFS that contains the corresponding hash will return the data, allowing you to access it from anywhere (and potentially even offline).

If this is not enough clear for you, I strongly advise you to refer to this [video (Simply Explained IPFS)](https://www.youtube.com/watch?v=5Uj6uR3fp-U).

### 🕸️ 2.0 Improve the storage

Here is what we are going to do : We are going to upload our files directly on IPFS and not locally anymore.
Instead of having the file locally, let's pin it with [Pinata](https://pinata.cloud/). Which is a pinning service.

1. Go to `frontend/src/hooks/` and create a new hook `usePinFileToIPFS.ts` as a manner of `usePostImage.ts` It should call the [list file Pinata API route](https://docs.pinata.cloud/api-reference/endpoint/list-files).
> 💡 Don't forget to create an [Pinata API & Gateway key](https://app.pinata.cloud/developers/api-keys) and write it down into your a `.env`. You can create one by typing in your terminal in `frontend/` folder:

```
cp .env.dist .env
```

2. Go to `frontend/src/pages/Upload.tsx` and modify the code of the upload view to communicate with your new hook in order to upload the file there.
3. Go https://app.pinata.cloud/pinmanager and make sure the hash of the song appears.

<details>
<summary>Some Trouble with IPFS API ?</summary>
Here is some links that could help you:
<li>
<a href="https://en.wikipedia.org/wiki/API">What is an API ?</a>
</li>
<li>
<a href="https://docs.pinata.cloud/quickstart">Infura IPFS API</a>
</li>
<li>
<a href="https://axios-http.com/fr/docs/intro">ipfs-Api python package</a>
</li>
</details>

### 📥 2.2 Retrieve

Last step : if anyone wants to see from our website some images, we need to get it from IPFS.
Since you did the previous step, this one would seem easy : in your `src/page`
on the `index.tsx`, do the same thing as previously but instead of adding a file, call the get method to retrieve your image.

## 🚀 Going further

A very cool feature with IPFS is that if someone is having an IPFS node running on its machine and download your image then you deleted it, you will be able to retrieve it from its node !

* Learn [how](https://docs.ipfs.io/how-to/command-line-quick-start) you can deploy and configure your own IPFS node.
* Want to store a lot of data on IPFS but being the only one that can access it? Look at [OrbitDB](https://orbitdb.org/).

## Authors

| [<img src="https://github.com/sacharbon.png?size=85" width=85><br><sub>Sacha Dujardin</sub>](https://github.com/Sacharbon) |
| :---: |

<h2 align=center>
Organization
</h2>
<br/>
<p align='center'>
<a href="https://www.linkedin.com/company/pocinnovation/mycompany/">
<img src="https://img.shields.io/badge/LinkedIn-0077B5?style=for-the-badge&logo=linkedin&logoColor=white" alt="LinkedIn logo">
</a>
<a href="https://www.instagram.com/pocinnovation/">
<img src="https://img.shields.io/badge/Instagram-E4405F?style=for-the-badge&logo=instagram&logoColor=white" alt="Instagram logo"
>
</a>
<a href="https://twitter.com/PoCInnovation">
<img src="https://img.shields.io/badge/Twitter-1DA1F2?style=for-the-badge&logo=twitter&logoColor=white" alt="Twitter logo">
</a>
<a href="https://discord.com/invite/Yqq2ADGDS7">
<img src="https://img.shields.io/badge/Discord-7289DA?style=for-the-badge&logo=discord&logoColor=white" alt="Discord logo">
</a>
</p>
<p align=center>
<a href="https://www.poc-innovation.fr/">
<img src="https://img.shields.io/badge/WebSite-1a2b6d?style=for-the-badge&logo=GitHub Sponsors&logoColor=white" alt="Website logo">
</a>
</p>

> 🚀 Don't hesitate to follow us on our different networks, and put a star 🌟 on `PoC's` repositories.
10 changes: 10 additions & 0 deletions p2p/10.IPFS_or_HTTP/SETUP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Setup 🔧

In order to launch our image platform, you have to download Docker and Bun.

* [Docker](https://docs.docker.com/get-started/get-docker/)
Once docker is downloaded, pull this docker repo `sacharbon/workshop-ipfs`

* [Bun](https://bun.sh/)

[Go back to the exercise](README.md#step-1---http)
2 changes: 2 additions & 0 deletions p2p/10.IPFS_or_HTTP/openocean/frontend/.env.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
VITE_PINATA_API_KEY=
VITE_PINATA_GATEWAY=
18 changes: 18 additions & 0 deletions p2p/10.IPFS_or_HTTP/openocean/frontend/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
26 changes: 26 additions & 0 deletions p2p/10.IPFS_or_HTTP/openocean/frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

.env
Binary file added p2p/10.IPFS_or_HTTP/openocean/frontend/bun.lockb
Binary file not shown.
13 changes: 13 additions & 0 deletions p2p/10.IPFS_or_HTTP/openocean/frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/openocean.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OpenOcean</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
40 changes: 40 additions & 0 deletions p2p/10.IPFS_or_HTTP/openocean/frontend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"@chakra-ui/react": "^2.8.2",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@tanstack/react-query": "^5.51.11",
"axios": "^1.7.2",
"env-var": "^7.5.0",
"framer-motion": "^11.2.11",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.52.0",
"react-icons": "^5.2.1",
"react-router-dom": "^6.23.1",
"vite-plugin-node-polyfills": "^0.22.0",
"zustand": "^4.5.2"
},
"devDependencies": {
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
"typescript": "^5.2.2",
"vite": "^5.2.0"
}
}
1 change: 1 addition & 0 deletions p2p/10.IPFS_or_HTTP/openocean/frontend/public/_redirects
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/* /index.html 200
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions p2p/10.IPFS_or_HTTP/openocean/frontend/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
55 changes: 55 additions & 0 deletions p2p/10.IPFS_or_HTTP/openocean/frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import Header from "./organisms/Header";
import { BrowserRouter, Outlet, Route, Routes } from "react-router-dom";
import theme, { colors } from "./theme";
import { ChakraProvider, VStack } from "@chakra-ui/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import Home from "./pages";
import { FC } from "react";
import ImageDetailsPage from "./pages/images/:id";
import ImagePage from "./pages/images";
import UploadPage from "./pages/Upload";

const Layout: FC = () => (
<VStack
h="100vh"
w="100vw"
bg={`linear-gradient(180deg, ${colors.gray[900]}, ${colors.gray[700]})`}
>
<Header />
<VStack
overflowY="scroll"
w="100%"
h="100%"
px="84px"
py="32px"
spacing="48px"
>
<Outlet />
</VStack>
</VStack>
);

const queryClient = new QueryClient;

const App: FC = () => {
return (
<QueryClientProvider client={queryClient}>
<ChakraProvider theme={theme}>
<BrowserRouter>
<Routes>
<Route element={<Layout />}>
<Route index element={<Home />} />
<Route path="/upload" element={<UploadPage />} />
<Route path="/images/*">
<Route index element={<ImagePage />} />
<Route path=":id" element={<ImageDetailsPage />} />
</Route>
</Route>
</Routes>
</BrowserRouter>
</ChakraProvider>
</QueryClientProvider>
);
};

export default App;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { HStack } from "@chakra-ui/react";
import { FC, PropsWithChildren } from "react";

const HeaderContainer: FC<PropsWithChildren> = ({ children }) => (
<HStack
width="100%"
height="64px"
bg="gray.900"
spacing={0}
justify="space-between"
userSelect="none"
p="24px"
boxShadow="lg"
>
{children}
</HStack>
);

export default HeaderContainer;
Loading
Loading