diff --git a/httpd-client/index.ts b/httpd-client/index.ts index 47ba2c967..b7bb41e1e 100644 --- a/httpd-client/index.ts +++ b/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; const nodeSchema = object({ id: string(), -}) satisfies ZodSchema; + 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 interface NodeInfo { - message: string; - service: string; - version: string; - node: Node; - path: string; - links: { href: string; rel: string; type: Method }[]; -} +export type NodeInfo = z.infer; 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; +}); export interface NodeStats { projects: { count: number }; diff --git a/httpd-client/lib/fetcher.ts b/httpd-client/lib/fetcher.ts index 50c6e0d4e..a4a3d6e91 100644 --- a/httpd-client/lib/fetcher.ts +++ b/httpd-client/lib/fetcher.ts @@ -74,7 +74,7 @@ export interface FetchParams { headers?: Record; } -export type Method = "DELETE" | "GET" | "PATCH" | "POST" | "PUT"; +type Method = "DELETE" | "GET" | "PATCH" | "POST" | "PUT"; export class Fetcher { #baseUrl: BaseUrl; diff --git a/src/views/nodes/View.svelte b/src/views/nodes/View.svelte index 54f46c8ec..33dfbbd80 100644 --- a/src/views/nodes/View.svelte +++ b/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}
-
- {truncateId(nid)}@{baseUrl.hostname} - +
+ {#each externalAddresses as address} + + +
+ {truncateId(nid)}@{address} + +
+ {:else} + + +
+ {nid} +
+
+ {truncateId(nid)} +
+ {/each}
v{version} diff --git a/src/views/nodes/router.ts b/src/views/nodes/router.ts index 544359d17..cdcbdb584 100644 --- a/src/views/nodes/router.ts +++ b/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, diff --git a/tests/e2e/clipboard.spec.ts b/tests/e2e/clipboard.spec.ts index 42482bd5b..3d56fc613 100644 --- a/tests/e2e/clipboard.spec.ts +++ b/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 diff --git a/tests/e2e/node.spec.ts b/tests/e2e/node.spec.ts index 8fbc89cca..68f8e79a2 100644 --- a/tests/e2e/node.spec.ts +++ b/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 }) => {