diff --git a/src/Profile.svelte b/src/Profile.svelte index f73f85d5f..5ad82ee84 100644 --- a/src/Profile.svelte +++ b/src/Profile.svelte @@ -377,7 +377,9 @@ {/await} {/if} - + {#if profile.seed?.valid} + + {/if} setNameForm = null} /> diff --git a/src/api.ts b/src/api.ts index 1f6726826..c5df28ffe 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,21 +1,20 @@ -import type { Config } from '@app/config'; +export interface Host { + host: string; + port: number; +} export async function get( path: string, params: Record, - config: Config + api: Host ): Promise { - if (! config.seed.api.host) { - throw new Error("Seed host unavailable"); - } - const query: Record = {}; for (const [key, val] of Object.entries(params)) { query[key] = val.toString(); } - const base = config.seed.api.host; - const port = config.seed.api.port; + const base = api.host; + const port = api.port; const search = new URLSearchParams(query).toString(); // Allow using the functionality with local runned http-api const isLocalhost = /^0.0.0.0$/.test(base); diff --git a/src/base/orgs/View/Projects.svelte b/src/base/orgs/View/Projects.svelte index 8890667ac..0e072bd63 100644 --- a/src/base/orgs/View/Projects.svelte +++ b/src/base/orgs/View/Projects.svelte @@ -8,11 +8,12 @@ import Widget from '@app/base/projects/Widget.svelte'; import type { Profile } from "@app/profile"; import type { ProjectInfo, Anchor, PendingAnchor } from "@app/project"; - import { Seed } from "@app/base/seeds/Seed"; + import type { Seed } from "@app/base/seeds/Seed"; import AnchorActions from "@app/base/profiles/AnchorActions.svelte"; export let profile: Profile; export let config: Config; + export let seed: Seed; export let account: string | null; let anchors: Record = {}; @@ -55,31 +56,29 @@
- {#if config.seed.api.host} - {#await Seed.getProjects(config)} - - {:then projects} - {#each projects as project} - {@const anchor = anchors[project.urn]} - {@const pendingAnchor = pendingAnchors[project.urn]} -
- onClick(project)}> - - {#if profile.org?.safe && account && anchor} - {#if pendingAnchor} - loadAnchors()} /> - {/if} + {#await seed.getProjects()} + + {:then projects} + {#each projects as project} + {@const anchor = anchors[project.urn]} + {@const pendingAnchor = pendingAnchors[project.urn]} +
+ onClick(project)}> + + {#if profile.org?.safe && account && anchor} + {#if pendingAnchor} + loadAnchors()} /> {/if} - - -
- {/each} - {:catch err} - - Error: failed to load projects: {err.message}. - - {/await} - {/if} + {/if} +
+
+
+ {/each} + {:catch err} + + Error: failed to load projects: {err.message}. + + {/await}
diff --git a/src/base/projects/Browser.svelte b/src/base/projects/Browser.svelte index 8f2364a6c..13d3cc988 100644 --- a/src/base/projects/Browser.svelte +++ b/src/base/projects/Browser.svelte @@ -23,7 +23,7 @@ export let revision: string; export let path: string; - let { urn, addressOrName, seed, peer, project, config, branches } = source; + let { urn, addressOrName, seed, peer, project, branches } = source; // This is reactive to respond to path changes that don't originate from this // component, eg. when using the browser's "back" button. @@ -45,8 +45,8 @@ const isMarkdownPath = utils.isMarkdownPath(path); const promise = path === "/" - ? proj.getReadme(urn, commit, config) - : proj.getBlob(urn, commit, path, { highlight: !isMarkdownPath }, config); + ? proj.getReadme(urn, commit, source.seed.api) + : proj.getBlob(urn, commit, path, { highlight: !isMarkdownPath }, source.seed.api); state = { status: Status.Loading, path }; state = { status: Status.Loaded, path, blob: await promise }; @@ -72,15 +72,13 @@ if (addressOrName) { navigate(proj.path({ peer, urn, addressOrName, revision, path })); - } else if (seed) { - navigate(proj.path({ peer, urn, seed, revision, path })); } else { - navigate(proj.path({ peer, urn, revision, path })); + navigate(proj.path({ peer, urn, seed: seed.host, revision, path })); } }; const fetchTree = async (path: string) => { - return proj.getTree(urn, commit, path, config); + return proj.getTree(urn, commit, path, seed.api); }; const toggleMobileFileTree = () => { diff --git a/src/base/projects/Commit.svelte b/src/base/projects/Commit.svelte index 808866c26..4f31660b1 100644 --- a/src/base/projects/Commit.svelte +++ b/src/base/projects/Commit.svelte @@ -18,10 +18,8 @@ if (addressOrName) { navigate(proj.path({ content, peer, urn, addressOrName, revision, path })); - } else if (seed) { - navigate(proj.path({ content, peer, urn, seed, revision, path })); } else { - navigate(proj.path({ content, peer, urn, revision, path })); + navigate(proj.path({ content, peer, urn, seed: seed.host, revision, path })); } }; diff --git a/src/base/projects/Header.svelte b/src/base/projects/Header.svelte index 5848c6888..729c29c1b 100644 --- a/src/base/projects/Header.svelte +++ b/src/base/projects/Header.svelte @@ -18,7 +18,7 @@ // If peerSelector should be showed. export let peerSelector: boolean; - let { urn, peer, config, project, branches, peers, anchors } = source; + let { urn, peer, project, branches, peers, seed, anchors } = source; let dropdownState: { [key: string]: boolean } = { clone: false, seed: false, branch: false, peer: false }; function toggleDropdown(input: string) { @@ -163,7 +163,7 @@ updateRevision(event.detail)} /> - {#if config.seed.git.host} + {#if seed.git.host}
toggleDropdown("clone")}> Clone @@ -175,7 +175,7 @@ {/if} - {#if config.seed.api.host} + {#if seed.api.host}
navigate(`/seeds/${config.seed.api.host}`)} + on:click={() => navigate(`/seeds/${seed.api.host}`)} title="Project data is fetched from this seed" > - {config.seed.api.host} + {seed.api.host}
{/if}
diff --git a/src/base/projects/History.svelte b/src/base/projects/History.svelte index 9a211321f..3ac98f0d3 100644 --- a/src/base/projects/History.svelte +++ b/src/base/projects/History.svelte @@ -12,7 +12,7 @@ export let revision: string; export let path: string; - let { urn, seed, addressOrName, peer, config, project, branches } = source; + let { urn, seed, addressOrName, peer, project, branches } = source; // Bind content to commit history to trigger updates in parent components. $: [revision_,] = splitPrefixFromPath(locator, branches, project.head); @@ -21,19 +21,17 @@ const navigateHistory = (revision: string, content?: ProjectContent) => { // Replaces path with current path if none passed. - if (path === undefined) path = "/"; + if (! path) path = "/"; if (addressOrName) { navigate(proj.path({ content, peer, urn, addressOrName, revision, path })); - } else if (seed) { - navigate(proj.path({ content, peer, urn, seed, revision, path })); } else { - navigate(proj.path({ content, peer, urn, revision, path })); + navigate(proj.path({ content, peer, urn, seed: seed.host, revision, path })); } }; async function fetchCommits(revision: string): Promise { - const commitsQuery = await getCommits(urn, getOid(project.head, revision, branches), config); + const commitsQuery = await getCommits(urn, getOid(project.head, revision, branches), seed.api); return groupCommitHistory(commitsQuery); } diff --git a/src/base/projects/Routes.svelte b/src/base/projects/Routes.svelte index aa4b8bdb8..69c27f3bf 100644 --- a/src/base/projects/Routes.svelte +++ b/src/base/projects/Routes.svelte @@ -11,11 +11,11 @@ - + - + diff --git a/src/base/projects/View.svelte b/src/base/projects/View.svelte index e709ac7c2..2a2909b42 100644 --- a/src/base/projects/View.svelte +++ b/src/base/projects/View.svelte @@ -16,7 +16,7 @@ export let id: string; // Project name or URN. export let addressOrName = ""; - export let seed = ""; + export let seedHost = ""; export let peer = ""; export let config: Config; @@ -29,16 +29,24 @@ let getProject = new Promise<{ profile?: Profile | null; seed?: Seed } | null>((resolve, reject) => { if (addressOrName) { Profile.get(addressOrName, ProfileType.Project, config).then(p => resolve({ profile: p })).catch(err => reject(err.message)); - } else if (seed) { - Seed.get(config.withSeed({ host: seed })).then(s => resolve({ seed: s })).catch(err => reject(err.message)); + } else if (seedHost) { + Seed.lookup(seedHost, config).then(s => resolve({ seed: s })).catch(err => reject(err.message)); } else { resolve(null); } }).then(async (result) => { - const profile = result?.profile; - const seedInstance = profile?.seed ?? result?.seed; - const cfg = seedInstance && seedInstance.valid ? config.withSeed(seedInstance) : config; - const info = await proj.getInfo(id, cfg); + if (! result) { + throw new Error("Couldn't load project"); + } + + const profile = result.profile; + const seed = result.seed || profile?.seed; + + if (! seed?.valid) { + throw new Error("Couldn't load project: invalid seed"); + } + + const info = await proj.getInfo(id, seed.api); const urn = isRadicleId(id) ? id : info.urn; const anchors = profile ? await profile.confirmedProjectAnchors(urn, config) : []; @@ -51,12 +59,12 @@ if (info.delegates) { // Check for selected peer to override available branches. if (peer) { - const branchesByPeer = await proj.getBranchesByPeer(urn, peer || info.delegates[0], cfg); + const branchesByPeer = await proj.getBranchesByPeer(urn, peer || info.delegates[0], seed.api); branches = [...Object.entries(branchesByPeer.heads)]; } - peers = await proj.getPeers(urn, cfg); + peers = await proj.getPeers(urn, seed.api); } - return { urn, addressOrName, seed, peer, project: info, branches, peers, config: cfg, profile, anchors }; + return { urn, addressOrName, seed, peer, project: info, branches, peers, config, profile, anchors }; }); $: if (projectInfo) { @@ -72,7 +80,16 @@ } function updateRouteParams({ detail: newParams }: { detail: { urn: string; path: string; revision: string; peer: string; content: proj.ProjectContent } }) { - let newLocation = proj.path({ addressOrName, seed, urn: newParams.urn, content: newParams.content, peer: newParams.peer, revision: newParams.revision, path: newParams.path }); + const newLocation = proj.path({ + addressOrName, + seed: seedHost, + urn: newParams.urn, + content: newParams.content, + peer: newParams.peer, + revision: newParams.revision, + path: newParams.path + }); + if (newLocation !== window.location.pathname) { navigate(newLocation); } @@ -162,7 +179,7 @@ / {/if} - {result.project.name} + {result.project.name} {#if peer} / {formatSeedId(peer)} {/if} @@ -171,10 +188,10 @@
{result.project.description}
- {#await proj.getTree(result.urn, getOid(result.project.head, revision, result.branches), "/", config) then tree} + {#await proj.getTree(result.urn, getOid(result.project.head, revision, result.branches), "/", result.seed.api) then tree}
- + diff --git a/src/base/seeds/Seed.ts b/src/base/seeds/Seed.ts index be89d6241..276bee4d5 100644 --- a/src/base/seeds/Seed.ts +++ b/src/base/seeds/Seed.ts @@ -19,52 +19,79 @@ export class InvalidSeed { export class Seed { valid: true = true; - host: string; - id: string; + api: { host: string; port: number }; + git: { host: string; port: number }; + link: { host: string; id: string; port: number }; - api?: string; - git?: string; version?: string; - constructor(host: string, id: string, git?: string | null, api?: string | null) { - assert(isDomain(host), "invalid seed host"); - assert(/^[a-z0-9]+$/.test(id), "invalid seed id"); + constructor(seed: { + host: string; + id: string; + git?: string | null; + api?: string | null; + version?: string | null; + }, cfg: Config) { + assert(isDomain(seed.host), "invalid seed host"); + assert(/^[a-z0-9]+$/.test(seed.id), "invalid seed id"); - this.host = host; - this.id = id; + seed.api && assert(isDomain(seed.api), "invalid seed api host"); + seed.git && assert(isDomain(seed.git), "invalid seed git host"); - if (api && isDomain(api)) { - this.api = api; - } - if (git && isDomain(git)) { - this.git = git; + // The `git` and `api` keys being more specific take + // precedence over the `host`, if available. + const api = seed.api ?? seed.host; + const git = seed.git ?? seed.host; + + this.api = { host: api, port: cfg.seed.api.port }; + this.git = { host: git, port: cfg.seed.git.port }; + this.link = { host: seed.host, id: seed.id, port: cfg.seed.link.port }; + + if (seed.version) { + this.version = seed.version; } } - static async getPeer(config: Config): Promise<{ id: string }> { - return api.get("/peer", {}, config); + get id(): string { + return this.link.id; } - static async getProject(urn: string, config: Config): Promise { - return proj.getInfo(urn, config); + get host(): string { + return this.api.host; } - static async getProjects(config: Config): Promise { - const result = await proj.getProjects(config); + async getPeer(): Promise<{ id: string }> { + return Seed.getPeer(this.api); + } + + async getProject(urn: string): Promise { + return proj.getInfo(urn, this.api); + } + + async getProjects(): Promise { + const result = await proj.getProjects(this.api); return result.map((project: any) => ({ ...project, id: project.urn })); } - static async get(config: Config): Promise { - assert(config.seed.api.host); + static async getPeer({ host, port }: api.Host): Promise<{ id: string }> { + return api.get("/peer", {}, { host, port }); + } + static async getInfo({ host, port }: api.Host): Promise<{ version: string }> { + return api.get("/", {}, { host, port }); + } + + static async lookup(hostname: string, cfg: Config): Promise { + const host = { host: hostname, port: cfg.seed.api.port }; const [info, peer] = await Promise.all([ - api.get("/", {}, config), - Seed.getPeer(config), + Seed.getInfo(host), + Seed.getPeer(host), ]); - const seed = new Seed(config.seed.api.host, peer.id); - seed.version = info.version; - - return seed; + return new Seed({ + host: hostname, + id: peer.id, + version: info.version, + }, cfg); } } diff --git a/src/base/seeds/View.svelte b/src/base/seeds/View.svelte index 19a801d68..0b9bba451 100644 --- a/src/base/seeds/View.svelte +++ b/src/base/seeds/View.svelte @@ -9,14 +9,12 @@ import * as proj from "@app/project"; export let config: Config; - export let seedAddress: string; - - config = config.withSeed({ host: seedAddress }); + export let host: string; const onProjectClick = (project: proj.ProjectInfo) => { navigate(proj.path({ urn: project.urn, - seed: seedAddress, + seed: host, })); }; @@ -80,20 +78,20 @@ - {seedAddress} + {host} -{#await Seed.get(config)} +{#await Seed.lookup(host, config)}
-{:then info} +{:then seed}
- 🌱 {seedAddress} + 🌱 {host}
@@ -102,27 +100,27 @@
Address
- {#if info.version === "0.2.0" && info.host} - + {#if seed.version === "0.2.0" && seed.host} + {:else}
N/A
{/if}
Seed ID
-
{info.id}
+
{seed.id}
API Port
-
{config.seed.api.port}
+
{seed.api.port}
Version
-
{info.version}
+
{seed.version}
- {#await Seed.getProjects(config) then projects} + {#await seed.getProjects() then projects}
{#each projects as project}
@@ -133,5 +131,5 @@ {/await}
{:catch} - + {/await} diff --git a/src/config.json b/src/config.json index fdc7b24f3..87ffeab61 100644 --- a/src/config.json +++ b/src/config.json @@ -73,7 +73,6 @@ }, "radicle": { "seed": { - "host": "0.0.0.0", "api": { "port": 8777 }, "link": { "port": 8776 }, "git": { "port": 80 } diff --git a/src/config.ts b/src/config.ts index d4905d832..947c38024 100644 --- a/src/config.ts +++ b/src/config.ts @@ -56,9 +56,9 @@ export class Config { }; abi: { [contract: string]: string[] }; seed: { - api: { host?: string; port: number }; - git: { host?: string; port: number }; - link: { host?: string; id?: string; port: number }; + api: { port: number }; + git: { port: number }; + link: { port: number }; }; ceramic: { client: Core; @@ -136,24 +136,6 @@ export class Config { this.signer = signer; } - // Return the config with an overwritten seed URL. - withSeed(seed: { host: string; id?: string; api?: string; git?: string }): Config { - const cfg = {} as Config; - Object.assign(cfg, this); - - // The `git` and `api` keys being more specific take - // precedence over the `host`, if available. - const api = seed.api ?? seed.host; - const git = seed.git ?? seed.host; - - cfg.seed.api.host = api; - cfg.seed.git.host = git; - cfg.seed.link.host = seed.host; - cfg.seed.link.id = seed.id; - - return cfg; - } - getWalletConnectSigner(): WalletConnectSigner { if (this.walletConnect.client.connected) { this.setSigner(this.walletConnect.signer); diff --git a/src/profile.ts b/src/profile.ts index 12460c8e1..ad3fb36af 100644 --- a/src/profile.ts +++ b/src/profile.ts @@ -115,15 +115,6 @@ export class Profile { return this.name ?? this.address; } - // Return the profile-specific config. This sets various URLs in the config, - // based on profile data. - config(config: Config): Config { - if (this.seed && this.seed.valid) { - return config.withSeed(this.seed); - } - return config; - } - // Returns the corresponding registration form to edit a user profile. // We are not interested in a non-existant registry link, since we check before hand if the name exists. registry(config: Config): string { diff --git a/src/project.ts b/src/project.ts index edd983d9b..6ce58df2e 100644 --- a/src/project.ts +++ b/src/project.ts @@ -1,8 +1,8 @@ -import type { Config } from '@app/config'; import * as api from '@app/api'; import type { Commit, CommitHeader, CommitsHistory } from '@app/commit'; import { isOid } from '@app/utils'; import type { Profile } from '@app/profile'; +import type { Seed } from '@app/base/seeds/Seed'; export type Urn = string; export type Peer = string; @@ -31,11 +31,10 @@ export interface Source { urn: string; addressOrName: string; peer: string; - config: Config; project: ProjectInfo; peers: Peer[]; anchors: string[]; - seed: string; + seed: Seed; branches: [string, string][]; profile?: Profile | null; } @@ -97,8 +96,8 @@ export interface Branches { heads: Branch; } -export async function getInfo(nameOrUrn: string, config: Config): Promise { - const info = await api.get(`projects/${nameOrUrn}`, {}, config); +export async function getInfo(nameOrUrn: string, host: api.Host): Promise { + const info = await api.get(`projects/${nameOrUrn}`, {}, host); return { ...info, @@ -106,34 +105,34 @@ export async function getInfo(nameOrUrn: string, config: Config): Promise { - return api.get(`projects/${urn}/commits?from=${commit}`, {}, config); +export async function getCommits(urn: string, commit: string, host: api.Host): Promise { + return api.get(`projects/${urn}/commits?from=${commit}`, {}, host); } -export async function getCommit(urn: string, commit: string, config: Config): Promise { - return api.get(`projects/${urn}/commits/${commit}`, {}, config); +export async function getCommit(urn: string, commit: string, host: api.Host): Promise { + return api.get(`projects/${urn}/commits/${commit}`, {}, host); } -export async function getProjects(config: Config): Promise { - return api.get("projects", {}, config); +export async function getProjects(host: api.Host): Promise { + return api.get("projects", {}, host); } -export async function getBranchesByPeer(urn: string, peer: string, config: Config): Promise { - return api.get(`projects/${urn}/remotes/${peer}`, {}, config); +export async function getBranchesByPeer(urn: string, peer: string, host: api.Host): Promise { + return api.get(`projects/${urn}/remotes/${peer}`, {}, host); } -export async function getPeers(urn: string, config: Config): Promise { - return api.get(`projects/${urn}/remotes`, {}, config); +export async function getPeers(urn: string, host: api.Host): Promise { + return api.get(`projects/${urn}/remotes`, {}, host); } export async function getTree( urn: string, commit: string, path: string, - config: Config + host: api.Host ): Promise { if (path === "/") path = ""; - return api.get(`projects/${urn}/tree/${commit}/${path}`, {}, config); + return api.get(`projects/${urn}/tree/${commit}/${path}`, {}, host); } export async function getBlob( @@ -141,17 +140,17 @@ export async function getBlob( commit: string, path: string, options: { highlight: boolean }, - config: Config + host: api.Host ): Promise { - return api.get(`projects/${urn}/blob/${commit}/${path}`, options, config); + return api.get(`projects/${urn}/blob/${commit}/${path}`, options, host); } export async function getReadme( urn: string, commit: string, - config: Config + host: api.Host ): Promise { - return api.get(`projects/${urn}/readme/${commit}`, {}, config); + return api.get(`projects/${urn}/readme/${commit}`, {}, host); } export function path(