From f0cfe47a0852036bc1975c16e8a31d26d25efd9f Mon Sep 17 00:00:00 2001 From: Atila Fassina Date: Mon, 20 Nov 2023 09:31:12 +0100 Subject: [PATCH 1/7] handle connection issue --- web-client/package.json | 1 + web-client/pnpm-lock.yaml | 11 ++ web-client/src/components/autoscroll-pane.tsx | 4 +- web-client/src/components/boot-time.tsx | 8 +- web-client/src/components/dialog.tsx | 50 +++++ .../src/components/disconnect-button.tsx | 15 +- web-client/src/components/error-dialog.tsx | 62 ++++++ web-client/src/components/health-status.tsx | 118 +++++++---- .../src/components/sources/code-view.tsx | 51 +++-- .../src/components/sources/directory.tsx | 15 +- .../src/components/sources/image-view.tsx | 7 +- .../src/components/status-indicator.tsx | 12 -- .../src/context/connection-provider.tsx | 38 ++++ web-client/src/context/monitor-provider.tsx | 56 ++++++ web-client/src/entry.tsx | 16 +- web-client/src/lib/connection/monitor.ts | 20 +- web-client/src/lib/connection/transport.ts | 51 ----- web-client/src/lib/connection/transport.tsx | 105 ++++++++++ web-client/src/views/connect.tsx | 14 +- web-client/src/views/dashboard/console.tsx | 2 +- web-client/src/views/dashboard/layout.tsx | 187 ++++-------------- .../src/views/dashboard/span-waterfall.tsx | 2 +- .../src/views/dashboard/tauri-config.tsx | 85 ++++++++ web-client/tailwind.config.ts | 3 +- 24 files changed, 596 insertions(+), 337 deletions(-) create mode 100644 web-client/src/components/dialog.tsx create mode 100644 web-client/src/components/error-dialog.tsx delete mode 100644 web-client/src/components/status-indicator.tsx create mode 100644 web-client/src/context/connection-provider.tsx create mode 100644 web-client/src/context/monitor-provider.tsx delete mode 100644 web-client/src/lib/connection/transport.ts create mode 100644 web-client/src/lib/connection/transport.tsx create mode 100644 web-client/src/views/dashboard/tauri-config.tsx diff --git a/web-client/package.json b/web-client/package.json index 5e7ad261..6b2dfd9a 100644 --- a/web-client/package.json +++ b/web-client/package.json @@ -44,6 +44,7 @@ "dependencies": { "@crabnebula/file-icons": "^0.1.0", "@kobalte/core": "^0.11.2", + "@kobalte/tailwindcss": "^0.9.0", "@protobuf-ts/grpcweb-transport": "^2.9.1", "@protobuf-ts/plugin": "^2.9.1", "@protobuf-ts/runtime": "^2.9.1", diff --git a/web-client/pnpm-lock.yaml b/web-client/pnpm-lock.yaml index c6c1b3c4..a77d7dba 100644 --- a/web-client/pnpm-lock.yaml +++ b/web-client/pnpm-lock.yaml @@ -11,6 +11,9 @@ dependencies: '@kobalte/core': specifier: ^0.11.2 version: 0.11.2(solid-js@1.8.5) + '@kobalte/tailwindcss': + specifier: ^0.9.0 + version: 0.9.0(tailwindcss@3.3.5) '@protobuf-ts/grpcweb-transport': specifier: ^2.9.1 version: 2.9.1 @@ -874,6 +877,14 @@ packages: solid-js: 1.8.5 dev: false + /@kobalte/tailwindcss@0.9.0(tailwindcss@3.3.5): + resolution: {integrity: sha512-WbueJTVRiO4yrmfHIBwp07y3M5iibJ/gauEAQ7mOyg1tZulvpO7SM/UdgzX95a9a0KDt1mQFxwO7RmpOUXWOWA==} + peerDependencies: + tailwindcss: ^3.3.3 + dependencies: + tailwindcss: 3.3.5 + dev: false + /@kobalte/utils@0.9.0(solid-js@1.8.5): resolution: {integrity: sha512-TYVCpQcpqo1+0HBn3NXoGEBzxd4tH6Um1oc07nrYw1V7Qq0qbMaYAOnfBc1qhlh7sGV4XZldmb0j13Of0FrZQg==} peerDependencies: diff --git a/web-client/src/components/autoscroll-pane.tsx b/web-client/src/components/autoscroll-pane.tsx index 8c21abb2..ab748f08 100644 --- a/web-client/src/components/autoscroll-pane.tsx +++ b/web-client/src/components/autoscroll-pane.tsx @@ -1,4 +1,4 @@ -import { Accessor, JSX, createEffect, on } from "solid-js"; +import { Accessor, JSXElement, createEffect, on } from "solid-js"; function scrollEnd(ref?: HTMLElement, smooth?: boolean) { ref?.scroll({ @@ -14,7 +14,7 @@ function scrollEnd(ref?: HTMLElement, smooth?: boolean) { type AutoScrollPaneProps = { dataStream: unknown; shouldAutoScroll: Accessor; - children: JSX.Element; + children: JSXElement; }; export function AutoscrollPane(props: AutoScrollPaneProps) { diff --git a/web-client/src/components/boot-time.tsx b/web-client/src/components/boot-time.tsx index b8fc312a..12af1dfd 100644 --- a/web-client/src/components/boot-time.tsx +++ b/web-client/src/components/boot-time.tsx @@ -1,16 +1,16 @@ import { Show } from "solid-js"; -import { useMonitor } from "~/lib/connection/monitor"; +import { Loader } from "./loader"; +import { useMonitor } from "~/context/monitor-provider"; export function BootTime() { const { monitorData } = useMonitor(); - return ( - + }> {(e) => (
Loading time: - {Number(e().seconds) * 1000 + e().nanos / 1e6}ms + {(Number(e().seconds) * 1000 + e().nanos / 1e6).toFixed(2)}ms
)} diff --git a/web-client/src/components/dialog.tsx b/web-client/src/components/dialog.tsx new file mode 100644 index 00000000..4bb68127 --- /dev/null +++ b/web-client/src/components/dialog.tsx @@ -0,0 +1,50 @@ +import { AlertDialog } from "@kobalte/core"; +import { A } from "@solidjs/router"; +import { JSXElement, Show, mergeProps } from "solid-js"; + +type Props = { + title?: JSXElement; + children: JSXElement; +}; + +export function Dialog(p: Props) { + const props = mergeProps({ title: "Alert" }, p); + + return ( + + {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion*/} + + +
+ +
+ {props.title} +
+ + {props.children} + +
+ { + window.location.reload(); + }} + > + Reload route + + + Dismiss + + + Reset App + +
+
+
+
+
+ ); +} diff --git a/web-client/src/components/disconnect-button.tsx b/web-client/src/components/disconnect-button.tsx index 647d6084..31bdb682 100644 --- a/web-client/src/components/disconnect-button.tsx +++ b/web-client/src/components/disconnect-button.tsx @@ -1,16 +1,19 @@ import { Button } from "@kobalte/core"; +import { useNavigate } from "@solidjs/router"; +import { useConnection } from "~/context/connection-provider"; -type DisconnectProps = { - closeSession: () => void; -}; - -export function DisconnectButton(props: DisconnectProps) { +export function DisconnectButton() { + const { connectionStore } = useConnection(); + const goto = useNavigate(); return ( { + connectionStore.abortController.abort(); + goto("/"); + }} > Close connection + Irrecoverable Error + + ); +} + +export function ErrorDialog(props: Props) { + return ( + }> +
+

Something terrible happened.

+

The log is on the way and we'll work on it!

+
+
+ + Reset App + + + Something else + +
+ + + + See the stack trace + + ↓ + + + +
{props.error?.toString() || String(props.error)}
+
+
+ + ); +} diff --git a/web-client/src/components/health-status.tsx b/web-client/src/components/health-status.tsx index ba88c362..6aa6ebeb 100644 --- a/web-client/src/components/health-status.tsx +++ b/web-client/src/components/health-status.tsx @@ -1,47 +1,87 @@ -import { useMonitor } from "~/lib/connection/monitor"; -import { Tooltip } from "@kobalte/core"; -import { HealthCheckResponse_ServingStatus } from "~/lib/proto/health"; +import { + HealthCheckRequest, + HealthCheckResponse_ServingStatus, +} from "~/lib/proto/health"; +import { Show, createEffect, createSignal, on, onMount } from "solid-js"; +import { Dialog } from "./dialog"; +import { addStreamListneners, connect } from "~/lib/connection/transport"; +import { produce } from "solid-js/store"; +import { useConnection } from "~/context/connection-provider"; +import { useMonitor } from "~/context/monitor-provider"; + +/** + * Reconnect + * terminate all connections + * createResource => setup a new connection, push new data to monitor store. [retry for 5s] + * return resource. + */ + +const variant = (status: HealthCheckResponse_ServingStatus) => { + return [ + // unknown + { + style: "inline-block mr-3 w-3 h-3 bg-gray-200 rounded-full", + tooltip: "Disconected", + }, + // serving + { + style: "inline-block mr-3 w-3 h-3 bg-green-500 rounded-full", + tooltip: "Connected", + }, + // not serving + { + style: "inline-block mr-3 w-3 h-3 bg-red-500 rounded-full", + tooltip: "Disconected", + }, + ][status]; +}; export function HealthStatus() { - const { monitorData } = useMonitor(); - - const variant = (status: HealthCheckResponse_ServingStatus) => { - return [ - // unknown - { - style: "flex w-3 h-3 bg-gray-200 rounded-full", - tooltip: "Instrumentation is not connected", - }, - // serving - { - style: "flex w-3 h-3 bg-green-500 rounded-full", - tooltip: "Instrumentation is operating normally", - }, - // not serving - { - style: "flex w-3 h-3 bg-red-500 rounded-full", - tooltip: "Instrumentation not operational", - }, - ][status]; - }; + const { connectionStore, setConnection } = useConnection(); + const { monitorData, setMonitorData } = useMonitor(); + const [isConnectionDead, setConnectionDead] = createSignal(false); + + onMount(() => { + connectionStore.stream.health.responses.onError(() => { + console.error("an error happened on Health Stream"); + setMonitorData("health", 0); + setConnectionDead(true); + + /** cleanup connections */ + connectionStore.abortController.abort(); + + setTimeout(() => { + const newConnection = connect(connectionStore.serviceUrl); + console.log(newConnection); + addStreamListneners(newConnection.stream.update, setMonitorData); + setConnection(produce(() => newConnection)); + }, 10000); + }); + + connectionStore.stream.update.responses.onError(() => { + console.error("an error happened on Updates Stream"); + // if (isConnectionDead()) { + /**22 + * we do nothing because it's not an instrumentation issue. + */ + return; + // } + }); + }); + + createEffect(() => { + if (monitorData.health === 1 && isConnectionDead()) { + setConnectionDead(false); + } + }); return (
- - - - - - - -

{variant(monitorData.health).tooltip}

-
-
-
+ + Tauri app stopped streaming. + + + {variant(monitorData.health).tooltip}
); } diff --git a/web-client/src/components/sources/code-view.tsx b/web-client/src/components/sources/code-view.tsx index 205f0469..799c00a7 100644 --- a/web-client/src/components/sources/code-view.tsx +++ b/web-client/src/components/sources/code-view.tsx @@ -1,12 +1,10 @@ import { Suspense, createResource } from "solid-js"; -import { Connection } from "~/lib/connection/transport.ts"; -import { useRouteData } from "@solidjs/router"; import { Loader } from "~/components/loader"; +import { useConnection } from "~/context/connection-provider"; import { HighlighterLang, createHighlighter, getHighlightedCode, - getText, } from "~/lib/code-highlight"; import { Highlighter } from "shiki"; @@ -19,40 +17,37 @@ type CodeViewProps = { }; export default function CodeView(props: CodeViewProps) { - const { client } = useRouteData(); - - // We split the computations into 3 steps. This decouples them from the reactive props they don't need to react to - - // The text only needs to be computed when the the source changes - const [text] = createResource( - () => [client.sources, props.path, props.size] as const, - async (textProps) => getText(...textProps) + const { connectionStore } = useConnection(); + const [html] = createResource( + () => + [ + connectionStore.client.sources, + props.path, + props.size, + props.lang, + ] as const, + (sourceSignals) => getHighlightedCode(sourceSignals) ); // The used highlighter does not change at all atm so it does not need to be coupled - const [highlighter] = createResource(() => createHighlighter()); - - const html = ( - text: string | undefined, - highlighter: Highlighter | undefined, - lang: HighlighterLang, - highlightedLine?: number - ) => { - if (!text || !highlighter) return undefined; - return getHighlightedCode([text, highlighter, lang, highlightedLine]); - }; + // const [highlighter] = createResource(() => createHighlighter()); + + // const html = ( + // text: string | undefined, + // highlighter: Highlighter | undefined, + // lang: HighlighterLang, + // highlightedLine?: number + // ) => { + // if (!text || !highlighter) return undefined; + // return getHighlightedCode([text, highlighter, lang, highlightedLine]); + // }; return (
}>
diff --git a/web-client/src/components/sources/directory.tsx b/web-client/src/components/sources/directory.tsx index f6584eba..d43c839c 100644 --- a/web-client/src/components/sources/directory.tsx +++ b/web-client/src/components/sources/directory.tsx @@ -5,10 +5,10 @@ import { mergeProps, Show, Suspense, + untrack, } from "solid-js"; import { Entry } from "~/lib/proto/sources.ts"; -import { A, useRouteData } from "@solidjs/router"; -import { Connection } from "~/lib/connection/transport.ts"; +import { A } from "@solidjs/router"; import { Collapsible } from "@kobalte/core"; import CaretDown from "~/components/icons/caret-down.tsx"; import CaretRight from "~/components/icons/caret-right.tsx"; @@ -21,6 +21,7 @@ import { encodeFileName, } from "~/lib/sources/file-entries"; import { Loader } from "~/components/loader"; +import { useConnection } from "~/context/connection-provider"; type DirectoryProps = { parent?: Entry["path"]; @@ -40,11 +41,11 @@ type TreeEntryProps = { const liStyles = "hover:bg-gray-800 hover:text-white focus:bg-gray-800"; export function Directory(props: DirectoryProps) { - const { client } = useRouteData(); - const path = props.parent - ? `${props.parent}/${props.defaultPath}` - : props.defaultPath; - const [entries] = awaitEntries(client.sources, path); + const { connectionStore } = useConnection(); + const path = untrack(() => + props.parent ? `${props.parent}/${props.defaultPath}` : props.defaultPath + ); + const [entries] = awaitEntries(connectionStore.client.sources, path); const sortedEntries = () => entries()?.sort(sortByPath); return ( diff --git a/web-client/src/components/sources/image-view.tsx b/web-client/src/components/sources/image-view.tsx index 82740d25..c5e7f566 100644 --- a/web-client/src/components/sources/image-view.tsx +++ b/web-client/src/components/sources/image-view.tsx @@ -1,12 +1,11 @@ import { createResource, Show } from "solid-js"; -import { useRouteData } from "@solidjs/router"; -import { Connection } from "~/lib/connection/transport.ts"; +import { useConnection } from "~/context/connection-provider"; import { decodeFileName, getEntryBytes } from "~/lib/sources/file-entries.ts"; export function ImageView(props: { path: string; size: number; type: string }) { - const { client } = useRouteData(); + const { connectionStore } = useConnection(); const [bytes] = createResource( - () => [client.sources, props.path, props.size] as const, + () => [connectionStore.client.sources, props.path, props.size] as const, ([client, path, size]) => getEntryBytes(client, decodeFileName(path), size) ); diff --git a/web-client/src/components/status-indicator.tsx b/web-client/src/components/status-indicator.tsx deleted file mode 100644 index d6041637..00000000 --- a/web-client/src/components/status-indicator.tsx +++ /dev/null @@ -1,12 +0,0 @@ -type Props = { - status: "on" | "off"; -}; -export const StatusIndicator = (props: Props) => { - let className = "bg-gray-500"; - - if (props.status === "on") { - className = "bg-emerald-500"; - } - - return
; -}; diff --git a/web-client/src/context/connection-provider.tsx b/web-client/src/context/connection-provider.tsx new file mode 100644 index 00000000..d591135d --- /dev/null +++ b/web-client/src/context/connection-provider.tsx @@ -0,0 +1,38 @@ +import { + JSXElement, + createContext, + onCleanup, + untrack, + useContext, +} from "solid-js"; +import { setup } from "~/lib/connection/transport"; + +type ProviderProps = { + host: string; + port: string; + children: JSXElement; +}; + +const ConnectionContext = createContext>(); + +export function useConnection() { + const ctx = useContext(ConnectionContext); + + if (!ctx) throw new Error("can not find ConnectionContext.Provider"); + return ctx; +} + +export function ConnectionProvider(props: ProviderProps) { + const url = untrack(() => `http://${props.host}:${props.port}`); + const connection = setup(url); + + onCleanup(() => { + connection.connectionStore.abortController.abort(); + }); + + return ( + + {props.children} + + ); +} diff --git a/web-client/src/context/monitor-provider.tsx b/web-client/src/context/monitor-provider.tsx new file mode 100644 index 00000000..684ab5eb --- /dev/null +++ b/web-client/src/context/monitor-provider.tsx @@ -0,0 +1,56 @@ +import { + type JSXElement, + createEffect, + Show, + createContext, + useContext, +} from "solid-js"; +import { SetStoreFunction, createStore } from "solid-js/store"; +import { getTauriConfig, getTauriMetrics } from "~/lib/connection/getters"; +import { MonitorData, initialMonitorData } from "~/lib/connection/monitor"; +import { addStreamListneners } from "~/lib/connection/transport"; +import { useConnection } from "~/context/connection-provider"; + +type ProviderProps = { + children: JSXElement; +}; + +const MonitorContext = createContext<{ + monitorData: MonitorData; + setMonitorData: SetStoreFunction; +}>(); + +export function useMonitor() { + const ctx = useContext(MonitorContext); + + if (!ctx) throw new Error("can not find context"); + return ctx; +} + +export function MonitorProvider(props: ProviderProps) { + const { connectionStore } = useConnection(); + const [monitorData, setMonitorData] = createStore(initialMonitorData); + const [tauriMetrics] = getTauriMetrics(connectionStore.client.tauri); + const [tauriConfig] = getTauriConfig(connectionStore.client.tauri); + + createEffect(() => { + setMonitorData("tauriConfig", tauriConfig()); + }); + + createEffect(() => { + if (tauriMetrics()) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + setMonitorData("perf", tauriMetrics()!); + } + }); + + addStreamListneners(connectionStore.stream.update, setMonitorData); + + return ( + + + {props.children} + + + ); +} diff --git a/web-client/src/entry.tsx b/web-client/src/entry.tsx index ad32aae3..88da02d0 100644 --- a/web-client/src/entry.tsx +++ b/web-client/src/entry.tsx @@ -1,7 +1,7 @@ import { type RouteDefinition, useNavigate, useRoutes } from "@solidjs/router"; -import { lazy } from "solid-js"; -import { connect } from "./lib/connection/transport.ts"; +import { lazy, ErrorBoundary } from "solid-js"; import { setCDN } from "@crabnebula/file-icons"; +import { ErrorDialog } from "./components/error-dialog.tsx"; const ROUTES: RouteDefinition[] = [ { @@ -11,12 +11,6 @@ const ROUTES: RouteDefinition[] = [ { path: "/dash/:host/:port", component: lazy(() => import("./views/dashboard/layout.tsx")), - data: ({ params }) => { - const { host, port } = params; - const connection = connect(`http://${host}:${port}`); - - return connection; - }, children: [ { path: "/", @@ -51,5 +45,9 @@ export default function Entry() { setCDN("/icons"); - return ; + return ( + }> + + + ); } diff --git a/web-client/src/lib/connection/monitor.ts b/web-client/src/lib/connection/monitor.ts index 688f5463..8ff15954 100644 --- a/web-client/src/lib/connection/monitor.ts +++ b/web-client/src/lib/connection/monitor.ts @@ -1,4 +1,3 @@ -import { createContext, useContext } from "solid-js"; import { HealthCheckResponse_ServingStatus } from "~/lib/proto/health"; import { LogEvent } from "~/lib/proto/logs"; import { Field, Metadata } from "~/lib/proto/common"; @@ -8,6 +7,8 @@ import { timestampToDate } from "~/lib/formatters"; import { AppMetadata } from "../proto/meta"; import { Versions } from "../proto/tauri"; +export type HealthStatus = keyof typeof HealthCheckResponse_ServingStatus; + export type Span = { id: bigint; parentId?: bigint; @@ -34,6 +35,8 @@ export type MonitorData = { perfStartDate: Date | null; perfReadyDate: Date | null; perfElapsed: Timestamp | null; + + connectionStatus: HealthStatus; }; export const initialMonitorData: MonitorData = { @@ -51,6 +54,10 @@ export const initialMonitorData: MonitorData = { readyAt: undefined, }, + get connectionStatus() { + return HealthCheckResponse_ServingStatus[this.health] as HealthStatus; + }, + get perfStartDate() { return this.perf.initializedAt ? timestampToDate(this.perf.initializedAt) @@ -72,14 +79,3 @@ export const initialMonitorData: MonitorData = { } }, }; - -export const MonitorContext = createContext<{ - monitorData: MonitorData; -}>(); - -export function useMonitor() { - const ctx = useContext(MonitorContext); - - if (!ctx) throw new Error("can not find context"); - return ctx; -} diff --git a/web-client/src/lib/connection/transport.ts b/web-client/src/lib/connection/transport.ts deleted file mode 100644 index 55989e21..00000000 --- a/web-client/src/lib/connection/transport.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { createContext, useContext } from "solid-js"; -import { GrpcWebFetchTransport } from "@protobuf-ts/grpcweb-transport"; -import { HealthClient } from "~/lib/proto/health.client"; -import { InstrumentClient } from "~/lib/proto/instrument.client"; -import { TauriClient } from "~/lib/proto/tauri.client"; -import { SourcesClient } from "~/lib/proto/sources.client.ts"; -import { MetadataClient } from "../proto/meta.client"; - -export function connect(url: string) { - const abortController = new AbortController(); - const transport = new GrpcWebFetchTransport({ - format: "binary", - baseUrl: url, - abort: abortController.signal, - }); - - const instrumentClient = new InstrumentClient(transport); - const tauriClient = new TauriClient(transport); - const healthClient = new HealthClient(transport); - const sourcesClient = new SourcesClient(transport); - const metaClient = new MetadataClient(transport); - - return { - abortController, - client: { - tauri: tauriClient, - health: healthClient, - instrument: instrumentClient, - sources: sourcesClient, - meta: metaClient, - }, - }; -} - -export type Connection = ReturnType; - -export function disconnect(controller: AbortController) { - controller.abort(); -} - -export const TransportContext = createContext<{ - transport: GrpcWebFetchTransport; - abort: AbortController; -}>(); - -export function useTransport() { - const ctx = useContext(TransportContext); - - if (!ctx) throw new Error("can not find TransportContext.Provider"); - return ctx; -} diff --git a/web-client/src/lib/connection/transport.tsx b/web-client/src/lib/connection/transport.tsx new file mode 100644 index 00000000..d49c13db --- /dev/null +++ b/web-client/src/lib/connection/transport.tsx @@ -0,0 +1,105 @@ +import { GrpcWebFetchTransport } from "@protobuf-ts/grpcweb-transport"; +import { HealthClient } from "~/lib/proto/health.client"; +import { InstrumentClient } from "~/lib/proto/instrument.client"; +import { TauriClient } from "~/lib/proto/tauri.client"; +import { SourcesClient } from "~/lib/proto/sources.client.ts"; +import { MetadataClient } from "../proto/meta.client"; +import { SetStoreFunction, createStore, produce } from "solid-js/store"; +import { HealthCheckRequest } from "../proto/health"; +import { InstrumentRequest } from "../proto/instrument"; +import { updateSpanMetadata } from "../span/update-span-metadata"; +import { updatedSpans } from "../span/update-spans"; +import { MonitorData } from "./monitor"; +import { createResource } from "solid-js"; + +export function connect(url: string) { + const abortController = new AbortController(); + const transport = new GrpcWebFetchTransport({ + format: "binary", + baseUrl: url, + abort: abortController.signal, + }); + + const instrumentClient = new InstrumentClient(transport); + const tauriClient = new TauriClient(transport); + const healthClient = new HealthClient(transport); + const sourcesClient = new SourcesClient(transport); + const metaClient = new MetadataClient(transport); + + const healthStream = healthClient.watch( + /** + * empty string means all services. + */ + HealthCheckRequest.create({ service: "" }) + ); + const updateStream = instrumentClient.watchUpdates( + InstrumentRequest.create({}) + ); + + const connectionStore = { + serviceUrl: url, + abortController, + client: { + tauri: tauriClient, + health: healthClient, + instrument: instrumentClient, + sources: sourcesClient, + meta: metaClient, + }, + stream: { + health: healthStream, + update: updateStream, + }, + }; + + return connectionStore; +} + +export function setup(url: string) { + const [connectionStore, setConnection] = createStore(connect(url)); + + return { connectionStore, setConnection }; +} + +type UpdateStream = ReturnType["stream"]["update"]; + +export function addStreamListneners( + stream: UpdateStream, + setMonitorData: SetStoreFunction +) { + stream.responses.onMessage((update) => { + setMonitorData("health", 1); + if (update.newMetadata.length > 0) { + setMonitorData("metadata", (prev) => updateSpanMetadata(prev, update)); + } + + const logsUpdate = update.logsUpdate; + if (logsUpdate && logsUpdate.logEvents.length > 0) { + setMonitorData("logs", (prev) => [...prev, ...logsUpdate.logEvents]); + } + + const spansUpdate = update.spansUpdate; + if (spansUpdate && spansUpdate.spanEvents.length > 0) { + setMonitorData( + "spans", + produce((clonedSpans) => + updatedSpans(clonedSpans, spansUpdate.spanEvents) + ) + ); + } + }); +} + +// function reconnect(connectionStore: ReturnType, +// setMonitorData: SetStoreFunction +// ) { +// connectionStore.abortController.abort() + +// const [data] = createResource(async () => { +// const newConnection = connect(connectionStore.serviceUrl) + +// addStreamListneners(connectionStore.stream.update, setMonitorData) + +// }) + +// } diff --git a/web-client/src/views/connect.tsx b/web-client/src/views/connect.tsx index caba4db6..2d8411d9 100644 --- a/web-client/src/views/connect.tsx +++ b/web-client/src/views/connect.tsx @@ -21,13 +21,7 @@ export default function Connect() { --inspect flag. Then, paste the appropriate connection values below.

-
{ - e.preventDefault(); - navigate(`/dash/${host()}/${port()}/`); - }} - class="grid gap-8 border-neutral-800 p-4 rounded" - > +
web socket URL host
{ + e.preventDefault(); + navigate(`/dash/${host()}/${port()}/`); + }} > Inspect diff --git a/web-client/src/views/dashboard/console.tsx b/web-client/src/views/dashboard/console.tsx index 764efb31..a0ad9917 100644 --- a/web-client/src/views/dashboard/console.tsx +++ b/web-client/src/views/dashboard/console.tsx @@ -2,9 +2,9 @@ import { For, Show, createSignal } from "solid-js"; import { AutoscrollPane } from "~/components/autoscroll-pane"; import { FilterToggle } from "~/components/filter-toggle"; import { formatTimestamp, timestampToDate } from "~/lib/formatters"; -import { useMonitor } from "~/lib/connection/monitor"; import { Toolbar } from "~/components/toolbar"; import { Metadata_Level, MetaId } from "~/lib/proto/common"; +import { useMonitor } from "~/context/monitor-provider"; const levelStyles = (level: Metadata_Level | undefined) => { switch (level) { diff --git a/web-client/src/views/dashboard/layout.tsx b/web-client/src/views/dashboard/layout.tsx index a5630887..ecf81517 100644 --- a/web-client/src/views/dashboard/layout.tsx +++ b/web-client/src/views/dashboard/layout.tsx @@ -1,163 +1,46 @@ -import { createEffect, onCleanup } from "solid-js"; -import { createStore, produce } from "solid-js/store"; -import { Outlet, useRouteData } from "@solidjs/router"; +import { ErrorBoundary } from "solid-js"; +import { Outlet, useParams } from "@solidjs/router"; import { Navigation } from "~/components/navigation"; import { BootTime } from "~/components/boot-time"; import { HealthStatus } from "~/components/health-status.tsx"; -import { initialMonitorData, MonitorContext } from "~/lib/connection/monitor"; -import { InstrumentRequest } from "~/lib/proto/instrument"; -import { - getHealthStatus, - getTauriConfig, - getTauriMetrics, - getVersions, - getMetadata, -} from "~/lib/connection/getters"; -import { - HealthCheckRequest, - HealthCheckResponse, - HealthCheckResponse_ServingStatus, -} from "~/lib/proto/health"; -import { Connection, disconnect } from "~/lib/connection/transport"; import { Logo } from "~/components/crabnebula-logo"; -import { useNavigate } from "@solidjs/router"; import { DisconnectButton } from "~/components/disconnect-button"; -import { updatedSpans } from "~/lib/span/update-spans"; -import { updateSpanMetadata } from "~/lib/span/update-span-metadata"; -import { returnLatestSchemaForVersion } from "~/lib/tauri/tauri-conf-schema"; +import { ErrorDialog } from "~/components/error-dialog"; +import { MonitorProvider } from "~/context/monitor-provider"; +import { ConnectionProvider } from "~/context/connection-provider"; -export default function Layout() { - const { abortController, client } = useRouteData(); - - const [monitorData, setMonitorData] = createStore(initialMonitorData); - const [tauriMetrics] = getTauriMetrics(client.tauri); - const [tauriConfig] = getTauriConfig(client.tauri); - const [tauriVersions] = getVersions(client.tauri); - const [appMetaData] = getMetadata(client.meta); - - const healthStream = client.health.watch( - HealthCheckRequest.create({ service: "" }) - ); - - const navigate = useNavigate(); - - function closeSession() { - // Clean up all the listeners to make sure we don't try to close the session multiple times. - removeListeners.forEach((removeListener) => removeListener()); - - setMonitorData("health", HealthCheckResponse_ServingStatus.UNKNOWN); - disconnect(abortController); - navigate("/"); - } - - healthStream.responses.onMessage((res: HealthCheckResponse) => { - const status = getHealthStatus(res); - - setMonitorData("health", status); - }); - - createEffect(() => { - setMonitorData("tauriConfig", tauriConfig()); - }); - - createEffect(() => { - const versions = tauriVersions(); - if (versions) { - const schema = returnLatestSchemaForVersion(versions.tauri); - setMonitorData("schema", schema); - } - setMonitorData("tauriVersions", versions); - }); +type RouteParams = Record<"host" | "port", string>; - createEffect(() => { - setMonitorData("appMetaData", appMetaData()); - }); - - createEffect(() => { - if (tauriMetrics()) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - setMonitorData("perf", tauriMetrics()!); - } - }); - - const updateStream = client.instrument.watchUpdates( - InstrumentRequest.create({}) - ); - - const removeListeners = [ - healthStream.responses.onError(() => { - closeSession(); - }), - healthStream.responses.onComplete(() => { - closeSession(); - }), - updateStream.responses.onError(() => { - closeSession(); - }), - updateStream.responses.onComplete(() => { - closeSession(); - }), - ]; - - updateStream.responses.onMessage((update) => { - if (update.newMetadata.length > 0) { - setMonitorData("metadata", (prev) => updateSpanMetadata(prev, update)); - } - - if (update.logsUpdate && update.spansUpdate) { - console.assert( - update.logsUpdate.droppedEvents == 0n, - "Dropped log events because the internal event buffer was at capacity. This is a bug, please report!" - ); - console.assert( - update.spansUpdate.droppedEvents == 0n, - "Dropped span events because the internal event buffer was at capacity. This is a bug, please report!" - ); - } - - const logsUpdate = update.logsUpdate; - if (logsUpdate && logsUpdate.logEvents.length > 0) { - setMonitorData("logs", (prev) => [...prev, ...logsUpdate.logEvents]); - } - - const spansUpdate = update.spansUpdate; - if (spansUpdate && spansUpdate.spanEvents.length > 0) { - setMonitorData( - "spans", - produce((clonedSpans) => - updatedSpans(clonedSpans, spansUpdate.spanEvents) - ) - ); - } - }); - - onCleanup(() => { - abortController.abort(); - }); +export default function Layout() { + const { host, port } = useParams(); return ( - -
-
- - - -
- -
-
- -
-
- Built by CrabNebula -
-
- -
-
+ }> + + +
+
+ + + +
+ +
+
+ +
+
+ Built by CrabNebula +
+
+ +
+
+
+
); } diff --git a/web-client/src/views/dashboard/span-waterfall.tsx b/web-client/src/views/dashboard/span-waterfall.tsx index 70bbcb3d..4601a47b 100644 --- a/web-client/src/views/dashboard/span-waterfall.tsx +++ b/web-client/src/views/dashboard/span-waterfall.tsx @@ -1,4 +1,3 @@ -import { useMonitor } from "~/lib/connection/monitor"; import { Toolbar } from "~/components/toolbar"; import { createEffect, createSignal } from "solid-js"; import { formatSpansForUi } from "~/lib/span/format-spans-for-ui"; @@ -7,6 +6,7 @@ import { SplitPane } from "~/components/split-pane"; import { SpanDetailPanel } from "~/components/span/span-detail-panel"; import { ColumnSort, SpanList } from "~/components/span/span-list"; import { SpanScaleSlider } from "~/components/span/span-scale-slider"; +import { useMonitor } from "~/context/monitor-provider"; export default function SpanWaterfall() { const { monitorData } = useMonitor(); diff --git a/web-client/src/views/dashboard/tauri-config.tsx b/web-client/src/views/dashboard/tauri-config.tsx new file mode 100644 index 00000000..631ac0ba --- /dev/null +++ b/web-client/src/views/dashboard/tauri-config.tsx @@ -0,0 +1,85 @@ +import { For, Show } from "solid-js"; +import { Collapsible } from "@kobalte/core"; +import { evaluateCSP } from "~/lib/security"; +import { useMonitor } from "~/context/monitor-provider"; + +function formatBooleanProp(prop?: boolean) { + return prop ? "✅" : "❌"; +} + +export default function TauriConfig() { + const { monitorData } = useMonitor(); + + return ( + <> + + {(pkg) => ( +
+

+ Inspecting config for:{" "} + + {pkg().productName} - v{pkg().version} + +

+
+ )} +
+ + + {(sec) => ( +
+ + +

Security

+
+ +
    +
  • + CSP: {sec().csp} +
    {evaluateCSP(sec().csp)}
    +
  • +
  • + Dangerous Disable Asset CSP Modification:{" "} + {formatBooleanProp( + sec().dangerousDisableAssetCspModification + )} +
  • +
  • + Dangerous Remote Domain IPC Access:{" "} + {formatBooleanProp(sec().dangerousRemoteDomainIpcAccess)} +
  • +
  • + Freeze Prototype: {formatBooleanProp(sec().freezePrototype)} +
  • +
+
+
+
+ )} +
+ + {(bundle) => ( +
+

Icons

+
    + {(icon) =>
  • {icon()}
  • }
    +
+
+ )} +
+
+ + +

show JSON source

+
+ +
+              {/* {JSON.stringify(tauriConfig()?.result.tauri, null, 2)} */}
+              {JSON.stringify(monitorData.tauriConfig, null, 2)}
+            
+
+
+
+ + ); +} diff --git a/web-client/tailwind.config.ts b/web-client/tailwind.config.ts index 5a7bbc68..b7f8ba2d 100644 --- a/web-client/tailwind.config.ts +++ b/web-client/tailwind.config.ts @@ -1,5 +1,6 @@ import type { Config } from "tailwindcss"; import scrollbar from "tailwind-scrollbar"; +import kobalte from "@kobalte/tailwindcss"; import defaultTheme from "tailwindcss/defaultTheme"; export default { @@ -35,5 +36,5 @@ export default { }, }, }, - plugins: [scrollbar({ nocompatible: true })], + plugins: [scrollbar({ nocompatible: true }), kobalte({ prefix: "kb" })], } satisfies Config; From 42cc3f6a4cfc2cf7e2fc02734ae64ba997651d2b Mon Sep 17 00:00:00 2001 From: Atila Fassina Date: Tue, 21 Nov 2023 12:36:45 +0100 Subject: [PATCH 2/7] restablish connection automatically --- web-client/src/components/dialog.tsx | 4 +- web-client/src/components/error-dialog.tsx | 107 +++++++++++--------- web-client/src/components/health-status.tsx | 82 ++++++++------- 3 files changed, 102 insertions(+), 91 deletions(-) diff --git a/web-client/src/components/dialog.tsx b/web-client/src/components/dialog.tsx index 4bb68127..6ca9896b 100644 --- a/web-client/src/components/dialog.tsx +++ b/web-client/src/components/dialog.tsx @@ -16,7 +16,7 @@ export function Dialog(p: Props) {
- +
{props.title}
@@ -37,7 +37,7 @@ export function Dialog(p: Props) { Reset App diff --git a/web-client/src/components/error-dialog.tsx b/web-client/src/components/error-dialog.tsx index 4856d9a2..6fab283a 100644 --- a/web-client/src/components/error-dialog.tsx +++ b/web-client/src/components/error-dialog.tsx @@ -1,62 +1,69 @@ -import { AlertDialog, Collapsible } from "@kobalte/core"; import { A } from "@solidjs/router"; -import { Dialog } from "~/components/dialog"; +import * as pkg from "~/../package.json"; type Props = { error: unknown; }; -function ErrorTitle() { - return ( - - Irrecoverable Error - - ); -} - export function ErrorDialog(props: Props) { return ( - }> -
-

Something terrible happened.

-

The log is on the way and we'll work on it!

+ <> +
+
-
- - Reset App - - - Something else - +
+
+

+ Irrecoverable Error +

+
+

Something terrible happened.

+

The log is on the way and we'll work on it!

+
+
+
+
+

System log

+
+              {props.error?.toString() || String(props.error)}
+            
+
    +
  • App version: {pkg.version}
  • +
  • Browser: {window.navigator.userAgent}
  • +
+
+
+ + Reset App + + +
+
+
- - - - See the stack trace - - ↓ - - - -
{props.error?.toString() || String(props.error)}
-
-
-
+ ); } diff --git a/web-client/src/components/health-status.tsx b/web-client/src/components/health-status.tsx index 6aa6ebeb..b0a82926 100644 --- a/web-client/src/components/health-status.tsx +++ b/web-client/src/components/health-status.tsx @@ -1,21 +1,11 @@ -import { - HealthCheckRequest, - HealthCheckResponse_ServingStatus, -} from "~/lib/proto/health"; -import { Show, createEffect, createSignal, on, onMount } from "solid-js"; +import { HealthCheckResponse_ServingStatus } from "~/lib/proto/health"; +import { Show, createEffect, createSignal, onMount } from "solid-js"; import { Dialog } from "./dialog"; import { addStreamListneners, connect } from "~/lib/connection/transport"; -import { produce } from "solid-js/store"; +import { reconcile } from "solid-js/store"; import { useConnection } from "~/context/connection-provider"; import { useMonitor } from "~/context/monitor-provider"; -/** - * Reconnect - * terminate all connections - * createResource => setup a new connection, push new data to monitor store. [retry for 5s] - * return resource. - */ - const variant = (status: HealthCheckResponse_ServingStatus) => { return [ // unknown @@ -36,37 +26,46 @@ const variant = (status: HealthCheckResponse_ServingStatus) => { ][status]; }; +const INITIAL_ERROR_TIMER_SECONDS = 10; + export function HealthStatus() { + const updateErrorHandler = () => { + console.error("an error happened on Updates Stream"); + // if (isConnectionDead()) { + /** + * we do nothing because it's not an instrumentation issue. + */ + return; + // } + }; + + const healthErrorHandler = () => { + console.error("an error happened on Health Stream"); + setMonitorData("health", 0); + setConnectionDead(true); + + /** cleanup possible connections */ + connectionStore.abortController.abort(); + reconnect(); + // setTimeout(reconnect, INITIAL_ERROR_TIMER_SECONDS * 1000); + }; + + function reconnect() { + const newConnection = connect(connectionStore.serviceUrl); + setConnection(reconcile(newConnection, { merge: false })); + + addStreamListneners(connectionStore.stream.update, setMonitorData); + connectionStore.stream.health.responses.onError(healthErrorHandler); + connectionStore.stream.update.responses.onError(updateErrorHandler); + } + const { connectionStore, setConnection } = useConnection(); const { monitorData, setMonitorData } = useMonitor(); const [isConnectionDead, setConnectionDead] = createSignal(false); onMount(() => { - connectionStore.stream.health.responses.onError(() => { - console.error("an error happened on Health Stream"); - setMonitorData("health", 0); - setConnectionDead(true); - - /** cleanup connections */ - connectionStore.abortController.abort(); - - setTimeout(() => { - const newConnection = connect(connectionStore.serviceUrl); - console.log(newConnection); - addStreamListneners(newConnection.stream.update, setMonitorData); - setConnection(produce(() => newConnection)); - }, 10000); - }); - - connectionStore.stream.update.responses.onError(() => { - console.error("an error happened on Updates Stream"); - // if (isConnectionDead()) { - /**22 - * we do nothing because it's not an instrumentation issue. - */ - return; - // } - }); + connectionStore.stream.health.responses.onError(healthErrorHandler); + connectionStore.stream.update.responses.onError(updateErrorHandler); }); createEffect(() => { @@ -78,7 +77,12 @@ export function HealthStatus() { return (
- Tauri app stopped streaming. + +

Streaming has stopped.

+

+ Waiting on new signal from your Tauri app. +

+
{variant(monitorData.health).tooltip} From 7fbf04dcb10694c2bbf5cf83930c1760a4f2f9ef Mon Sep 17 00:00:00 2001 From: Atila Fassina Date: Tue, 21 Nov 2023 16:42:33 +0100 Subject: [PATCH 3/7] fix merge conflicts, cleanup code --- web-client/src/components/dialog.tsx | 50 --------- web-client/src/components/error-dialog.tsx | 103 +++++++----------- web-client/src/components/error-root.tsx | 69 ++++++++++++ web-client/src/components/health-status.tsx | 6 +- .../src/components/sources/code-view.tsx | 44 ++++---- .../src/components/span/span-detail.tsx | 2 +- web-client/src/entry.tsx | 4 +- web-client/src/lib/tauri/tauri-conf-schema.ts | 10 +- web-client/src/views/dashboard/layout.tsx | 52 ++++----- 9 files changed, 168 insertions(+), 172 deletions(-) delete mode 100644 web-client/src/components/dialog.tsx create mode 100644 web-client/src/components/error-root.tsx diff --git a/web-client/src/components/dialog.tsx b/web-client/src/components/dialog.tsx deleted file mode 100644 index 6ca9896b..00000000 --- a/web-client/src/components/dialog.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { AlertDialog } from "@kobalte/core"; -import { A } from "@solidjs/router"; -import { JSXElement, Show, mergeProps } from "solid-js"; - -type Props = { - title?: JSXElement; - children: JSXElement; -}; - -export function Dialog(p: Props) { - const props = mergeProps({ title: "Alert" }, p); - - return ( - - {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion*/} - - -
- -
- {props.title} -
- - {props.children} - -
- { - window.location.reload(); - }} - > - Reload route - - - Dismiss - - - Reset App - -
-
-
-
-
- ); -} diff --git a/web-client/src/components/error-dialog.tsx b/web-client/src/components/error-dialog.tsx index 6fab283a..e703a404 100644 --- a/web-client/src/components/error-dialog.tsx +++ b/web-client/src/components/error-dialog.tsx @@ -1,69 +1,50 @@ +import { AlertDialog } from "@kobalte/core"; import { A } from "@solidjs/router"; -import * as pkg from "~/../package.json"; +import { JSXElement, Show, mergeProps } from "solid-js"; type Props = { - error: unknown; + title?: JSXElement; + children: JSXElement; }; -export function ErrorDialog(props: Props) { +export function ErrorDialog(p: Props) { + const props = mergeProps({ title: "Alert" }, p); + return ( - <> -
- -
-
-
-

- Irrecoverable Error -

-
-

Something terrible happened.

-

The log is on the way and we'll work on it!

-
-
-
-
-

System log

-
-              {props.error?.toString() || String(props.error)}
-            
-
    -
  • App version: {pkg.version}
  • -
  • Browser: {window.navigator.userAgent}
  • -
-
-
- - Reset App - - -
-
- -
- + + {/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion*/} + + +
+ +
+ {props.title} +
+ + {props.children} + +
+ { + window.location.reload(); + }} + > + Reload route + + + Dismiss + + + Reset App + +
+
+
+
+
); } diff --git a/web-client/src/components/error-root.tsx b/web-client/src/components/error-root.tsx new file mode 100644 index 00000000..ba49e9c1 --- /dev/null +++ b/web-client/src/components/error-root.tsx @@ -0,0 +1,69 @@ +import { A } from "@solidjs/router"; +import * as pkg from "~/../package.json"; + +type Props = { + error: unknown; +}; + +export function ErrorRoot(props: Props) { + return ( + <> +
+ +
+
+
+

+ Irrecoverable Error +

+
+

Something terrible happened.

+

The log is on the way and we'll work on it!

+
+
+
+
+

System log

+
+              {props.error?.toString() || String(props.error)}
+            
+
    +
  • App version: {pkg.version}
  • +
  • Browser: {window.navigator.userAgent}
  • +
+
+
+ + Reset App + + +
+
+ +
+ + ); +} diff --git a/web-client/src/components/health-status.tsx b/web-client/src/components/health-status.tsx index b0a82926..f9a0eca7 100644 --- a/web-client/src/components/health-status.tsx +++ b/web-client/src/components/health-status.tsx @@ -1,6 +1,6 @@ import { HealthCheckResponse_ServingStatus } from "~/lib/proto/health"; import { Show, createEffect, createSignal, onMount } from "solid-js"; -import { Dialog } from "./dialog"; +import { ErrorDialog } from "./error-dialog"; import { addStreamListneners, connect } from "~/lib/connection/transport"; import { reconcile } from "solid-js/store"; import { useConnection } from "~/context/connection-provider"; @@ -77,12 +77,12 @@ export function HealthStatus() { return (
- +

Streaming has stopped.

Waiting on new signal from your Tauri app.

-
+
{variant(monitorData.health).tooltip} diff --git a/web-client/src/components/sources/code-view.tsx b/web-client/src/components/sources/code-view.tsx index 799c00a7..c443fcf1 100644 --- a/web-client/src/components/sources/code-view.tsx +++ b/web-client/src/components/sources/code-view.tsx @@ -1,3 +1,4 @@ +import { Highlighter } from "shiki"; import { Suspense, createResource } from "solid-js"; import { Loader } from "~/components/loader"; import { useConnection } from "~/context/connection-provider"; @@ -5,10 +6,9 @@ import { HighlighterLang, createHighlighter, getHighlightedCode, + getText, } from "~/lib/code-highlight"; -import { Highlighter } from "shiki"; - type CodeViewProps = { path: string; size: number; @@ -18,36 +18,36 @@ type CodeViewProps = { export default function CodeView(props: CodeViewProps) { const { connectionStore } = useConnection(); - const [html] = createResource( - () => - [ - connectionStore.client.sources, - props.path, - props.size, - props.lang, - ] as const, - (sourceSignals) => getHighlightedCode(sourceSignals) + + const [text] = createResource( + () => [connectionStore.client.sources, props.path, props.size] as const, + async (textProps) => getText(...textProps) ); // The used highlighter does not change at all atm so it does not need to be coupled - // const [highlighter] = createResource(() => createHighlighter()); + const [highlighter] = createResource(() => createHighlighter()); - // const html = ( - // text: string | undefined, - // highlighter: Highlighter | undefined, - // lang: HighlighterLang, - // highlightedLine?: number - // ) => { - // if (!text || !highlighter) return undefined; - // return getHighlightedCode([text, highlighter, lang, highlightedLine]); - // }; + const html = ( + text: string | undefined, + highlighter: Highlighter | undefined, + lang: HighlighterLang, + highlightedLine?: number + ) => { + if (!text || !highlighter) return undefined; + return getHighlightedCode([text, highlighter, lang, highlightedLine]); + }; return (
}>
diff --git a/web-client/src/components/span/span-detail.tsx b/web-client/src/components/span/span-detail.tsx index 61c1fd3d..f8f65462 100644 --- a/web-client/src/components/span/span-detail.tsx +++ b/web-client/src/components/span/span-detail.tsx @@ -1,5 +1,4 @@ import { For, createResource, Show } from "solid-js"; -import { useMonitor } from "~/lib/connection/monitor"; import { formatSpansForUi } from "~/lib/span/format-spans-for-ui"; import { getIpcRequestValues } from "~/lib/span/get-ipc-request-value"; import { createHighlighter, getHighlightedCode } from "~/lib/code-highlight"; @@ -9,6 +8,7 @@ import { getChildrenList } from "~/lib/span/get-children-list"; import { SpanDetailTrace } from "./span-detail-trace"; import { SpanDetailArgs } from "./span-detail-args"; import { isIpcSpanName } from "~/lib/span/isIpcSpanName"; +import { useMonitor } from "~/context/monitor-provider"; export function SpanDetail() { const [searchParams] = useSearchParams(); diff --git a/web-client/src/entry.tsx b/web-client/src/entry.tsx index 88da02d0..b4315bce 100644 --- a/web-client/src/entry.tsx +++ b/web-client/src/entry.tsx @@ -1,7 +1,7 @@ import { type RouteDefinition, useNavigate, useRoutes } from "@solidjs/router"; import { lazy, ErrorBoundary } from "solid-js"; import { setCDN } from "@crabnebula/file-icons"; -import { ErrorDialog } from "./components/error-dialog.tsx"; +import { ErrorRoot } from "./components/error-root.tsx"; const ROUTES: RouteDefinition[] = [ { @@ -46,7 +46,7 @@ export default function Entry() { setCDN("/icons"); return ( - }> + }> ); diff --git a/web-client/src/lib/tauri/tauri-conf-schema.ts b/web-client/src/lib/tauri/tauri-conf-schema.ts index 816a1a90..6030a0ed 100644 --- a/web-client/src/lib/tauri/tauri-conf-schema.ts +++ b/web-client/src/lib/tauri/tauri-conf-schema.ts @@ -3,12 +3,12 @@ import tauriConfigSchemaV2 from "./tauri-conf-schema-v2.json"; import { Draft07, JsonSchema, JsonPointer } from "json-schema-library"; import { createResource, Signal } from "solid-js"; import { useRouteData, useLocation, useParams } from "@solidjs/router"; -import { Connection } from "~/lib/connection/transport.ts"; import { awaitEntries, getEntryBytes } from "~/lib/sources/file-entries"; import { useConfiguration } from "~/components/tauri/configuration-context"; import { unwrap, reconcile } from "solid-js/store"; -import { useMonitor } from "../connection/monitor"; import { bytesToText } from "../code-highlight"; +import { useConnection } from "~/context/connection-provider"; +import { useMonitor } from "~/context/monitor-provider"; export type configurationStore = { configs?: configurationObject[]; @@ -74,8 +74,8 @@ export function retrieveConfigurations() { if (configurations.configs) return createResource(() => configurations.configs); - const { client } = useRouteData(); - const [entries] = awaitEntries(client.sources, ""); + const { connectionStore } = useConnection(); + const [entries] = awaitEntries(connectionStore.client.sources, ""); return createResource( entries, @@ -85,7 +85,7 @@ export function retrieveConfigurations() { return await Promise.all( filteredEntries.map(async (e): Promise => { const bytes = await getEntryBytes( - client.sources, + connectionStore.client.sources, e.path, Number(e.size) ); diff --git a/web-client/src/views/dashboard/layout.tsx b/web-client/src/views/dashboard/layout.tsx index ecf81517..b189fef4 100644 --- a/web-client/src/views/dashboard/layout.tsx +++ b/web-client/src/views/dashboard/layout.tsx @@ -1,11 +1,9 @@ -import { ErrorBoundary } from "solid-js"; import { Outlet, useParams } from "@solidjs/router"; import { Navigation } from "~/components/navigation"; import { BootTime } from "~/components/boot-time"; import { HealthStatus } from "~/components/health-status.tsx"; import { Logo } from "~/components/crabnebula-logo"; import { DisconnectButton } from "~/components/disconnect-button"; -import { ErrorDialog } from "~/components/error-dialog"; import { MonitorProvider } from "~/context/monitor-provider"; import { ConnectionProvider } from "~/context/connection-provider"; @@ -15,32 +13,30 @@ export default function Layout() { const { host, port } = useParams(); return ( - }> - - -
-
- - - -
- -
-
- -
-
- Built by CrabNebula -
-
- + + +
+
+ + +
- - - + +
+
+ +
+
+ Built by CrabNebula +
+
+ +
+
+
); } From 674b694e92fef1656d5a650fa341d25ed5077d6d Mon Sep 17 00:00:00 2001 From: Atila Fassina Date: Wed, 22 Nov 2023 12:18:36 +0100 Subject: [PATCH 4/7] remove unused var --- web-client/src/components/health-status.tsx | 7 ++----- web-client/src/lib/connection/transport.tsx | 14 -------------- web-client/src/views/dashboard/tauri-config.tsx | 1 - 3 files changed, 2 insertions(+), 20 deletions(-) diff --git a/web-client/src/components/health-status.tsx b/web-client/src/components/health-status.tsx index f9a0eca7..7ebf3565 100644 --- a/web-client/src/components/health-status.tsx +++ b/web-client/src/components/health-status.tsx @@ -11,7 +11,7 @@ const variant = (status: HealthCheckResponse_ServingStatus) => { // unknown { style: "inline-block mr-3 w-3 h-3 bg-gray-200 rounded-full", - tooltip: "Disconected", + tooltip: "Disconnected", }, // serving { @@ -21,13 +21,11 @@ const variant = (status: HealthCheckResponse_ServingStatus) => { // not serving { style: "inline-block mr-3 w-3 h-3 bg-red-500 rounded-full", - tooltip: "Disconected", + tooltip: "Disconnected", }, ][status]; }; -const INITIAL_ERROR_TIMER_SECONDS = 10; - export function HealthStatus() { const updateErrorHandler = () => { console.error("an error happened on Updates Stream"); @@ -47,7 +45,6 @@ export function HealthStatus() { /** cleanup possible connections */ connectionStore.abortController.abort(); reconnect(); - // setTimeout(reconnect, INITIAL_ERROR_TIMER_SECONDS * 1000); }; function reconnect() { diff --git a/web-client/src/lib/connection/transport.tsx b/web-client/src/lib/connection/transport.tsx index d49c13db..ca43d139 100644 --- a/web-client/src/lib/connection/transport.tsx +++ b/web-client/src/lib/connection/transport.tsx @@ -89,17 +89,3 @@ export function addStreamListneners( } }); } - -// function reconnect(connectionStore: ReturnType, -// setMonitorData: SetStoreFunction -// ) { -// connectionStore.abortController.abort() - -// const [data] = createResource(async () => { -// const newConnection = connect(connectionStore.serviceUrl) - -// addStreamListneners(connectionStore.stream.update, setMonitorData) - -// }) - -// } diff --git a/web-client/src/views/dashboard/tauri-config.tsx b/web-client/src/views/dashboard/tauri-config.tsx index 631ac0ba..8f08145f 100644 --- a/web-client/src/views/dashboard/tauri-config.tsx +++ b/web-client/src/views/dashboard/tauri-config.tsx @@ -74,7 +74,6 @@ export default function TauriConfig() {
-              {/* {JSON.stringify(tauriConfig()?.result.tauri, null, 2)} */}
               {JSON.stringify(monitorData.tauriConfig, null, 2)}
             
From 2bc35c44cf113f57847a3aeb212cce8bc19d565b Mon Sep 17 00:00:00 2001 From: Atila Fassina Date: Wed, 22 Nov 2023 14:18:28 +0100 Subject: [PATCH 5/7] hard navigation on Reset --- web-client/src/components/error-dialog.tsx | 5 ++--- web-client/src/components/error-root.tsx | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/web-client/src/components/error-dialog.tsx b/web-client/src/components/error-dialog.tsx index e703a404..b08ee281 100644 --- a/web-client/src/components/error-dialog.tsx +++ b/web-client/src/components/error-dialog.tsx @@ -1,5 +1,4 @@ import { AlertDialog } from "@kobalte/core"; -import { A } from "@solidjs/router"; import { JSXElement, Show, mergeProps } from "solid-js"; type Props = { @@ -35,12 +34,12 @@ export function ErrorDialog(p: Props) { Dismiss - Reset App - +
diff --git a/web-client/src/components/error-root.tsx b/web-client/src/components/error-root.tsx index ba49e9c1..5107f496 100644 --- a/web-client/src/components/error-root.tsx +++ b/web-client/src/components/error-root.tsx @@ -1,4 +1,3 @@ -import { A } from "@solidjs/router"; import * as pkg from "~/../package.json"; type Props = { @@ -33,12 +32,12 @@ export function ErrorRoot(props: Props) {
- Reset App - +