Revise seed config

No longer use `Config` to store the seed config.

Signed-off-by: Alexis Sellier <self@cloudhead.io>
This commit is contained in:
Alexis Sellier
2022-02-09 13:27:12 +01:00
parent 3fcbffe307
commit e6f4441be7
17 changed files with 181 additions and 172 deletions
+3 -1
View File
@@ -377,7 +377,9 @@
</Message>
{/await}
{/if}
<Projects {profile} {account} config={profile.config(config)} />
{#if profile.seed?.valid}
<Projects {profile} seed={profile.seed} {account} {config} />
{/if}
</main>
<svelte:component this={setNameForm} entity={profile.org ?? new User(profile.address)} {config} on:close={() => setNameForm = null} />
+7 -8
View File
@@ -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<string, any>,
config: Config
api: Host
): Promise<any> {
if (! config.seed.api.host) {
throw new Error("Seed host unavailable");
}
const query: Record<string, string> = {};
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);
+26 -27
View File
@@ -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<string, Anchor> = {};
@@ -55,31 +56,29 @@
</style>
<div class="projects">
{#if config.seed.api.host}
{#await Seed.getProjects(config)}
<Loading center />
{:then projects}
{#each projects as project}
{@const anchor = anchors[project.urn]}
{@const pendingAnchor = pendingAnchors[project.urn]}
<div class="project">
<Widget {project} {anchor} on:click={() => onClick(project)}>
<span class="actions" slot="actions">
{#if profile.org?.safe && account && anchor}
{#if pendingAnchor} <!-- Pending anchor -->
<AnchorActions
{account} {config} anchor={pendingAnchor} safe={profile.org.safe}
on:success={() => loadAnchors()} />
{/if}
{#await seed.getProjects()}
<Loading center />
{:then projects}
{#each projects as project}
{@const anchor = anchors[project.urn]}
{@const pendingAnchor = pendingAnchors[project.urn]}
<div class="project">
<Widget {project} {anchor} on:click={() => onClick(project)}>
<span class="actions" slot="actions">
{#if profile.org?.safe && account && anchor}
{#if pendingAnchor} <!-- Pending anchor -->
<AnchorActions
{account} {config} anchor={pendingAnchor} safe={profile.org.safe}
on:success={() => loadAnchors()} />
{/if}
</span>
</Widget>
</div>
{/each}
{:catch err}
<Message error>
<strong>Error: </strong> failed to load projects: {err.message}.
</Message>
{/await}
{/if}
{/if}
</span>
</Widget>
</div>
{/each}
{:catch err}
<Message error>
<strong>Error: </strong> failed to load projects: {err.message}.
</Message>
{/await}
</div>
+5 -7
View File
@@ -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 = () => {
+1 -3
View File
@@ -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 }));
}
};
+6 -6
View File
@@ -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 @@
<AnchorBadge {commit} {anchors}
head={project.head} on:click={(event) => updateRevision(event.detail)} />
</div>
{#if config.seed.git.host}
{#if seed.git.host}
<span>
<div class="clone" on:click={() => toggleDropdown("clone")}>
Clone
@@ -175,7 +175,7 @@
<input
readonly
name="clone-url"
value="https://{config.seed.git.host}/{utils.parseRadicleId(urn)}.git"
value="https://{seed.git.host}/{utils.parseRadicleId(urn)}.git"
/>
<label for="clone-url"
>Use Git to clone this repository from the URL above.</label
@@ -184,13 +184,13 @@
</span>
{/if}
<span>
{#if config.seed.api.host}
{#if seed.api.host}
<div
class="stat seed"
on:click={() => navigate(`/seeds/${config.seed.api.host}`)}
on:click={() => navigate(`/seeds/${seed.api.host}`)}
title="Project data is fetched from this seed"
>
<span>{config.seed.api.host}</span>
<span>{seed.api.host}</span>
</div>
{/if}
</span>
+4 -6
View File
@@ -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<GroupedCommitsHistory> {
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);
}
</script>
+2 -2
View File
@@ -11,11 +11,11 @@
<!-- With an Seed context -->
<Route path="/seeds/:seed/:id/*" let:params>
<View {config} seed={params.seed} id={params.id} />
<View {config} seedHost={params.seed} id={params.id} />
</Route>
<Route path="/seeds/:seed/:id/remotes/:peer/*" let:params>
<View {config} seed={params.seed} peer={params.peer} id={params.id} />
<View {config} seedHost={params.seed} peer={params.peer} id={params.id} />
</Route>
<!-- Explicit user and org context, will at some point be replaced by the generic route -->
+31 -14
View File
@@ -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 @@
</a>
<span class="divider">/</span>
{/if}
<Link to={proj.path({ urn: result.urn, addressOrName, seed })}>{result.project.name}</Link>
<Link to={proj.path({ urn: result.urn, addressOrName, seed: result.seed.host })}>{result.project.name}</Link>
{#if peer}
<span class="divider" title={peer}>/ {formatSeedId(peer)}</span>
{/if}
@@ -171,10 +188,10 @@
<div class="description">{result.project.description}</div>
</header>
{#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}
<Header {tree} {revision} {content} {path}
source={result}
peerSelector={!!seed}
peerSelector={!!seedHost}
on:routeParamsChange={updateRouteParams} />
<ProjectContentRoutes {tree}
source={result}
+4 -2
View File
@@ -98,7 +98,9 @@ export async function getRegistration(name: string, config: Config, resolver?: E
// If no seed provided profile.seed ends up being undefined
if (seedHost && seedId) {
try {
profile.seed = new Seed(seedHost, seedId, seedGit, seedApi);
profile.seed = new Seed({
host: seedHost, id: seedId, git: seedGit, api: seedApi
}, config);
} catch (e: any) {
console.debug(e, seedHost, seedId);
profile.seed = new InvalidSeed(seedHost, seedId);
@@ -149,7 +151,7 @@ export async function getSeed(name: string, config: Config, resolver?: EnsResolv
}
try {
return new Seed(host, id, git, api);
return new Seed({ host, id, git, api }, config);
} catch (e: any) {
console.debug(e, host, id);
return new InvalidSeed(id, host);
+1 -1
View File
@@ -7,5 +7,5 @@
</script>
<Route path="/seeds/:seed" let:params>
<View {config} seedAddress={params.seed}/>
<View {config} host={params.seed}/>
</Route>
+55 -28
View File
@@ -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<proj.ProjectInfo> {
return proj.getInfo(urn, config);
get host(): string {
return this.api.host;
}
static async getProjects(config: Config): Promise<proj.ProjectInfo[]> {
const result = await proj.getProjects(config);
async getPeer(): Promise<{ id: string }> {
return Seed.getPeer(this.api);
}
async getProject(urn: string): Promise<proj.ProjectInfo> {
return proj.getInfo(urn, this.api);
}
async getProjects(): Promise<proj.ProjectInfo[]> {
const result = await proj.getProjects(this.api);
return result.map((project: any) => ({ ...project, id: project.urn }));
}
static async get(config: Config): Promise<Seed> {
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<Seed> {
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);
}
}
+13 -15
View File
@@ -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,
}));
};
</script>
@@ -80,20 +78,20 @@
</style>
<svelte:head>
<title>{seedAddress}</title>
<title>{host}</title>
</svelte:head>
{#await Seed.get(config)}
{#await Seed.lookup(host, config)}
<main class="off-centered">
<Loading center />
</main>
{:then info}
{:then seed}
<main>
<header>
<div class="info">
<span class="title">
<span class="bold">
🌱 {seedAddress}
🌱 {host}
</span>
</span>
</div>
@@ -102,27 +100,27 @@
<div class="fields">
<!-- Seed Address -->
<div class="label">Address</div>
{#if info.version === "0.2.0" && info.host}
<SeedAddress seed={info} port={config.seed.link.port} />
{#if seed.version === "0.2.0" && seed.host}
<SeedAddress {seed} port={seed.link.port} />
{:else}
<div class="seed-address subtle">N/A</div>
<div class="desktop" />
{/if}
<!-- Seed ID -->
<div class="label">Seed ID</div>
<div>{info.id}</div>
<div>{seed.id}</div>
<div class="desktop" />
<!-- API Port -->
<div class="label">API Port</div>
<div>{config.seed.api.port}</div>
<div>{seed.api.port}</div>
<div class="desktop" />
<!-- API Version -->
<div class="label">Version</div>
<div>{info.version}</div>
<div>{seed.version}</div>
<div class="desktop" />
</div>
<!-- Seed Projects -->
{#await Seed.getProjects(config) then projects}
{#await seed.getProjects() then projects}
<div class="projects">
{#each projects as project}
<div class="project">
@@ -133,5 +131,5 @@
{/await}
</main>
{:catch}
<NotFound title={seedAddress} subtitle="Not able to query information from this seed." />
<NotFound title={host} subtitle="Not able to query information from this seed." />
{/await}
-1
View File
@@ -73,7 +73,6 @@
},
"radicle": {
"seed": {
"host": "0.0.0.0",
"api": { "port": 8777 },
"link": { "port": 8776 },
"git": { "port": 80 }
+3 -21
View File
@@ -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);
-9
View File
@@ -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 {
+20 -21
View File
@@ -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<ProjectInfo> {
const info = await api.get(`projects/${nameOrUrn}`, {}, config);
export async function getInfo(nameOrUrn: string, host: api.Host): Promise<ProjectInfo> {
const info = await api.get(`projects/${nameOrUrn}`, {}, host);
return {
...info,
@@ -106,34 +105,34 @@ export async function getInfo(nameOrUrn: string, config: Config): Promise<Projec
};
}
export async function getCommits(urn: string, commit: string, config: Config): Promise<CommitsHistory> {
return api.get(`projects/${urn}/commits?from=${commit}`, {}, config);
export async function getCommits(urn: string, commit: string, host: api.Host): Promise<CommitsHistory> {
return api.get(`projects/${urn}/commits?from=${commit}`, {}, host);
}
export async function getCommit(urn: string, commit: string, config: Config): Promise<Commit> {
return api.get(`projects/${urn}/commits/${commit}`, {}, config);
export async function getCommit(urn: string, commit: string, host: api.Host): Promise<Commit> {
return api.get(`projects/${urn}/commits/${commit}`, {}, host);
}
export async function getProjects(config: Config): Promise<ProjectInfo[]> {
return api.get("projects", {}, config);
export async function getProjects(host: api.Host): Promise<ProjectInfo[]> {
return api.get("projects", {}, host);
}
export async function getBranchesByPeer(urn: string, peer: string, config: Config): Promise<Branches> {
return api.get(`projects/${urn}/remotes/${peer}`, {}, config);
export async function getBranchesByPeer(urn: string, peer: string, host: api.Host): Promise<Branches> {
return api.get(`projects/${urn}/remotes/${peer}`, {}, host);
}
export async function getPeers(urn: string, config: Config): Promise<Peer[]> {
return api.get(`projects/${urn}/remotes`, {}, config);
export async function getPeers(urn: string, host: api.Host): Promise<Peer[]> {
return api.get(`projects/${urn}/remotes`, {}, host);
}
export async function getTree(
urn: string,
commit: string,
path: string,
config: Config
host: api.Host
): Promise<Tree> {
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<Blob> {
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<Blob> {
return api.get(`projects/${urn}/readme/${commit}`, {}, config);
return api.get(`projects/${urn}/readme/${commit}`, {}, host);
}
export function path(