페이스트잇! - IPFS의 페이스트빈
나는 몇 년 전에 IPFS를 우연히 발견했고 그것이 흥미로웠다. 그 당시 IPFS에 액세스하는 유일한 방법은 자체 노드를 가동하는 것이었습니다(확실하지 않음, 연구 부족일 수 있음). 오늘날 우리는 여러 개의 무료 IPFS 엔드포인트를 보유하고 있습니다. 이러한 끝점을 사용하여 IPFS 네트워크와 상호 작용할 수 있습니다.
이 기사는 IPFS 네트워크에 텍스트 데이터를 저장하는 방법에 관한 것입니다. 이것은 무료로 데이터를 저장하기 위해 IPFS 네트워크를 사용하여 지난 며칠 동안 작업한 것입니다.
사용 도구
백엔드를 사용하는 이유는 무엇입니까?
javascript의
fetch
API를 사용하여 IPFS 끝점에 대한 가져오기/게시 요청을 쉽게 만들 수 있습니다. 그러나 문제는 IPFS가 각 파일에 대한 해시를 생성한다는 것입니다. 이 해시는 파일 식별에 사용할 수 있습니다.QmR7GSQM93Cx5eAg6a6yRzNde1FQv7uL6X1o4k7zrJa3LX
그러나 이러한 해시를 기억하는 것은 쉬운 일이 아니므로 데이터베이스를 사용하여 이러한 해시에 대한 별칭을 저장해야 합니다.
FastAPI는 전체 프로그램 흐름을 규제합니다. 서비스 간 통신을 위한 API를 구축합니다.
애플리케이션 구축
설정 환경 변수
# .env
MONGO_CON_STRING=mongodb://localhost:27017/
몽고디비 설정
Docker를 사용하여 MongoDB를 가동해 봅시다. Docker는 로컬 설치 및 기타 기본 설정에 대한 오버헤드를 제거합니다.
# pull MongoDB
docker pull mongo
# Start mongo container
docker run -it -v mongodata:/data/db -p 27017:27017 --name ipfs-store -d mongo
-v mongodata:/data/db
-v
는 볼륨을 지정하기 위한 것입니다. 컨테이너가 중지된 후에도 데이터를 유지하려면 MongoDB 저장소를 로컬 디렉터리에 매핑하는 것이 중요합니다. 컨테이너의 /data/db
를 프로젝트 디렉토리의 mongodata
에 매핑합니다. mongodata
폴더가 있는지 확인하십시오.# requirements.txt
aiofiles==0.5.0
fastapi==0.61.1
ipfs-api==0.2.3
pymongo==3.11.0
sqlitedict==1.7.0
uvicorn==0.12.2
데이터베이스 코딩
pymongo
를 사용하여 데이터베이스와 통신합니다.# database/database.py
class DataBase:
def __init__(self) -> None:
self.client = MongoClient(getenv("MONGO_CON_STRING"))
self.db = self.client.pasteit
self.col = self.db.links
def set(self, short: str, hash: str) -> str:
short_exists = self.col.find_one({"hash": hash})
if short_exists is not None:
return short_exists.get("short")
data = {"short": short, "hash": hash}
self.col.insert_one(data)
return short
def get(self, short: str) -> str:
data = self.col.find_one({"short": short})
if data is not None:
return data.get("hash")
return None
def close(self) -> None:
self.client.close()
이와 같이 추상화를 생성하면 코드를 쉽게 읽을 수 있습니다. 작업을 완료하기 위해 일련의 pymongo 작업으로
set
및 get
메서드를 정의했습니다.모든 데이터베이스 삽입은 이 형식입니다.
{
"short": "hash"
}
모든 삽입
key: value
을 기반으로 하고 있으므로 여기에서 Redis를 사용할 수도 있습니다. 이 응용 프로그램은 MongoDB atlas와 함께 vercel에 배포되기 때문에 MongoDB를 사용했습니다.위의 코드는 상당히 간단합니다. 제공된 short를 기반으로 해시를 가져오는
get
메서드를 만듭니다. set
쌍을 저장하기 위해 short: hash
메서드를 정의합니다. 그러나 먼저 해시가 이미 데이터베이스에 있지 않은지 확인합니다.IPFS 연결 만들기
# ipfs/ipfs.py
class IPFS:
def __init__(self) -> None:
self.ipfs = ipfsApi.Client("https://ipfs.infura.io", 5001)
def add(self, text: str) -> str:
filename = f"/tmp/{str(uuid4())}"
with open(filename, "w") as f:
f.write(text)
res = self.ipfs.add(filename)
remove(filename)
print(res)
return res[0].get("Hash")
def cat(self, hash: str) -> str:
data = self.ipfs.cat(hash)
return data
IPFS 끝점과의 통신은 페이로드가 있는 간단한 get/post 요청이지만 인코딩을 관리해야 합니다. 나는 우리를 위해 이미 기본적인 일을 한 라이브러리를 사용했습니다.
입력 문자열을 파일에 쓴 다음 IPFS에 업로드하는
add
메서드를 정의합니다. cat
메서드는 해시를 사용하여 데이터를 읽습니다.서버 코딩
서버에는 두 개의 엔드포인트가 있습니다.
/api/v1
는 업로드할 텍스트를 게시하고 /
는 짧은 URL을 사용하여 데이터를 가져옵니다.# main.py
async def connection() -> dict:
return {"db": DataBase(), "ipfs": IPFS()}
@app.post("/api/v1/")
async def pasteit(data: Data, con: dict = Depends(connection)) -> dict:
hash = con["ipfs"].add(data.text)
short = str(uuid4())[:6]
short = con["db"].set(short, hash)
con["db"].close()
return {"message": short}
@app.get("/{short}")
async def get_paste(short: str, con: dict = Depends(connection)) -> dict:
hash = con["db"].get(short)
if hash is not None:
data = con["ipfs"].cat(hash)
return {"message": data}
con["db"].close()
return {"message": "invalid short"}
여기서는 모든 데이터가 성공적으로 업로드되었다고 가정합니다. 그런 다음
uuid.uuid4()
의 처음 6자를 사용하여 각 해시에 대한 사용자 정의 식별자를 생성합니다. 이 짧은 생성 방법에 대해 충돌 테스트를 수행해야 합니다.# collision_test.py
from uuid import uuid4
def get_id() -> str:
return str(uuid4())[:6]
def test_n(n: int) -> None:
outputs = [get_id() for _ in range(n)]
unique_outputs = set(outputs)
fraction = 1 - (len(unique_outputs) / len(outputs))
print(f"Test for {n} shorts, collision: {fraction*100:.2f}")
if __name__ == "__main__":
test_n(100)
test_n(1000)
test_n(10000)
test_n(100000)
test_n(1000000)
-> python collision_test.py
Test for 100 shorts, collision: 0.00
Test for 1000 shorts, collision: 0.00
Test for 10000 shorts, collision: 0.05
Test for 100000 shorts, collision: 0.26
Test for 1000000 shorts, collision: 2.93
-> python collision_test.py
Test for 100 shorts, collision: 0.00
Test for 1000 shorts, collision: 0.00
Test for 10000 shorts, collision: 0.01
Test for 100000 shorts, collision: 0.27
Test for 1000000 shorts, collision: 2.92
n=1,000,000
를 제외하고는 테스트가 통과된 것 같습니다. ~30,000번의 충돌이 발생했습니다. 그러나 단기간에 그렇게 많은 요청을 받지는 않을 것이라고 가정하는 것이 안전합니다.프론트엔드
# src/App.svelte
<script>
let data = "";
let hash = "";
const upload = () => {
fetch("http://localhost:8000/api/v1", {
method: "POST",
body: JSON.stringify({ text: data }),
})
.then((res) => res.json)
.then((data) => (hash = data.message));
};
</script>
<textarea id="data" bind:value={data} />
<button id="upload" on:click={upload}>Upload</button>
<p>{hash}</p>
이 코드는 프런트엔드 빌드에 대한 공정한 아이디어를 제공합니다. 현재 텍스트 제한은 200자로 설정되어 있습니다.
Pastit의 다음 단계는 무엇입니까! ?
이것을 IPFS에서 파일 공유 서비스로 전환할 계획입니다. 사람들의 관심을 끌기 위해 약간의 암호화를 추가할 수도 있습니다!!
데모
https://pasteit.vercel.app/에서 최종 지원서를 확인하세요.
GitHub : https://github.com/amalshaji/pasteit
Reference
이 문제에 관하여(페이스트잇! - IPFS의 페이스트빈), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/amal/pasteit-a-pastebin-on-ipfs-1kl9텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)