Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
Migrate `/projects`: `remotes`, `remote`, `blob`, `readme` handlers
xphoniex committed 3 years ago
commit b4902d34088378c9555f6f0f4e40d863c415f6aa
parent 84c3cf4cf13915508bbcfa333ccec30adda2b8a8
5 files changed +92 -11
modified radicle-crypto/src/lib.rs
@@ -19,7 +19,7 @@ mod cyphernet;
pub use self::cyphernet::Ed25519;

/// Verified (used as type witness).
-
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)]
pub struct Verified;
/// Unverified (used as type witness).
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
modified radicle-httpd/src/api/error.rs
@@ -67,6 +67,10 @@ pub enum Error {
    /// Git2 error.
    #[error(transparent)]
    Git2(#[from] radicle::git::raw::Error),
+

+
    /// Storage refs error.
+
    #[error(transparent)]
+
    StorageRef(#[from] radicle::storage::refs::Error),
}

impl Error {
modified radicle-httpd/src/api/v1/projects.rs
@@ -13,8 +13,10 @@ use tower_http::set_header::SetResponseHeaderLayer;
use radicle::cob::issue::Issues;
use radicle::git::raw::BranchType;
use radicle::identity::{Doc, Id};
+
use radicle::node::NodeId;
use radicle::storage::{Oid, ReadRepository, WriteRepository, WriteStorage};
use radicle_surf::git::History;
+
use radicle_surf::Revision::Sha;

use crate::api::axum_extra::{Path, Query};
use crate::api::error::Error;
@@ -39,6 +41,10 @@ pub fn router(ctx: Context) -> Router {
            ),
        )
        .route("/projects/:project/tree/:sha/*path", get(tree_handler))
+
        .route("/projects/:project/remotes", get(remotes_handler))
+
        .route("/projects/:project/remotes/:peer", get(remote_handler))
+
        .route("/projects/:project/blob/:sha/*path", get(blob_handler))
+
        .route("/projects/:project/readme/:sha", get(readme_handler))
        .layer(Extension(ctx))
}

@@ -183,7 +189,7 @@ async fn commit_handler(
    let repo = storage.repository(project)?;
    let commit = radicle_surf::commit(&repo.raw().into(), sha)?;

-
    Ok::<_, Error>(Json(json!(commit)))
+
    Ok::<_, Error>(Json(commit))
}

/// Get project activity for the past year.
@@ -221,11 +227,7 @@ async fn tree_handler(
    let path = path.strip_prefix('/').ok_or(Error::NotFound)?.to_string();
    let storage = &ctx.profile.storage;
    let repo = storage.repository(project)?;
-
    let tree = radicle_surf::object::tree(
-
        &repo.raw().into(),
-
        Some(radicle_surf::Revision::Sha { sha }),
-
        Some(path),
-
    )?;
+
    let tree = radicle_surf::object::tree(&repo.raw().into(), Some(Sha { sha }), Some(path))?;
    let response = json!({
        "path": &tree.path,
        "entries": &tree.entries,
@@ -236,6 +238,77 @@ async fn tree_handler(
    Ok::<_, Error>(Json(response))
}

+
/// Get all project remotes.
+
/// `GET /projects/:project/remotes`
+
async fn remotes_handler(
+
    Extension(ctx): Extension<Context>,
+
    Path(project): Path<Id>,
+
) -> impl IntoResponse {
+
    let storage = &ctx.profile.storage;
+
    let repo = storage.repository(project)?;
+
    let remotes = repo
+
        .remotes()?
+
        .filter_map(|r| r.map(|r| r.1).ok())
+
        .collect::<Vec<_>>();
+

+
    Ok::<_, Error>(Json(remotes))
+
}
+

+
/// Get project remote.
+
/// `GET /projects/:project/remotes/:peer`
+
async fn remote_handler(
+
    Extension(ctx): Extension<Context>,
+
    Path((project, node_id)): Path<(Id, NodeId)>,
+
) -> impl IntoResponse {
+
    let storage = &ctx.profile.storage;
+
    let repo = storage.repository(project)?;
+
    let remote = repo.remote(&node_id)?;
+

+
    Ok::<_, Error>(Json(remote))
+
}
+

+
/// Get project source file.
+
/// `GET /projects/:project/blob/:sha/*path`
+
async fn blob_handler(
+
    Extension(ctx): Extension<Context>,
+
    Path((project, sha, path)): Path<(Id, Oid, String)>,
+
) -> impl IntoResponse {
+
    let path = path.strip_prefix('/').ok_or(Error::NotFound)?;
+
    let storage = &ctx.profile.storage;
+
    let repo = storage.repository(project)?;
+
    let blob = radicle_surf::blob::blob(&repo.raw().into(), Some(Sha { sha }), path)?;
+

+
    Ok::<_, Error>(Json(blob))
+
}
+

+
/// Get project readme.
+
/// `GET /projects/:project/readme/:sha`
+
async fn readme_handler(
+
    Extension(ctx): Extension<Context>,
+
    Path((project, sha)): Path<(Id, Oid)>,
+
) -> impl IntoResponse {
+
    let storage = &ctx.profile.storage;
+
    let repo = storage.repository(project)?;
+
    let paths = &[
+
        "README",
+
        "README.md",
+
        "README.markdown",
+
        "README.txt",
+
        "README.rst",
+
        "Readme.md",
+
    ];
+

+
    for path in paths {
+
        if let Ok(blob) = radicle_surf::blob::blob(&repo.raw().into(), Some(Sha { sha }), path) {
+
            return Ok::<_, Error>(Json(blob));
+
        }
+
    }
+

+
    Err(radicle_surf::object::Error::PathNotFound(
+
        radicle_surf::file_system::Path::try_from("README").unwrap(),
+
    ))?
+
}
+

#[derive(Serialize)]
struct Stats {
    branches: usize,
modified radicle/src/storage.rs
@@ -6,6 +6,7 @@ use std::ops::Deref;
use std::path::Path;
use std::{fmt, io};

+
use serde::Serialize;
use thiserror::Error;

use crypto::{PublicKey, Signer, Unverified, Verified};
@@ -184,11 +185,12 @@ impl<V> From<Remotes<V>> for HashMap<RemoteId, Refs> {
}

/// A project remote.
-
#[derive(Debug, Clone, PartialEq, Eq)]
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct Remote<V = Verified> {
    /// ID of remote.
    pub id: PublicKey,
    /// Git references published under this remote, and their hashes.
+
    #[serde(flatten)]
    pub refs: SignedRefs<V>,
    /// Whether this remote is a delegate for the project.
    pub delegate: bool,
modified radicle/src/storage/refs.rs
@@ -8,6 +8,7 @@ use std::path::Path;
use std::str::FromStr;

use crypto::{PublicKey, Signature, Signer, SignerError, Unverified, Verified};
+
use serde::Serialize;
use thiserror::Error;

use crate::git;
@@ -62,7 +63,7 @@ impl Error {
}

/// The published state of a local repository.
-
#[derive(Default, Clone, Debug, PartialEq, Eq)]
+
#[derive(Default, Clone, Debug, PartialEq, Eq, Serialize)]
pub struct Refs(BTreeMap<git::RefString, Oid>);

impl Refs {
@@ -197,11 +198,12 @@ impl DerefMut for Refs {
///
/// The type parameter keeps track of whether the signature was [`Verified`] or
/// [`Unverified`].
-
#[derive(Debug, Clone, PartialEq, Eq)]
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct SignedRefs<V> {
    pub refs: Refs,
+
    #[serde(skip)]
    pub signature: Signature,
-

+
    #[serde(skip)]
    _verified: PhantomData<V>,
}