Migrate `/projects`: `remotes`, `remote`, `blob`, `readme` handlers
Signed-off-by: xphoniex <dj.2dixx@gmail.com>
This commit is contained in:
parent
84c3cf4cf1
commit
b4902d3408
|
|
@ -19,7 +19,7 @@ mod cyphernet;
|
||||||
pub use self::cyphernet::Ed25519;
|
pub use self::cyphernet::Ed25519;
|
||||||
|
|
||||||
/// Verified (used as type witness).
|
/// Verified (used as type witness).
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)]
|
||||||
pub struct Verified;
|
pub struct Verified;
|
||||||
/// Unverified (used as type witness).
|
/// Unverified (used as type witness).
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,10 @@ pub enum Error {
|
||||||
/// Git2 error.
|
/// Git2 error.
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Git2(#[from] radicle::git::raw::Error),
|
Git2(#[from] radicle::git::raw::Error),
|
||||||
|
|
||||||
|
/// Storage refs error.
|
||||||
|
#[error(transparent)]
|
||||||
|
StorageRef(#[from] radicle::storage::refs::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,10 @@ use tower_http::set_header::SetResponseHeaderLayer;
|
||||||
use radicle::cob::issue::Issues;
|
use radicle::cob::issue::Issues;
|
||||||
use radicle::git::raw::BranchType;
|
use radicle::git::raw::BranchType;
|
||||||
use radicle::identity::{Doc, Id};
|
use radicle::identity::{Doc, Id};
|
||||||
|
use radicle::node::NodeId;
|
||||||
use radicle::storage::{Oid, ReadRepository, WriteRepository, WriteStorage};
|
use radicle::storage::{Oid, ReadRepository, WriteRepository, WriteStorage};
|
||||||
use radicle_surf::git::History;
|
use radicle_surf::git::History;
|
||||||
|
use radicle_surf::Revision::Sha;
|
||||||
|
|
||||||
use crate::api::axum_extra::{Path, Query};
|
use crate::api::axum_extra::{Path, Query};
|
||||||
use crate::api::error::Error;
|
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/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))
|
.layer(Extension(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -183,7 +189,7 @@ async fn commit_handler(
|
||||||
let repo = storage.repository(project)?;
|
let repo = storage.repository(project)?;
|
||||||
let commit = radicle_surf::commit(&repo.raw().into(), sha)?;
|
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.
|
/// 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 path = path.strip_prefix('/').ok_or(Error::NotFound)?.to_string();
|
||||||
let storage = &ctx.profile.storage;
|
let storage = &ctx.profile.storage;
|
||||||
let repo = storage.repository(project)?;
|
let repo = storage.repository(project)?;
|
||||||
let tree = radicle_surf::object::tree(
|
let tree = radicle_surf::object::tree(&repo.raw().into(), Some(Sha { sha }), Some(path))?;
|
||||||
&repo.raw().into(),
|
|
||||||
Some(radicle_surf::Revision::Sha { sha }),
|
|
||||||
Some(path),
|
|
||||||
)?;
|
|
||||||
let response = json!({
|
let response = json!({
|
||||||
"path": &tree.path,
|
"path": &tree.path,
|
||||||
"entries": &tree.entries,
|
"entries": &tree.entries,
|
||||||
|
|
@ -236,6 +238,77 @@ async fn tree_handler(
|
||||||
Ok::<_, Error>(Json(response))
|
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)]
|
#[derive(Serialize)]
|
||||||
struct Stats {
|
struct Stats {
|
||||||
branches: usize,
|
branches: usize,
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use std::ops::Deref;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::{fmt, io};
|
use std::{fmt, io};
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crypto::{PublicKey, Signer, Unverified, Verified};
|
use crypto::{PublicKey, Signer, Unverified, Verified};
|
||||||
|
|
@ -184,11 +185,12 @@ impl<V> From<Remotes<V>> for HashMap<RemoteId, Refs> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A project remote.
|
/// A project remote.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||||
pub struct Remote<V = Verified> {
|
pub struct Remote<V = Verified> {
|
||||||
/// ID of remote.
|
/// ID of remote.
|
||||||
pub id: PublicKey,
|
pub id: PublicKey,
|
||||||
/// Git references published under this remote, and their hashes.
|
/// Git references published under this remote, and their hashes.
|
||||||
|
#[serde(flatten)]
|
||||||
pub refs: SignedRefs<V>,
|
pub refs: SignedRefs<V>,
|
||||||
/// Whether this remote is a delegate for the project.
|
/// Whether this remote is a delegate for the project.
|
||||||
pub delegate: bool,
|
pub delegate: bool,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use std::path::Path;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crypto::{PublicKey, Signature, Signer, SignerError, Unverified, Verified};
|
use crypto::{PublicKey, Signature, Signer, SignerError, Unverified, Verified};
|
||||||
|
use serde::Serialize;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::git;
|
use crate::git;
|
||||||
|
|
@ -62,7 +63,7 @@ impl Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The published state of a local repository.
|
/// 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>);
|
pub struct Refs(BTreeMap<git::RefString, Oid>);
|
||||||
|
|
||||||
impl Refs {
|
impl Refs {
|
||||||
|
|
@ -197,11 +198,12 @@ impl DerefMut for Refs {
|
||||||
///
|
///
|
||||||
/// The type parameter keeps track of whether the signature was [`Verified`] or
|
/// The type parameter keeps track of whether the signature was [`Verified`] or
|
||||||
/// [`Unverified`].
|
/// [`Unverified`].
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||||
pub struct SignedRefs<V> {
|
pub struct SignedRefs<V> {
|
||||||
pub refs: Refs,
|
pub refs: Refs,
|
||||||
|
#[serde(skip)]
|
||||||
pub signature: Signature,
|
pub signature: Signature,
|
||||||
|
#[serde(skip)]
|
||||||
_verified: PhantomData<V>,
|
_verified: PhantomData<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue