mirror of
https://github.com/logos-messaging/logos-messaging-frontend.git
synced 2026-01-02 13:53:13 +00:00
add health and debug status, fix double fetching
This commit is contained in:
parent
0389d6cce6
commit
6cc8f45028
@ -1,28 +1,24 @@
|
||||
import { Block, BlockTypes } from "@/components/Block";
|
||||
import { Title } from "@/components/Title";
|
||||
import { Status } from "@/components/Status";
|
||||
import { useStore } from "@/hooks";
|
||||
import { Button } from "@/components/Button";
|
||||
|
||||
type HeaderProps = {
|
||||
children?: React.ReactNode;
|
||||
onWalletConnect?: () => void;
|
||||
}
|
||||
|
||||
export const Header: React.FunctionComponent<HeaderProps> = (props) => {
|
||||
const { appStatus, wallet } = useStore();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Block className="mb-5" type={BlockTypes.FlexHorizontal}>
|
||||
<Title>Waku RLN</Title>
|
||||
<Title>Waku</Title>
|
||||
{props.onWalletConnect && (
|
||||
<Button onClick={props.onWalletConnect}>
|
||||
Connect Wallet
|
||||
</Button>
|
||||
)}
|
||||
</Block>
|
||||
<Status text="Application status" mark={appStatus} />
|
||||
{wallet && <p className="mt-3 text-sm">Wallet connected: {wallet}</p> }
|
||||
{props.children}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -2,15 +2,16 @@ import React from "react";
|
||||
import { Block } from "@/components/Block";
|
||||
import { Subtitle } from "@/components/Subtitle";
|
||||
import { Button } from "@/components/Button";
|
||||
import { MessageContent, useWaku } from "@/hooks";
|
||||
import { MessageContent } from "@/hooks";
|
||||
|
||||
export const Waku: React.FunctionComponent<{}> = () => {
|
||||
const {
|
||||
onSend,
|
||||
messages,
|
||||
contentTopic: activeContentTopic,
|
||||
onContentTopicChange: onActiveContentTopicChange
|
||||
} = useWaku();
|
||||
type WakuProps = {
|
||||
onSend: (nick: string, text: string) => Promise<void>;
|
||||
activeContentTopic: string;
|
||||
messages: MessageContent[];
|
||||
onActiveContentTopicChange: (contentTopic: string) => void;
|
||||
}
|
||||
|
||||
export const Waku: React.FunctionComponent<WakuProps> = (props) => {
|
||||
const {
|
||||
nick,
|
||||
text,
|
||||
@ -21,16 +22,16 @@ export const Waku: React.FunctionComponent<{}> = () => {
|
||||
const {
|
||||
contentTopic,
|
||||
onContentTopicChange,
|
||||
} = useContentTopic(activeContentTopic);
|
||||
} = useContentTopic(props.activeContentTopic);
|
||||
|
||||
const onSendClick = async () => {
|
||||
await onSend(nick, text);
|
||||
await props.onSend(nick, text);
|
||||
resetText();
|
||||
};
|
||||
|
||||
const renderedMessages = React.useMemo(
|
||||
() => messages.map(renderMessage),
|
||||
[messages]
|
||||
() => props.messages.map(renderMessage),
|
||||
[props.messages]
|
||||
);
|
||||
|
||||
return (
|
||||
@ -53,7 +54,7 @@ export const Waku: React.FunctionComponent<{}> = () => {
|
||||
onChange={onContentTopicChange}
|
||||
className="w-96 mr-2 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
/>
|
||||
<Button className="mt-1" onClick={() => { onActiveContentTopicChange(contentTopic); }}>Change</Button>
|
||||
<Button className="mt-1" onClick={() => { props.onActiveContentTopicChange(contentTopic); }}>Change</Button>
|
||||
</Block>
|
||||
|
||||
<Block className="mt-4 mr-10 min-w-fit">
|
||||
|
||||
@ -1,12 +1,58 @@
|
||||
"use client";
|
||||
import { Header } from "@/app/components/Header";
|
||||
import { Waku } from "@/app/components/Waku";
|
||||
import { useWaku } from "@/hooks";
|
||||
import { DebugInfo } from "@/services/waku";
|
||||
|
||||
export default function Home() {
|
||||
const {
|
||||
onSend,
|
||||
messages,
|
||||
debugInfo,
|
||||
contentTopic,
|
||||
onContentTopicChange
|
||||
} = useWaku();
|
||||
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col p-24 font-mono max-w-screen-lg">
|
||||
<Header />
|
||||
<Waku />
|
||||
<Header>
|
||||
<DebugInfo value={debugInfo} />
|
||||
</Header>
|
||||
<Waku
|
||||
onSend={onSend}
|
||||
messages={messages}
|
||||
activeContentTopic={contentTopic}
|
||||
onActiveContentTopicChange={onContentTopicChange}
|
||||
/>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
type DebugInfoProps = {
|
||||
value?: DebugInfo;
|
||||
}
|
||||
|
||||
const DebugInfo: React.FunctionComponent<DebugInfoProps> = (props) => {
|
||||
if (!props.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<details className="border rounded p-2">
|
||||
<summary className="cursor-pointer bg-gray-300 p-2 rounded-md">
|
||||
<span className="font-bold">Show node info</span>
|
||||
</summary>
|
||||
<div className="mt-2 text-sm break-words">
|
||||
<p className="mb-2">Health: {props.value.health}</p>
|
||||
<p className="mb-2">Version: {props.value.version}</p>
|
||||
<p className="mb-2">ENR URI: {props.value.enrUri}</p>
|
||||
<p className="mb-2">Listen Addresses:</p>
|
||||
<ul>
|
||||
{props.value.listenAddresses.map((address, index) => (
|
||||
<li key={index}>{address}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
);
|
||||
}
|
||||
|
||||
@ -3,12 +3,18 @@ import { Header } from "@/app/components/Header";
|
||||
import { Keystore } from "@/app/components/Keystore";
|
||||
import { KeystoreDetails } from "@/app/components/KeystoreDetails";
|
||||
import { useWallet } from "@/hooks";
|
||||
import { Status } from "@/components/Status";
|
||||
import { useStore } from "@/hooks";
|
||||
|
||||
export default function KeystorePage() {
|
||||
const { onWalletConnect } = useWallet();
|
||||
const { appStatus, wallet } = useStore();
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col p-24 font-mono max-w-screen-lg m-auto">
|
||||
<Header onWalletConnect={onWalletConnect} />
|
||||
<Header onWalletConnect={onWalletConnect}>
|
||||
<Status text="Application status" mark={appStatus} />
|
||||
{wallet && <p className="mt-3 text-sm">Wallet connected: {wallet}</p> }
|
||||
</Header>
|
||||
<Keystore />
|
||||
<KeystoreDetails />
|
||||
</main>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import { CONTENT_TOPIC } from "@/constants";
|
||||
import { Message, waku } from "@/services/waku";
|
||||
import { DebugInfo, Message, waku } from "@/services/waku";
|
||||
|
||||
export type MessageContent = {
|
||||
nick: string;
|
||||
@ -11,6 +11,7 @@ export type MessageContent = {
|
||||
export const useWaku = () => {
|
||||
const [contentTopic, setContentTopic] = React.useState<string>(CONTENT_TOPIC);
|
||||
const [messages, setMessages] = React.useState<Map<string, MessageContent>>(new Map());
|
||||
const [debugInfo, setDebugInfo] = React.useState<undefined | DebugInfo>();
|
||||
|
||||
React.useEffect(() => {
|
||||
const messageListener = (event: CustomEvent) => {
|
||||
@ -38,6 +39,25 @@ export const useWaku = () => {
|
||||
};
|
||||
}, [messages, setMessages, contentTopic]);
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
const debugInfoListener = (event: CustomEvent) => {
|
||||
const debugInfo = event.detail;
|
||||
|
||||
if (!debugInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
setDebugInfo(debugInfo);
|
||||
};
|
||||
|
||||
waku.debug.addEventListener("debug", debugInfoListener);
|
||||
|
||||
return () => {
|
||||
waku.debug.removeEventListener("debug", debugInfoListener);
|
||||
};
|
||||
}, [debugInfo, setDebugInfo]);
|
||||
|
||||
const onSend = React.useCallback(
|
||||
async (nick: string, text: string) => {
|
||||
const timestamp = Date.now();
|
||||
@ -73,6 +93,7 @@ export const useWaku = () => {
|
||||
|
||||
return {
|
||||
onSend,
|
||||
debugInfo,
|
||||
contentTopic,
|
||||
onContentTopicChange,
|
||||
messages: Array.from(messages.values())
|
||||
|
||||
@ -17,6 +17,7 @@ const RELAY = "/relay/v1";
|
||||
const buildURL = (endpoint: string) => `${LOCAL_NODE}${endpoint}`;
|
||||
|
||||
class Relay {
|
||||
private subscribing = false;
|
||||
private readonly subscriptionsEmitter = new EventTarget();
|
||||
// only one content topic subscriptions is possible now
|
||||
private subscriptionRoutine: undefined | number;
|
||||
@ -24,7 +25,7 @@ class Relay {
|
||||
constructor() {}
|
||||
|
||||
public addEventListener(contentTopic: string, fn: EventListener) {
|
||||
this.subscribe(contentTopic);
|
||||
this.subscribe();
|
||||
return this.subscriptionsEmitter.addEventListener(contentTopic, fn as any);
|
||||
}
|
||||
|
||||
@ -35,11 +36,12 @@ class Relay {
|
||||
);
|
||||
}
|
||||
|
||||
private async subscribe(contentTopic: string) {
|
||||
if (this.subscriptionRoutine) {
|
||||
private async subscribe() {
|
||||
if (this.subscriptionRoutine || this.subscribing) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.subscribing = true;
|
||||
try {
|
||||
await http.post(buildURL(`${RELAY}/subscriptions`), [PUBSUB_TOPIC]);
|
||||
|
||||
@ -47,11 +49,12 @@ class Relay {
|
||||
await this.fetchMessages();
|
||||
}, 5 * SECOND);
|
||||
} catch (error) {
|
||||
console.error(`Failed to subscribe node ${contentTopic}:`, error);
|
||||
console.error(`Failed to subscribe node ${PUBSUB_TOPIC}:`, error);
|
||||
}
|
||||
this.subscribing = false;
|
||||
}
|
||||
|
||||
public async unsubscribe(contentTopic: string) {
|
||||
public async unsubscribe() {
|
||||
if (!this.subscriptionRoutine) {
|
||||
return;
|
||||
}
|
||||
@ -59,10 +62,11 @@ class Relay {
|
||||
try {
|
||||
await http.delete(buildURL(`${RELAY}/subscriptions`), [PUBSUB_TOPIC]);
|
||||
} catch (error) {
|
||||
console.error(`Failed to unsubscribe node from ${contentTopic}:`, error);
|
||||
console.error(`Failed to unsubscribe node from ${PUBSUB_TOPIC}:`, error);
|
||||
}
|
||||
|
||||
clearInterval(this.subscriptionRoutine);
|
||||
this.subscriptionRoutine = undefined;
|
||||
}
|
||||
|
||||
private async fetchMessages(): Promise<void> {
|
||||
@ -103,6 +107,92 @@ class Relay {
|
||||
}
|
||||
}
|
||||
|
||||
type DebugInfoResponse = {
|
||||
enrUri: string;
|
||||
listenAddresses: string[];
|
||||
}
|
||||
|
||||
export type DebugInfo = {
|
||||
health: string;
|
||||
version: string;
|
||||
} & DebugInfoResponse;
|
||||
|
||||
class Debug {
|
||||
private subscribing = false;
|
||||
private readonly subscriptionsEmitter = new EventTarget();
|
||||
private subscriptionRoutine: undefined | number;
|
||||
|
||||
constructor() {}
|
||||
|
||||
public addEventListener(event: string, fn: EventListener) {
|
||||
this.subscribe();
|
||||
return this.subscriptionsEmitter.addEventListener(event, fn as any);
|
||||
}
|
||||
|
||||
public removeEventListener(event: string, fn: EventListener) {
|
||||
return this.subscriptionsEmitter.removeEventListener(
|
||||
event,
|
||||
fn as any
|
||||
);
|
||||
}
|
||||
|
||||
private async subscribe() {
|
||||
if (this.subscriptionRoutine || this.subscribing) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.subscribing = true;
|
||||
try {
|
||||
await this.fetchParameters();
|
||||
this.subscriptionRoutine = window.setInterval(async () => {
|
||||
await this.fetchParameters();
|
||||
}, 30 * SECOND);
|
||||
} catch(error) {
|
||||
console.error("Failed to fetch debug info:", error);
|
||||
}
|
||||
this.subscribing = false;
|
||||
}
|
||||
|
||||
private async unsubscribe() {
|
||||
if (!this.subscriptionRoutine) {
|
||||
return;
|
||||
}
|
||||
clearInterval(this.subscriptionRoutine);
|
||||
this.subscriptionRoutine = undefined;
|
||||
}
|
||||
|
||||
private async fetchParameters(): Promise<void> {
|
||||
const health = await this.fetchHealth();
|
||||
const debug = await this.fetchDebugInfo();
|
||||
const version = await this.fetchDebugVersion();
|
||||
|
||||
this.subscriptionsEmitter.dispatchEvent(
|
||||
new CustomEvent("debug", { detail: {
|
||||
health,
|
||||
version,
|
||||
...debug,
|
||||
} })
|
||||
);
|
||||
}
|
||||
|
||||
private async fetchHealth(): Promise<string> {
|
||||
const response = await http.get(buildURL(`/health`));
|
||||
return response.text();
|
||||
}
|
||||
|
||||
private async fetchDebugInfo(): Promise<DebugInfoResponse> {
|
||||
const response = await http.get(buildURL(`/debug/v1/info`));
|
||||
const body: DebugInfoResponse = await response.json();
|
||||
return body;
|
||||
}
|
||||
|
||||
private async fetchDebugVersion(): Promise<string> {
|
||||
const response = await http.get(buildURL(`/debug/v1/version`));
|
||||
return response.text();
|
||||
}
|
||||
}
|
||||
|
||||
export const waku = {
|
||||
relay: new Relay(),
|
||||
debug: new Debug(),
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user