Skip to content

Commit

Permalink
Add list_repo and config commands
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastinez committed Sep 5, 2024
1 parent 3edfcf9 commit 80c8e26
Show file tree
Hide file tree
Showing 23 changed files with 330 additions and 146 deletions.
12 changes: 1 addition & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
"tslib": "^2.7.0",
"typescript": "^5.2.2",
"typescript-eslint": "^8.4.0",
"vite": "^5.4.2",
"zod": "^3.23.8"
"vite": "^5.4.2"
}
}
2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ tauri = { version = "2.0.0-rc.0", features = ["isolation"] }
tauri-plugin-shell = { version = "2.0.0-rc.0" }
tauri-plugin-window-state = "2.0.0-rc.1"
thiserror = { version = "1.0.63" }
ts-rs = { version = "9.0.1", features = ["serde-json-impl"] }
ts-rs = { version = "9.0.1", features = ["serde-json-impl", "no-serde-warnings"] }

[features]
# by default Tauri runs in production mode
Expand Down
21 changes: 21 additions & 0 deletions src-tauri/bindings/Config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.

/**
* Service configuration.
*/
export type Config = {
/**
* Node alias.
*/
publicKey: string;
/**
* Node alias.
*/
alias: string;
/**
* Default seeding policy.
*/
seedingPolicy:
| { default: "allow"; scope: "followed" | "all" }
| { default: "block" };
};
14 changes: 14 additions & 0 deletions src-tauri/bindings/RepoInfo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { SupportedPayloads } from "./SupportedPayloads";

/**
* Repos info.
*/
export type RepoInfo = {
payloads: SupportedPayloads;
delegates: ({ id: string } | { id: string; alias?: string })[];
threshold: number;
visibility: { type: "public" } | { type: "private"; allow?: string[] };
rid: string;
seeding: number;
};
25 changes: 25 additions & 0 deletions src-tauri/bindings/SupportedPayloads.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.

export type SupportedPayloads = {
"xyz.radicle.project"?: {
data: {
defaultBranch: string;
description: string;
name: string;
};
meta: {
head: string;
issues: {
open: number;
closed: number;
};
patches: {
open: number;
draft: number;
archived: number;
merged: number;
};
lastCommit: number;
};
};
};
7 changes: 7 additions & 0 deletions src-tauri/bindings/serde_json/JsonValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.

export type JsonValue =
| number
| string
| Array<JsonValue>
| { [key: string]: JsonValue };
3 changes: 3 additions & 0 deletions src-tauri/src/commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod auth;
pub mod profile;
pub mod repos;
File renamed without changes.
15 changes: 15 additions & 0 deletions src-tauri/src/commands/profile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use crate::error::Error;
use crate::types::config::Config;
use crate::AppState;

/// Get active config.
#[tauri::command]
pub fn config(ctx: tauri::State<AppState>) -> Result<Config, Error> {
let config = Config {
public_key: ctx.profile.public_key.clone(),
alias: ctx.profile.config.node.alias.clone(),
seeding_policy: ctx.profile.config.node.seeding_policy.clone(),
};

Ok::<_, Error>(config)
}
30 changes: 30 additions & 0 deletions src-tauri/src/commands/repos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use radicle::storage::ReadStorage;

use crate::error::Error;
use crate::types;
use crate::AppState;

/// List all repos.
#[tauri::command]
pub fn list_repos(ctx: tauri::State<AppState>) -> Result<Vec<types::repo::RepoInfo>, Error> {
let storage = &ctx.profile.storage;
let policies = ctx.profile.policies()?;

let mut repos = storage.repositories()?.into_iter().collect::<Vec<_>>();
repos.sort_by_key(|p| p.rid);

let infos = repos
.into_iter()
.filter_map(|info| {
if !policies.is_seeding(&info.rid).unwrap_or_default() {
return None;
}
let (repo, doc) = ctx.repo(info.rid).ok()?;
let repo_info = ctx.repo_info(&repo, doc).ok()?;

Some(repo_info)
})
.collect::<Vec<_>>();

Ok::<_, Error>(infos)
}
19 changes: 19 additions & 0 deletions src-tauri/src/json.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use serde_json::{json, Value};

use radicle::identity;
use radicle::node::AliasStore;

pub(crate) struct Author<'a>(&'a identity::Did);

impl<'a> Author<'a> {
pub fn new(did: &'a identity::Did) -> Self {
Self(did)
}

pub fn as_json(&self, aliases: &impl AliasStore) -> Value {
aliases.alias(self.0).map_or(
json!({ "id": self.0 }),
|alias| json!({ "id": self.0, "alias": alias, }),
)
}
}
79 changes: 76 additions & 3 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,82 @@
mod auth;
mod commands;
mod error;
mod json;
mod types;

use auth::authenticate;
use serde_json::json;
use tauri::Manager;

use radicle::identity::doc::PayloadId;
use radicle::identity::DocAt;
use radicle::identity::RepoId;
use radicle::issue::cache::Issues;
use radicle::node::routing::Store;
use radicle::patch::cache::Patches;
use radicle::storage::git::Repository;
use radicle::storage::{ReadRepository, ReadStorage};

use commands::{auth, profile, repos};
use types::repo::SupportedPayloads;

struct AppState {
profile: radicle::Profile,
}

impl AppState {
pub fn repo_info<R: ReadRepository + radicle::cob::Store>(
&self,
repo: &R,
doc: DocAt,
) -> Result<types::repo::RepoInfo, error::Error> {
let DocAt { doc, .. } = doc;
let rid = repo.id();

let aliases = self.profile.aliases();
let delegates = doc
.delegates
.into_iter()
.map(|did| json::Author::new(&did).as_json(&aliases))
.collect::<Vec<_>>();
let db = &self.profile.database()?;
let seeding = db.count(&rid).unwrap_or_default();

let project = doc.payload.get(&PayloadId::project()).and_then(|payload| {
let (_, head) = repo.head().ok()?;
let commit = repo.commit(head).ok()?;
let patches = self.profile.patches(repo).ok()?;
let patches = patches.counts().ok()?;
let issues = self.profile.issues(repo).ok()?;
let issues = issues.counts().ok()?;

Some(json!({
"data": payload,
"meta": {
"issues": issues,
"patches": patches,
"head": head,
"lastCommit": commit.time().seconds(),
},
}))
});

Ok(types::repo::RepoInfo {
payloads: SupportedPayloads { project },
delegates: delegates,
threshold: doc.threshold,
visibility: doc.visibility,
rid,
seeding,
})
}

/// Get a repository by RID, checking to make sure we're allowed to view it.
pub fn repo(&self, rid: RepoId) -> Result<(Repository, DocAt), error::Error> {
let repo = self.profile.storage.repository(rid)?;
let doc = repo.identity_doc()?;
Ok((repo, doc))
}
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
Expand All @@ -30,7 +99,11 @@ pub fn run() {
})
.plugin(tauri_plugin_shell::init())
.plugin(tauri_plugin_window_state::Builder::default().build())
.invoke_handler(tauri::generate_handler![authenticate])
.invoke_handler(tauri::generate_handler![
auth::authenticate,
repos::list_repos,
profile::config,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
23 changes: 23 additions & 0 deletions src-tauri/src/types/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use radicle::crypto::PublicKey;
use serde::Serialize;
use ts_rs::TS;

use radicle::node::config::DefaultSeedingPolicy;
use radicle::node::Alias;

/// Service configuration.
#[derive(Debug, Clone, TS, Serialize)]
#[serde(rename_all = "camelCase")]
#[ts(export)]
pub struct Config {
/// Node Public Key in NID format.
#[ts(as = "String")]
pub public_key: PublicKey,
/// Node alias.
#[ts(as = "String")]
pub alias: Alias,
/// Default seeding policy.
#[serde(default)]
#[ts(type = "{ default: 'allow', scope: 'followed' | 'all' } | { default: 'block' }")]
pub seeding_policy: DefaultSeedingPolicy,
}
2 changes: 2 additions & 0 deletions src-tauri/src/types/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod config;
pub mod repo;
50 changes: 50 additions & 0 deletions src-tauri/src/types/repo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use serde::Serialize;
use serde_json::Value;
use ts_rs::TS;

use radicle::identity::RepoId;

/// Repos info.
#[derive(Serialize, TS)]
#[ts(export)]
pub struct RepoInfo {
pub payloads: SupportedPayloads,
#[ts(type = "({ id: string } | { id: string, alias?: string })[]")]
pub delegates: Vec<Value>,
pub threshold: usize,
#[ts(type = "{ type: 'public' } | { type: 'private', allow?: string[] }")]
pub visibility: radicle::identity::Visibility,
#[ts(as = "String")]
pub rid: RepoId,
pub seeding: usize,
}

#[derive(Serialize, TS)]
#[ts(export)]
pub struct SupportedPayloads {
#[serde(rename = "xyz.radicle.project")]
#[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(optional)]
#[ts(type = r#"{
data: {
defaultBranch: string,
description: string,
name: string,
},
meta: {
head: string,
issues: {
open: number,
closed: number,
},
patches: {
open: number,
draft: number,
archived: number,
merged: number,
}
lastCommit: number,
}
}"#)]
pub project: Option<Value>,
}
4 changes: 2 additions & 2 deletions src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import { theme } from "@app/components/ThemeSwitch.svelte";
import { unreachable } from "@app/lib/utils";
import AuthenticationError from "@app/views/AuthenticationError.svelte";
import DesignSystem from "@app/views/DesignSystem.svelte";
import Home from "@app/views/Home.svelte";
import AuthenticationError from "@app/views/AuthenticationError.svelte";
const activeRouteStore = router.activeRouteStore;
void router.loadFromLocation();
Expand All @@ -36,7 +36,7 @@
{#if $activeRouteStore.resource === "booting"}
<!-- Don't show anything -->
{:else if $activeRouteStore.resource === "home"}
<Home />
<Home {...$activeRouteStore.params} />
{:else if $activeRouteStore.resource === "authenticationError"}
<AuthenticationError {...$activeRouteStore.params} />
{:else if $activeRouteStore.resource === "designSystem"}
Expand Down
Loading

0 comments on commit 80c8e26

Please sign in to comment.