diff --git a/config/default.json b/config/default.json index ee175b3d1..6f92bbbec 100644 --- a/config/default.json +++ b/config/default.json @@ -1,7 +1,7 @@ { "nodes": { "fallbackPublicExplorer": "https://app.radicle.xyz/nodes/$host/$rid$path", - "apiVersion": "1.0.0", + "apiVersion": "1.1.0", "defaultHttpdPort": 443, "defaultLocalHttpdPort": 8080, "defaultHttpdHostname": "seed.radicle.garden", diff --git a/http-client/index.ts b/http-client/index.ts index 7cf0e39aa..0b58c6827 100644 --- a/http-client/index.ts +++ b/http-client/index.ts @@ -12,8 +12,7 @@ import type { SuccessResponse, CodeLocation, Range, - Policy, - Scope, + DefaultSeedingPolicy, } from "./lib/shared.js"; import type { Comment, Embed, Reaction } from "./lib/project/comment.js"; import type { @@ -48,7 +47,11 @@ import * as project from "./lib/project.js"; import * as profile from "./lib/profile.js"; import * as session from "./lib/session.js"; import { Fetcher } from "./lib/fetcher.js"; -import { nodeConfigSchema, successResponseSchema } from "./lib/shared.js"; +import { + nodeConfigSchema, + scopeSchema, + successResponseSchema, +} from "./lib/shared.js"; export type { BaseUrl, @@ -60,6 +63,7 @@ export type { Commit, CommitBlob, CommitHeader, + DefaultSeedingPolicy, Diff, DiffBlob, DiffContent, @@ -74,7 +78,6 @@ export type { Patch, PatchState, PatchUpdateAction, - Policy, Project, ProjectListQuery, Range, @@ -82,7 +85,6 @@ export type { Remote, Review, Revision, - Scope, TreeStats, Tree, Verdict, @@ -120,15 +122,18 @@ const nodeInfoSchema = object({ ), }); -export type NodeTracking = z.infer; +export type NodePolicies = z.infer; -const nodeTrackingSchema = array( - object({ - id: string(), - scope: string(), - policy: string(), - }), -); +const nodePoliciesSchema = object({ + rid: string(), + policy: union([ + object({ policy: literal("block") }), + object({ + policy: literal("allow"), + scope: scopeSchema, + }), + ]), +}); export interface NodeStats { repos: { total: number }; @@ -192,14 +197,28 @@ export class HttpdClient { ); } - public async getTracking(options?: RequestOptions): Promise { + public async getPolicies(options?: RequestOptions): Promise { return this.#fetcher.fetchOk( { method: "GET", path: "node/policies/repos", options, }, - nodeTrackingSchema, + array(nodePoliciesSchema), + ); + } + + public async getPoliciesById( + id: string, + options?: RequestOptions, + ): Promise { + return this.#fetcher.fetchOk( + { + method: "GET", + path: `node/policies/repos/${id}`, + options, + }, + array(nodePoliciesSchema.shape.policy), ); } diff --git a/http-client/lib/shared.ts b/http-client/lib/shared.ts index d081a0416..b0425a211 100644 --- a/http-client/lib/shared.ts +++ b/http-client/lib/shared.ts @@ -10,8 +10,17 @@ export const successResponseSchema = object({ success: literal(true), }) satisfies ZodSchema; -const policySchema = union([literal("allow"), literal("block")]); -const scopeSchema = union([literal("followed"), literal("all")]); +export const scopeSchema = union([literal("followed"), literal("all")]); + +const defaultSeedingPolicySchema = union([ + object({ + default: literal("block"), + }), + object({ + default: literal("allow"), + scope: scopeSchema, + }), +]); export const nodeConfigSchema = object({ alias: string(), @@ -61,14 +70,10 @@ export const nodeConfigSchema = object({ }), }), workers: number(), - seedingPolicy: object({ - default: policySchema, - scope: scopeSchema.optional(), - }), + seedingPolicy: defaultSeedingPolicySchema, }); -export type Policy = z.infer; -export type Scope = z.infer; +export type DefaultSeedingPolicy = z.infer; export const rangeSchema = union([ object({ diff --git a/src/App/Header/NodeInfo.svelte b/src/App/Header/NodeInfo.svelte index 70f12320a..5cd7b3d06 100644 --- a/src/App/Header/NodeInfo.svelte +++ b/src/App/Header/NodeInfo.svelte @@ -46,21 +46,23 @@ Your node is running and syncing with the network. - {#if node.scope && node.policy} + {#if node.seedingPolicy}
Seeding Policy: - {capitalize(node.policy)} - -
-
- Scope: - - {capitalize(node.scope)} + {capitalize(node.seedingPolicy.default)}
+ {#if node.seedingPolicy.default === "allow"} +
+ Scope: + + {capitalize(node.seedingPolicy.scope)} + +
+ {/if} - +
{/if}
diff --git a/src/components/ScopePolicyExplainer.svelte b/src/components/ScopePolicyExplainer.svelte index 1165cfea2..6b399660c 100644 --- a/src/components/ScopePolicyExplainer.svelte +++ b/src/components/ScopePolicyExplainer.svelte @@ -1,10 +1,9 @@ -
- Scope: - {capitalize(scope)} -
-
- {#if scope === "all"} - All changes in seeded repositories, made by any peer, will be synced. - {:else if scope === "followed"} - Only changes made by explicitly followed peers will be synced. - {/if} -
+{#if seedingPolicy.default === "allow"} +
+ Scope: + {capitalize(seedingPolicy.scope)} +
+
+ {#if seedingPolicy.scope === "all"} + All changes in seeded repositories, made by any peer, will be synced. + {:else if seedingPolicy.scope === "followed"} + Only changes made by explicitly followed peers will be synced. + {/if} +
+{/if}
Policy: - {capitalize(policy)} + {capitalize(seedingPolicy.default)}
- {#if policy === "allow"} + {#if seedingPolicy.default === "allow"} All discovered repositories will get seeded. - {:else if policy === "block"} + {:else if seedingPolicy.default === "block"} Only repositories marked as such will get seeded. {/if}
diff --git a/src/lib/httpd.ts b/src/lib/httpd.ts index 85ef63f03..42ea6f691 100644 --- a/src/lib/httpd.ts +++ b/src/lib/httpd.ts @@ -1,4 +1,4 @@ -import type { Node, Policy, Scope } from "@http-client"; +import type { DefaultSeedingPolicy, Node } from "@http-client"; import { get, writable } from "svelte/store"; import { withTimeout, Mutex, E_CANCELED, E_TIMEOUT } from "async-mutex"; @@ -17,8 +17,7 @@ export interface Session { export interface HttpdNodeState { id: Node["id"]; state: Node["state"]; - policy: Policy | undefined; - scope: Scope | undefined; + seedingPolicy: DefaultSeedingPolicy | undefined; } export type HttpdState = @@ -86,8 +85,7 @@ export async function authenticate(params: { node: { id, state, - policy: config?.seedingPolicy.default, - scope: config?.seedingPolicy.scope, + seedingPolicy: config?.seedingPolicy, }, }); return true; @@ -116,8 +114,7 @@ export async function disconnect() { node: { id, state, - policy: config?.seedingPolicy.default, - scope: config?.seedingPolicy.scope, + seedingPolicy: config?.seedingPolicy, }, }); } catch (error) { @@ -155,8 +152,7 @@ async function checkState() { const node = { id, state, - policy: config?.seedingPolicy.default, - scope: config?.seedingPolicy.scope, + seedingPolicy: config?.seedingPolicy, }; if (httpdState && httpdState.state === "authenticated") { diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 4555877a4..b189a0ca9 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,4 +1,4 @@ -import type { BaseUrl } from "@http-client"; +import type { BaseUrl, DefaultSeedingPolicy } from "@http-client"; import md5 from "md5"; import bs58 from "bs58"; @@ -13,6 +13,14 @@ export function formatLocationHash(hash: string | null): number | null { return null; } +export function formatShortSeedingPolicy( + policy: DefaultSeedingPolicy | undefined, +) { + return policy?.default === "allow" && policy?.scope === "all" + ? "permissive" + : "restrictive"; +} + export function parseNodeId( nid: string, ): { prefix: string; pubkey: string } | undefined { diff --git a/src/views/nodes/ScopePolicyPopover.svelte b/src/views/nodes/ScopePolicyPopover.svelte index 581b656d5..ef8518337 100644 --- a/src/views/nodes/ScopePolicyPopover.svelte +++ b/src/views/nodes/ScopePolicyPopover.svelte @@ -1,5 +1,5 @@ @@ -34,15 +33,17 @@
- Seeding Policy: - {capitalize(policy)} + Policy: + {capitalize(seedingPolicy.default)} - - - Scope: - {capitalize(scope)} - + {#if seedingPolicy.default === "allow"} + + + Scope: + {capitalize(seedingPolicy.scope)} + + {/if}
- +
diff --git a/src/views/nodes/View.svelte b/src/views/nodes/View.svelte index ba33470a0..6627a987b 100644 --- a/src/views/nodes/View.svelte +++ b/src/views/nodes/View.svelte @@ -1,11 +1,16 @@