mirror of
https://github.com/usatiuk/dhfs.git
synced 2025-10-28 12:37:48 +01:00
add peers through ui
This commit is contained in:
@@ -8,7 +8,7 @@ import {
|
||||
import "./App.scss";
|
||||
import { Home } from "./Home";
|
||||
import { PeerState } from "./PeerState";
|
||||
import { peerStateLoader } from "./PeerStatePlumbing";
|
||||
import { peerStateAction, peerStateLoader } from "./PeerStatePlumbing";
|
||||
import { ErrorGate } from "./ErrorGate";
|
||||
|
||||
const router = createBrowserRouter(
|
||||
@@ -33,6 +33,7 @@ const router = createBrowserRouter(
|
||||
path: "peers",
|
||||
element: <PeerState />,
|
||||
loader: peerStateLoader,
|
||||
action: peerStateAction,
|
||||
},
|
||||
],
|
||||
errorElement: <ErrorGate />,
|
||||
|
||||
13
webui/src/PeerAvailableCard.scss
Normal file
13
webui/src/PeerAvailableCard.scss
Normal file
@@ -0,0 +1,13 @@
|
||||
@import "./PeerCardCommon";
|
||||
|
||||
.peerAvailableCard {
|
||||
@extend .peerCard;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.peerInfo {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
}
|
||||
26
webui/src/PeerAvailableCard.tsx
Normal file
26
webui/src/PeerAvailableCard.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { TAvailablePeerInfoTo } from "./api/dto";
|
||||
import { useFetcher } from "react-router-dom";
|
||||
|
||||
import "./PeerAvailableCard.scss";
|
||||
|
||||
export interface TPeerAvailableCardProps {
|
||||
peerInfo: TAvailablePeerInfoTo;
|
||||
}
|
||||
|
||||
export function PeerAvailableCard({ peerInfo }: TPeerAvailableCardProps) {
|
||||
const fetcher = useFetcher();
|
||||
|
||||
return (
|
||||
<div className="peerAvailableCard">
|
||||
<div className={"peerInfo"}>
|
||||
<span>UUID: </span>
|
||||
<span>{peerInfo.uuid}</span>
|
||||
</div>
|
||||
<fetcher.Form method="put" action={"/home/peers"}>
|
||||
<input name="intent" hidden={true} value={"add_peer"} />
|
||||
<input name="uuid" hidden={true} value={peerInfo.uuid} />
|
||||
<button type="submit">connect</button>
|
||||
</fetcher.Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
7
webui/src/PeerCardCommon.scss
Normal file
7
webui/src/PeerCardCommon.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
@import "./common";
|
||||
|
||||
.peerCard {
|
||||
@include border-shadow;
|
||||
|
||||
padding: 0.25rem;
|
||||
}
|
||||
5
webui/src/PeerKnownCard.scss
Normal file
5
webui/src/PeerKnownCard.scss
Normal file
@@ -0,0 +1,5 @@
|
||||
@import "./PeerCardCommon";
|
||||
|
||||
.peerKnownCard {
|
||||
@extend .peerCard;
|
||||
}
|
||||
11
webui/src/PeerKnownCard.tsx
Normal file
11
webui/src/PeerKnownCard.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { TKnownPeerInfoTo } from "./api/dto";
|
||||
|
||||
import "./PeerKnownCard.scss";
|
||||
|
||||
export interface TPeerKnownCardProps {
|
||||
peerInfo: TKnownPeerInfoTo;
|
||||
}
|
||||
|
||||
export function PeerKnownCard({ peerInfo }: TPeerKnownCardProps) {
|
||||
return <div className="peerKnownCard">{peerInfo.uuid}</div>;
|
||||
}
|
||||
@@ -2,4 +2,11 @@
|
||||
|
||||
#PeerState {
|
||||
@include home-view;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> * {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,30 @@ import "./PeerState.scss";
|
||||
import { useLoaderData } from "react-router-dom";
|
||||
import { LoaderToType } from "./commonPlumbing";
|
||||
import { peerStateLoader } from "./PeerStatePlumbing";
|
||||
import { PeerAvailableCard } from "./PeerAvailableCard";
|
||||
import { PeerKnownCard } from "./PeerKnownCard";
|
||||
|
||||
export function PeerState() {
|
||||
const loaderData = useLoaderData() as LoaderToType<typeof peerStateLoader>;
|
||||
|
||||
const availablePeers = loaderData?.availablePeers.map((p) => {
|
||||
return <div>{p.uuid}</div>;
|
||||
});
|
||||
const knownPeers = loaderData.knownPeers.map((p) => (
|
||||
<PeerKnownCard peerInfo={p} key={p.uuid} />
|
||||
));
|
||||
|
||||
return <div id={"PeerState"}>{availablePeers}</div>;
|
||||
const availablePeers = loaderData.availablePeers.map((p) => (
|
||||
<PeerAvailableCard peerInfo={p} key={p.uuid} />
|
||||
));
|
||||
|
||||
return (
|
||||
<div id={"PeerState"}>
|
||||
<div>
|
||||
<div>Known peers</div>
|
||||
<div>{knownPeers}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>Available peers</div>
|
||||
<div>{availablePeers}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
25
webui/src/PeerStatePlumbing.ts
Normal file
25
webui/src/PeerStatePlumbing.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
getAvailablePeers,
|
||||
getKnownPeers,
|
||||
putKnownPeer,
|
||||
} from "./api/PeerState";
|
||||
import { ActionFunctionArgs } from "react-router-dom";
|
||||
|
||||
export async function peerStateLoader() {
|
||||
return {
|
||||
availablePeers: await getAvailablePeers(),
|
||||
knownPeers: await getKnownPeers(),
|
||||
};
|
||||
}
|
||||
|
||||
export type PeerStateActionType = "add_peer" | unknown;
|
||||
|
||||
export async function peerStateAction({ request }: ActionFunctionArgs) {
|
||||
const formData = await request.formData();
|
||||
const intent = formData.get("intent") as PeerStateActionType;
|
||||
if (intent === "add_peer") {
|
||||
return await putKnownPeer(formData.get("uuid") as string);
|
||||
} else {
|
||||
throw new Error("Malformed action: " + JSON.stringify(request));
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { getAvailablePeers } from "./api/PeerState";
|
||||
|
||||
export async function peerStateLoader() {
|
||||
return {
|
||||
availablePeers: await getAvailablePeers(),
|
||||
};
|
||||
}
|
||||
@@ -1,8 +1,13 @@
|
||||
import { fetchJSON_throws } from "./utils";
|
||||
import { fetchJSON, fetchJSON_throws } from "./utils";
|
||||
import {
|
||||
AvailablePeerInfoToResp,
|
||||
KnownPeerInfoToResp,
|
||||
NoContentToResp,
|
||||
TAvailablePeerInfoArrTo,
|
||||
TAvailablePeerInfoToResp,
|
||||
TKnownPeerInfoArrTo,
|
||||
TKnownPeerInfoToResp,
|
||||
TNoContentToResp,
|
||||
} from "./dto";
|
||||
|
||||
export async function getAvailablePeers(): Promise<TAvailablePeerInfoArrTo> {
|
||||
@@ -11,3 +16,17 @@ export async function getAvailablePeers(): Promise<TAvailablePeerInfoArrTo> {
|
||||
typeof AvailablePeerInfoToResp
|
||||
>("/objects-manage/available-peers", "GET", AvailablePeerInfoToResp);
|
||||
}
|
||||
|
||||
export async function getKnownPeers(): Promise<TKnownPeerInfoArrTo> {
|
||||
return fetchJSON_throws<TKnownPeerInfoToResp, typeof KnownPeerInfoToResp>(
|
||||
"/objects-manage/known-peers",
|
||||
"GET",
|
||||
KnownPeerInfoToResp,
|
||||
);
|
||||
}
|
||||
|
||||
export async function putKnownPeer(uuid: string): Promise<TNoContentToResp> {
|
||||
return fetchJSON("/objects-manage/known-peers", "PUT", NoContentToResp, {
|
||||
uuid,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20,12 +20,14 @@ export type TNoContentTo = z.infer<typeof NoContentTo>;
|
||||
export const NoContentToResp = CreateAPIResponse(NoContentTo);
|
||||
export type TNoContentToResp = z.infer<typeof NoContentToResp>;
|
||||
|
||||
// TokenRequest
|
||||
export const TokenRequestTo = z.object({
|
||||
username: z.string(),
|
||||
password: z.string(),
|
||||
});
|
||||
export type TTokenRequestTo = z.infer<typeof TokenRequestTo>;
|
||||
|
||||
// Token
|
||||
export const TokenTo = z.object({
|
||||
token: z.string(),
|
||||
});
|
||||
@@ -34,6 +36,7 @@ export type TTokenTo = z.infer<typeof TokenTo>;
|
||||
export const TokenToResp = CreateAPIResponse(TokenTo);
|
||||
export type TTokenToResp = z.infer<typeof TokenToResp>;
|
||||
|
||||
// AvailablePeerInfo
|
||||
export const AvailablePeerInfoTo = z.object({
|
||||
uuid: z.string(),
|
||||
addr: z.string(),
|
||||
@@ -48,3 +51,19 @@ export const AvailablePeerInfoToResp = CreateAPIResponse(
|
||||
AvailablePeerInfoArrTo,
|
||||
);
|
||||
export type TAvailablePeerInfoToResp = z.infer<typeof AvailablePeerInfoToResp>;
|
||||
|
||||
// KnownPeerInfo
|
||||
export const KnownPeerInfoTo = z.object({
|
||||
uuid: z.string(),
|
||||
});
|
||||
export type TKnownPeerInfoTo = z.infer<typeof KnownPeerInfoTo>;
|
||||
|
||||
export const KnownPeerInfoArrTo = z.array(KnownPeerInfoTo);
|
||||
export type TKnownPeerInfoArrTo = z.infer<typeof KnownPeerInfoArrTo>;
|
||||
|
||||
export const KnownPeerInfoToResp = CreateAPIResponse(KnownPeerInfoArrTo);
|
||||
export type TKnownPeerInfoToResp = z.infer<typeof KnownPeerInfoToResp>;
|
||||
|
||||
// KnownPeerPut
|
||||
export const KnownPeerPutTo = z.object({ uuid: z.string() });
|
||||
export type TKnownPeerPutTo = z.infer<typeof KnownPeerPutTo>;
|
||||
|
||||
@@ -7,7 +7,7 @@ declare const process: {
|
||||
};
|
||||
};
|
||||
|
||||
const apiRoot: string =
|
||||
export const apiRoot: string =
|
||||
process.env.NODE_ENV == "production"
|
||||
? ""
|
||||
: "http://localhost:1234/apiproxy";
|
||||
@@ -91,9 +91,7 @@ export async function fetchJSONAuth<T, P extends { parse: (arg: string) => T }>(
|
||||
}
|
||||
}
|
||||
|
||||
function errorCheck<A extends unknown[], R>(
|
||||
fn: (...args: A) => R,
|
||||
): (...args: A) => Promise<Exclude<Awaited<R>, TErrorTo>> {
|
||||
function errorCheck<A extends unknown[], R>(fn: (...args: A) => R) {
|
||||
return async (...args: A): Promise<Exclude<Awaited<R>, TErrorTo>> => {
|
||||
const ret = await fn(...args);
|
||||
if (isError(ret)) {
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export type LoaderToType<T extends (...args: any) => any> =
|
||||
| Exclude<Awaited<ReturnType<T>>, Response>
|
||||
| undefined;
|
||||
export type LoaderToType<T extends (...args: unknown[]) => unknown> = Awaited<
|
||||
ReturnType<T>
|
||||
>;
|
||||
|
||||
Reference in New Issue
Block a user