mirror of
https://github.com/fluencelabs/examples
synced 2025-05-14 11:51:19 +00:00
305 lines
9.8 KiB
TypeScript
305 lines
9.8 KiB
TypeScript
|
import React, { useState } from "react";
|
||
|
import logo from "./logo.svg";
|
||
|
import "./App.scss";
|
||
|
|
||
|
import { createClient, FluenceClient } from "@fluencelabs/fluence";
|
||
|
import { get_external_api_multiaddr } from "@fluencelabs/aqua-ipfs";
|
||
|
import { stage } from "@fluencelabs/fluence-network-environment";
|
||
|
import { deploy_service, get_file_size, remove_service, provideFile } from "@fluencelabs/ipfs-execution";
|
||
|
const { create, globSource, urlSource, CID } = require('ipfs-http-client');
|
||
|
|
||
|
const relayNodes = [stage[0], stage[1], stage[2]];
|
||
|
|
||
|
const copyToClipboard = (text: string) => {
|
||
|
navigator.clipboard.writeText(text);
|
||
|
};
|
||
|
|
||
|
function App() {
|
||
|
const [client, setClient] = useState<FluenceClient | null>(null);
|
||
|
const [serviceId, setServiceId] = useState<string | null>(null);
|
||
|
|
||
|
const [peerIdInput, setPeerIdInput] = useState<string>("");
|
||
|
const [relayPeerIdInput, setRelayPeerIdInput] = useState<string>("");
|
||
|
const [wasm, setWasm] = useState<string | null>("QmVg9EnanAbwTuEqjjuc1R2uf3AdtEkrNagSifQMkHfyNU");
|
||
|
const [rpcAddr, setRpcAddr] = useState<string | null>("");
|
||
|
const [fileCID, setFileCID] = useState<string>("");
|
||
|
const [fileSize, setFileSize] = useState<string | null>(null);
|
||
|
|
||
|
const isConnected = client !== null;
|
||
|
const gotRpcAddr = rpcAddr !== null;
|
||
|
const deployed = serviceId !== null;
|
||
|
|
||
|
const connect = async (relayPeerId: string) => {
|
||
|
try {
|
||
|
let client = await createClient(relayPeerId);
|
||
|
setClient(client);
|
||
|
await getRpcAddr(client);
|
||
|
} catch (err) {
|
||
|
console.log("Client initialization failed", err);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const getRpcAddr = async (client: FluenceClient) => {
|
||
|
if (client === null) {
|
||
|
console.log("getRpcAddr client is null");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let result = await get_external_api_multiaddr(client, client.relayPeerId!);
|
||
|
console.log("getRpcAddr result", result);
|
||
|
let rpcAddr = result.multiaddr;
|
||
|
setRpcAddr(rpcAddr);
|
||
|
}
|
||
|
|
||
|
const deployService = async () => {
|
||
|
console.log("wasm %s rpcAddr %s", wasm, rpcAddr);
|
||
|
if (client === null || wasm === null || rpcAddr === null) {
|
||
|
return;
|
||
|
}
|
||
|
let service_id = await deploy_service(
|
||
|
client,
|
||
|
client.relayPeerId!, wasm, rpcAddr,
|
||
|
(msg, value) => console.log(msg, value),
|
||
|
{ ttl: 10000 }
|
||
|
);
|
||
|
setServiceId(service_id);
|
||
|
};
|
||
|
|
||
|
const getFileSize = async () => {
|
||
|
if (client === null || serviceId === null || rpcAddr === null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let size = await get_file_size(client, client.relayPeerId!, fileCID, rpcAddr, serviceId, { ttl: 10000 });
|
||
|
if (size.success) {
|
||
|
setFileSize(size.size.toString());
|
||
|
} else {
|
||
|
setFileSize("Error: " + size.error);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const removeService = async () => {
|
||
|
if (client === null || serviceId === null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
await remove_service(client, client.relayPeerId!, serviceId, { ttl: 10000 });
|
||
|
setServiceId(null);
|
||
|
};
|
||
|
|
||
|
console.log("isConnected gotRpcAddr deployed\n", isConnected, gotRpcAddr, deployed);
|
||
|
|
||
|
if (!isConnected) {
|
||
|
return (<div className="App">
|
||
|
<header>
|
||
|
<img src={logo} className="logo" alt="logo" />
|
||
|
</header>
|
||
|
|
||
|
<div className="content">
|
||
|
<>
|
||
|
<h1>Pick a relay</h1>
|
||
|
<ul>
|
||
|
{relayNodes.map((x) => (
|
||
|
<li key={x.peerId}>
|
||
|
<span className="mono">{x.peerId}</span>
|
||
|
<button className="btn" onClick={async () => await connect(x.multiaddr)}>
|
||
|
Connect
|
||
|
</button>
|
||
|
</li>
|
||
|
))}
|
||
|
</ul>
|
||
|
</>
|
||
|
</div>
|
||
|
</div>
|
||
|
);
|
||
|
} else if (isConnected && gotRpcAddr && !deployed) {
|
||
|
return (
|
||
|
<div className="App">
|
||
|
<header>
|
||
|
<img src={logo} className="logo" alt="logo" />
|
||
|
</header>
|
||
|
|
||
|
<div className="content">
|
||
|
<>
|
||
|
<h1>Connected</h1>
|
||
|
<table>
|
||
|
<tr>
|
||
|
<td className="bold">Peer id:</td>
|
||
|
<td className="mono">{client!.selfPeerId}</td>
|
||
|
<td>
|
||
|
<button
|
||
|
className="btn-clipboard"
|
||
|
onClick={() => copyToClipboard(client!.selfPeerId)}
|
||
|
>
|
||
|
<i className="gg-clipboard"></i>
|
||
|
</button>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td className="bold">Relay peer id:</td>
|
||
|
<td className="mono">{client!.relayPeerId}</td>
|
||
|
<td>
|
||
|
<button
|
||
|
className="btn-clipboard"
|
||
|
onClick={() => copyToClipboard(client!.relayPeerId!)}
|
||
|
>
|
||
|
<i className="gg-clipboard"></i>
|
||
|
</button>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td className="bold">IPFS RPC:</td>
|
||
|
<td className="mono">{rpcAddr?.substring(0, 49) + "..."}</td>
|
||
|
<td>
|
||
|
<button
|
||
|
className="btn-clipboard"
|
||
|
onClick={() => copyToClipboard(rpcAddr!)}
|
||
|
>
|
||
|
<i className="gg-clipboard"></i>
|
||
|
</button>
|
||
|
</td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
<div>
|
||
|
<div className="row">
|
||
|
<h2>Set process_files.wasm module CID</h2>
|
||
|
<p className="p">
|
||
|
To deploy a service, specify CID of WebAssembly module.
|
||
|
</p>
|
||
|
<input
|
||
|
className="input"
|
||
|
type="text"
|
||
|
onChange={(e) => setWasm(e.target.value)}
|
||
|
value={wasm!}
|
||
|
/>
|
||
|
</div>
|
||
|
</div>
|
||
|
<div>
|
||
|
<h2>Deploy ProcessFiles service</h2>
|
||
|
<p className="p">
|
||
|
process_files.wasm will be downloaded to the Fluence node,
|
||
|
and then a service will be dynamically created from it!
|
||
|
|
||
|
After that, you will be able to use that service to get sizes of IPFS files!
|
||
|
</p>
|
||
|
<div className="row">
|
||
|
<button className="btn btn-hello" onClick={deployService}>
|
||
|
deploy service
|
||
|
</button>
|
||
|
</div>
|
||
|
</div>
|
||
|
</>
|
||
|
</div>
|
||
|
</div>
|
||
|
)
|
||
|
} else if (deployed) {
|
||
|
return (
|
||
|
<div className="App">
|
||
|
<header>
|
||
|
<img src={logo} className="logo" alt="logo" />
|
||
|
</header>
|
||
|
|
||
|
<div className="content">
|
||
|
<>
|
||
|
<h1>Deployed</h1>
|
||
|
<table>
|
||
|
<tr>
|
||
|
<td className="bold">Peer id:</td>
|
||
|
<td className="mono">{client!.selfPeerId}</td>
|
||
|
<td>
|
||
|
<button
|
||
|
className="btn-clipboard"
|
||
|
onClick={() => copyToClipboard(client!.selfPeerId)}
|
||
|
>
|
||
|
<i className="gg-clipboard"></i>
|
||
|
</button>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td className="bold">Relay peer id:</td>
|
||
|
<td className="mono">{client!.relayPeerId}</td>
|
||
|
<td>
|
||
|
<button
|
||
|
className="btn-clipboard"
|
||
|
onClick={() => copyToClipboard(client!.relayPeerId!)}
|
||
|
>
|
||
|
<i className="gg-clipboard"></i>
|
||
|
</button>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td className="bold">process_files.wasm CID:</td>
|
||
|
<td className="mono">{wasm}</td>
|
||
|
<td>
|
||
|
<button
|
||
|
className="btn-clipboard"
|
||
|
onClick={() => copyToClipboard(wasm!)}
|
||
|
>
|
||
|
<i className="gg-clipboard"></i>
|
||
|
</button>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td className="bold">ProcessFiles service ID:</td>
|
||
|
<td className="mono">{serviceId}</td>
|
||
|
<button
|
||
|
className="btn-clipboard"
|
||
|
onClick={() => removeService()}
|
||
|
>
|
||
|
<i className="gg-trash"></i>
|
||
|
</button>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td className="bold">File Size:</td>
|
||
|
<td className="mono">{fileSize}</td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
<div>
|
||
|
<h2>Get file size</h2>
|
||
|
<p className="p">
|
||
|
Upload any file to IPFS node
|
||
|
<p><b>{ rpcAddr }</b></p>
|
||
|
paste CID here and get the size of that file via ProcessFiles service you have just deployed
|
||
|
</p>
|
||
|
<div className="row">
|
||
|
<label className="label bold">IPFS CID</label>
|
||
|
<input
|
||
|
className="input"
|
||
|
type="text"
|
||
|
onChange={(e) => setFileCID(e.target.value)}
|
||
|
value={fileCID}
|
||
|
/>
|
||
|
</div>
|
||
|
<div className="row">
|
||
|
<button className="btn btn-hello" onClick={getFileSize} >
|
||
|
get size
|
||
|
</button>
|
||
|
</div>
|
||
|
<div className="row">
|
||
|
<label className="label bold">File Size:</label>
|
||
|
<label className="mono"> {fileSize}</label>
|
||
|
</div>
|
||
|
</div>
|
||
|
</>
|
||
|
</div>
|
||
|
</div>
|
||
|
)
|
||
|
} else {
|
||
|
return (
|
||
|
<div className="App">
|
||
|
<header>
|
||
|
<img src={logo} className="logo" alt="logo" />
|
||
|
</header>
|
||
|
|
||
|
<div className="content">
|
||
|
<>
|
||
|
<h2>Unimplemented! isConnected {isConnected} deployed {deployed} wasm {wasm} </h2>
|
||
|
</>
|
||
|
</div>
|
||
|
</div>
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export default App;
|