diff --git a/webui/.proxyrc b/webui/.proxyrc
new file mode 100644
index 00000000..98c5259e
--- /dev/null
+++ b/webui/.proxyrc
@@ -0,0 +1,8 @@
+{
+ "/apiproxy": {
+ "target": "http://localhost:8080/",
+ "pathRewrite": {
+ "^/apiproxy": ""
+ }
+ }
+}
diff --git a/webui/src/App.tsx b/webui/src/App.tsx
index ac7fd175..ac28fb77 100644
--- a/webui/src/App.tsx
+++ b/webui/src/App.tsx
@@ -8,6 +8,8 @@ import {
import "./App.scss";
import { Home } from "./Home";
import { PeerState } from "./PeerState";
+import { peerStateLoader } from "./PeerStatePlumbing";
+import { ErrorGate } from "./ErrorGate";
const router = createBrowserRouter(
[
@@ -21,11 +23,19 @@ const router = createBrowserRouter(
// return redirect("/home");
// }
},
+ errorElement: ,
},
{
path: "/home",
element: ,
- children: [{ path: "peers", element: }],
+ children: [
+ {
+ path: "peers",
+ element: ,
+ loader: peerStateLoader,
+ },
+ ],
+ errorElement: ,
},
],
{ basename: "/webui" },
diff --git a/webui/src/ErrorGate.tsx b/webui/src/ErrorGate.tsx
new file mode 100644
index 00000000..61d003e4
--- /dev/null
+++ b/webui/src/ErrorGate.tsx
@@ -0,0 +1,7 @@
+import { useRouteError } from "react-router-dom";
+
+export function ErrorGate() {
+ const error = useRouteError();
+ console.error(error);
+ return
{JSON.stringify(error)}
;
+}
diff --git a/webui/src/PeerState.tsx b/webui/src/PeerState.tsx
index 9d996b56..9d787e6f 100644
--- a/webui/src/PeerState.tsx
+++ b/webui/src/PeerState.tsx
@@ -1,5 +1,14 @@
import "./PeerState.scss";
+import { useLoaderData } from "react-router-dom";
+import { LoaderToType } from "./commonPlumbing";
+import { peerStateLoader } from "./PeerStatePlumbing";
export function PeerState() {
- return peerstate
;
+ const loaderData = useLoaderData() as LoaderToType;
+
+ const availablePeers = loaderData?.availablePeers.map((p) => {
+ return {p.uuid}
;
+ });
+
+ return {availablePeers}
;
}
diff --git a/webui/src/PeerStatePlumbing.tsx b/webui/src/PeerStatePlumbing.tsx
new file mode 100644
index 00000000..899ecf81
--- /dev/null
+++ b/webui/src/PeerStatePlumbing.tsx
@@ -0,0 +1,7 @@
+import { getAvailablePeers } from "./api/PeerState";
+
+export async function peerStateLoader() {
+ return {
+ availablePeers: await getAvailablePeers(),
+ };
+}
diff --git a/webui/src/api/PeerState.ts b/webui/src/api/PeerState.ts
new file mode 100644
index 00000000..866e8956
--- /dev/null
+++ b/webui/src/api/PeerState.ts
@@ -0,0 +1,13 @@
+import { fetchJSON_throws } from "./utils";
+import {
+ AvailablePeerInfoToResp,
+ TAvailablePeerInfoArrTo,
+ TAvailablePeerInfoToResp,
+} from "./dto";
+
+export async function getAvailablePeers(): Promise {
+ return fetchJSON_throws<
+ TAvailablePeerInfoToResp,
+ typeof AvailablePeerInfoToResp
+ >("/objects-manage/available-peers", "GET", AvailablePeerInfoToResp);
+}
diff --git a/webui/src/api/dto.ts b/webui/src/api/dto.ts
index b09a7791..b3eef3e4 100644
--- a/webui/src/api/dto.ts
+++ b/webui/src/api/dto.ts
@@ -33,3 +33,18 @@ export type TTokenTo = z.infer;
export const TokenToResp = CreateAPIResponse(TokenTo);
export type TTokenToResp = z.infer;
+
+export const AvailablePeerInfoTo = z.object({
+ uuid: z.string(),
+ addr: z.string(),
+ port: z.number(),
+});
+export type TAvailablePeerInfoTo = z.infer;
+
+export const AvailablePeerInfoArrTo = z.array(AvailablePeerInfoTo);
+export type TAvailablePeerInfoArrTo = z.infer;
+
+export const AvailablePeerInfoToResp = CreateAPIResponse(
+ AvailablePeerInfoArrTo,
+);
+export type TAvailablePeerInfoToResp = z.infer;
diff --git a/webui/src/api/utils.ts b/webui/src/api/utils.ts
index 33d64c35..47bac190 100644
--- a/webui/src/api/utils.ts
+++ b/webui/src/api/utils.ts
@@ -1,5 +1,5 @@
import { jwtDecode } from "jwt-decode";
-import { isError } from "./dto";
+import { isError, TErrorTo } from "./dto";
declare const process: {
env: {
@@ -8,7 +8,9 @@ declare const process: {
};
const apiRoot: string =
- process.env.NODE_ENV == "production" ? "" : "http://localhost:8080";
+ process.env.NODE_ENV == "production"
+ ? ""
+ : "http://localhost:1234/apiproxy";
let token: string | null;
@@ -88,3 +90,21 @@ export async function fetchJSONAuth T }>(
throw new Error("Not logged in");
}
}
+
+function errorCheck(
+ fn: (...args: A) => R,
+): (...args: A) => Promise, TErrorTo>> {
+ return async (...args: A): Promise, TErrorTo>> => {
+ const ret = await fn(...args);
+ if (isError(ret)) {
+ throw ret;
+ } else {
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
+ // @ts-expect-error
+ return ret;
+ }
+ };
+}
+
+export const fetchJSON_throws = errorCheck(fetchJSON);
+export const fetchJSONAuth_throws = errorCheck(fetchJSONAuth);
diff --git a/webui/src/commonPlumbing.ts b/webui/src/commonPlumbing.ts
new file mode 100644
index 00000000..632a7ca9
--- /dev/null
+++ b/webui/src/commonPlumbing.ts
@@ -0,0 +1,3 @@
+export type LoaderToType any> =
+ | Exclude>, Response>
+ | undefined;