Radish alpha
r
Radicle web interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
Add externalNodes to node detail view
Sebastian Martinez committed 2 years ago
commit 1add6958cb8a97fef6974ab1c9bc5ed90c52ae9a
parent 38fbddeb21714dcf389c3f51aa8c4d9ea82e9bb2
6 files changed +80 -29
modified httpd-client/index.ts
@@ -30,10 +30,10 @@ import type {
  Revision,
  Verdict,
} from "./lib/project/patch.js";
-
import type { RequestOptions, Method } from "./lib/fetcher.js";
+
import type { RequestOptions } from "./lib/fetcher.js";
import type { ZodSchema } from "zod";

-
import { array, literal, number, object, string, union } from "zod";
+
import { z, array, boolean, literal, number, object, string, union } from "zod";

import * as project from "./lib/project.js";
import * as session from "./lib/session.js";
@@ -69,28 +69,48 @@ export type {
  Verdict,
};

-
export interface Node {
-
  id: string;
-
}
+
export type Node = z.infer<typeof nodeSchema>;

const nodeSchema = object({
  id: string(),
-
}) satisfies ZodSchema<Node>;
-

-
export interface NodeInfo {
-
  message: string;
-
  service: string;
-
  version: string;
-
  node: Node;
-
  path: string;
-
  links: { href: string; rel: string; type: Method }[];
-
}
+
  config: object({
+
    alias: string(),
+
    peers: union([
+
      object({ type: literal("static") }),
+
      object({ type: literal("dynamic"), target: number() }),
+
    ]),
+
    connect: array(string()),
+
    externalAddresses: array(string()),
+
    network: union([literal("main"), literal("test")]),
+
    relay: boolean(),
+
    limits: object({
+
      routingMaxSize: number(),
+
      routingMaxAge: number(),
+
      fetchConcurrency: number(),
+
      rate: object({
+
        inbound: object({
+
          fillRate: number(),
+
          capacity: number(),
+
        }),
+
        outbound: object({
+
          fillRate: number(),
+
          capacity: number(),
+
        }),
+
      }),
+
    }),
+
    policy: union([literal("track"), literal("block")]),
+
    scope: union([literal("trusted"), literal("all")]),
+
  }).nullable(),
+
  state: union([literal("running"), literal("stopped")]),
+
});
+

+
export type NodeInfo = z.infer<typeof nodeInfoSchema>;

const nodeInfoSchema = object({
  message: string(),
  service: string(),
  version: string(),
-
  node: nodeSchema,
+
  node: nodeSchema.pick({ id: true }),
  path: string(),
  links: array(
    object({
@@ -104,7 +124,7 @@ const nodeInfoSchema = object({
      ]),
    }),
  ),
-
}) satisfies ZodSchema<NodeInfo>;
+
});

export interface NodeStats {
  projects: { count: number };
modified httpd-client/lib/fetcher.ts
@@ -74,7 +74,7 @@ export interface FetchParams {
  headers?: Record<string, string>;
}

-
export type Method = "DELETE" | "GET" | "PATCH" | "POST" | "PUT";
+
type Method = "DELETE" | "GET" | "PATCH" | "POST" | "PUT";

export class Fetcher {
  #baseUrl: BaseUrl;
modified src/views/nodes/View.svelte
@@ -2,7 +2,6 @@
  import type { BaseUrl } from "@httpd-client";
  import type { ProjectActivity } from "@app/views/nodes/router";

-
  import { config } from "@app/lib/config";
  import { isLocal, truncateId } from "@app/lib/utils";
  import { loadProjects } from "@app/views/nodes/router";

@@ -15,6 +14,7 @@

  export let baseUrl: BaseUrl;
  export let nid: string;
+
  export let externalAddresses: string[];
  export let projectCount: number;
  export let projectPageIndex: number;
  export let projects: ProjectActivity[] = [];
@@ -107,11 +107,24 @@
        {hostname}
      </div>
      <div class="info">
-
        <div class="address">
-
          {truncateId(nid)}@{baseUrl.hostname}
-
          <Clipboard
-
            small
-
            text={`${nid}@${baseUrl.hostname}:${config.nodes.defaultNodePort}`} />
+
        <div>
+
          {#each externalAddresses as address}
+
            <!-- If there are externalAddresses this is probably a remote node -->
+
            <!-- in that case, we show all the defined externalAddresses as a listing -->
+
            <div class="address">
+
              {truncateId(nid)}@{address}
+
              <Clipboard small text={`${nid}@${address}`} />
+
            </div>
+
          {:else}
+
            <!-- else this is probably a local node -->
+
            <!-- So we show only the nid -->
+
            <div class="address layout-desktop">
+
              {nid}<Clipboard small text={nid} />
+
            </div>
+
            <div class="address layout-mobile">
+
              {truncateId(nid)}<Clipboard small text={nid} />
+
            </div>
+
          {/each}
        </div>
        <div class="version">
          v{version}
modified src/views/nodes/router.ts
@@ -31,6 +31,7 @@ export interface NodesLoadedRoute {
    baseUrl: BaseUrl;
    projectPageIndex: number;
    version: string;
+
    externalAddresses: string[];
    nid: string;
    projects: ProjectActivity[];
    projectCount: number;
@@ -96,8 +97,9 @@ export async function loadNodeRoute(
  const api = new HttpdClient(params.baseUrl);
  try {
    const projectPageIndex = 0;
-
    const [nodeInfo, { projects, total }] = await Promise.all([
+
    const [nodeInfo, node, { projects, total }] = await Promise.all([
      api.getNodeInfo(),
+
      api.getNode(),
      loadProjects(projectPageIndex, params.baseUrl),
    ]);
    return {
@@ -106,6 +108,7 @@ export async function loadNodeRoute(
        projectPageIndex: projectPageIndex + 1,
        baseUrl: params.baseUrl,
        nid: nodeInfo.node.id,
+
        externalAddresses: node.config?.externalAddresses ?? [],
        version: nodeInfo.version,
        projects: projects,
        projectCount: total,
modified tests/e2e/clipboard.spec.ts
@@ -63,7 +63,7 @@ test("copy to clipboard", async ({ page, browserName, context }) => {
  // Node address.
  {
    await page.locator(".clipboard").first().click();
-
    await expectClipboard(`${nodeRemote}@127.0.0.1:8776`, page);
+
    await expectClipboard(`${nodeRemote}`, page);
  }

  // Clear the system clipboard contents so developers don't wonder why there's
modified tests/e2e/node.spec.ts
@@ -6,14 +6,29 @@ import {
  test,
} from "@tests/support/fixtures.js";

-
test("node metadata", async ({ page }) => {
-
  await page.goto("/nodes/radicle.local");
+
test("node metadata", async ({ page, peerManager }) => {
+
  const peer = await peerManager.startPeer({
+
    name: "node-metadata-peer",
+
  });
+
  await peer.startHttpd();
+
  await peer.startNode({
+
    policy: "track",
+
    scope: "all",
+
    alias: "palm",
+
    externalAddresses: ["seed.radicle.test:8123"],
+
  });
+

+
  await page.goto(peer.uiUrl());

  await expect(
    page.locator(".header").getByText("radicle.local"),
  ).toBeVisible();
-
  await expect(page.getByText(shortNodeRemote)).toBeVisible();
+
  await expect(
+
    page.getByText(`${shortNodeRemote}@seed.radicle.test:8123`),
+
  ).toBeVisible();
  await expect(page.getByText("0.1.0-")).toBeVisible();
+
  await peer.stopHttpd();
+
  await peer.stopNode();
});

test("node projects", async ({ page }) => {