display blobs
This commit is contained in:
parent
27102f1a58
commit
dd446e25a1
5 changed files with 184 additions and 39 deletions
|
@ -4,6 +4,8 @@ version = "0.1.0"
|
||||||
authors = ["Michael Zhang <iptq@protonmail.com>"]
|
authors = ["Michael Zhang <iptq@protonmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
git2 = { version = "0.13.0", default-features = false }
|
git2 = { version = "0.13.0", default-features = false }
|
||||||
serde = "1.0.105"
|
serde = "1.0.105"
|
||||||
|
|
199
src/main.rs
199
src/main.rs
|
@ -8,14 +8,14 @@ extern crate serde_json;
|
||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
use std::net::SocketAddr;
|
|
||||||
use std::path::{Component as PathComponent, PathBuf};
|
use std::path::{Component as PathComponent, Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use futures::future::{self, TryFutureExt};
|
use futures::future::{self};
|
||||||
use git2::Repository;
|
use git2::{ObjectType, Oid, Repository, Tree};
|
||||||
use hyper::{
|
use hyper::{
|
||||||
service::{make_service_fn, service_fn},
|
service::{make_service_fn, service_fn},
|
||||||
Body, Request, Response, Server, StatusCode,
|
Body, Request, Response, Server, StatusCode,
|
||||||
|
@ -40,6 +40,7 @@ lazy_static! {
|
||||||
let mut tera = Tera::default();
|
let mut tera = Tera::default();
|
||||||
tera.add_raw_templates(vec![
|
tera.add_raw_templates(vec![
|
||||||
template!("index.html"),
|
template!("index.html"),
|
||||||
|
template!("repo_blob.html"),
|
||||||
template!("repo_index.html"),
|
template!("repo_index.html"),
|
||||||
template!("repo_tree.html"),
|
template!("repo_tree.html"),
|
||||||
])
|
])
|
||||||
|
@ -68,7 +69,7 @@ impl Fedhub {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_dir_list(&self) -> Result<Vec<PathBuf>> {
|
pub fn get_dir_list(&self) -> Result<Vec<PathBuf>> {
|
||||||
let state = self.state.read();
|
let state = self.state.read();
|
||||||
let mut con = state.cache.get_connection()?;
|
let mut con = state.cache.get_connection()?;
|
||||||
if RedisCmd::exists("repos").query(&mut con)? {
|
if RedisCmd::exists("repos").query(&mut con)? {
|
||||||
|
@ -102,63 +103,114 @@ impl Fedhub {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn render_index(&self) -> Result<Response<Body>> {
|
pub fn render_index(&self) -> Result<Response<Body>> {
|
||||||
let directories = self.get_dir_list().await?;
|
let directories = self.get_dir_list()?;
|
||||||
let mut ctx = TeraContext::new();
|
let mut ctx = TeraContext::new();
|
||||||
ctx.insert("repos", &directories);
|
ctx.insert("repos", &directories);
|
||||||
Ok(Response::new(TERA.render("index.html", &ctx)?.into()))
|
Ok(Response::new(TERA.render("index.html", &ctx)?.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn render_repo_index(
|
pub fn render_repo_index(&self, path: PathBuf, repo: &Repository) -> Result<Response<Body>> {
|
||||||
&self,
|
|
||||||
path: PathBuf,
|
|
||||||
repo: Repository,
|
|
||||||
) -> Result<Response<Body>> {
|
|
||||||
let mut ctx = TeraContext::new();
|
let mut ctx = TeraContext::new();
|
||||||
ctx.insert("name", &path);
|
ctx.insert("repo_name", &path);
|
||||||
let mut branches = Vec::new();
|
let mut branches = Vec::new();
|
||||||
for branch in repo.branches(None)? {
|
for branch in repo.branches(None)? {
|
||||||
let (branch, _) = branch?;
|
let (branch, _) = branch?;
|
||||||
branches.push(branch.name()?.unwrap().to_string());
|
let name = branch.name()?.unwrap().to_string();
|
||||||
|
let branch_ref = branch.into_reference();
|
||||||
|
let url = format!(
|
||||||
|
"/{}/+/{}",
|
||||||
|
path.to_str().unwrap(),
|
||||||
|
branch_ref.name().unwrap()
|
||||||
|
);
|
||||||
|
branches.push(json!({
|
||||||
|
"name": name,
|
||||||
|
"url": url,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
ctx.insert("branches", &branches);
|
ctx.insert("branches", &branches);
|
||||||
return Ok(Response::new(TERA.render("repo_index.html", &ctx)?.into()));
|
return Ok(Response::new(TERA.render("repo_index.html", &ctx)?.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn render_repo_tree(
|
pub fn render_repo_tree(
|
||||||
&self,
|
&self,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
repo: Repository,
|
repo: &Repository,
|
||||||
tree: String,
|
tree_id: Oid,
|
||||||
|
tree_name: String,
|
||||||
|
filepath: Option<PathBuf>,
|
||||||
) -> Result<Response<Body>> {
|
) -> Result<Response<Body>> {
|
||||||
let commit_ref = repo.resolve_reference_from_short_name(tree.as_ref())?;
|
let tree = repo.find_tree(tree_id)?;
|
||||||
let commit = commit_ref.peel_to_commit()?;
|
|
||||||
let tree = commit.tree()?;
|
|
||||||
let mut ctx = TeraContext::new();
|
let mut ctx = TeraContext::new();
|
||||||
ctx.insert("name", &path);
|
ctx.insert("repo_name", &path);
|
||||||
ctx.insert("ref", commit_ref.shorthand().unwrap());
|
ctx.insert("tree_name", &tree_name);
|
||||||
|
ctx.insert("filepath", &filepath);
|
||||||
let mut entries = Vec::new();
|
let mut entries = Vec::new();
|
||||||
for entry in tree.iter() {
|
for entry in tree.iter() {
|
||||||
|
let url = match &filepath {
|
||||||
|
Some(filepath) => format!(
|
||||||
|
"/{}/+/{}/{}/{}",
|
||||||
|
path.to_str().unwrap(),
|
||||||
|
tree_name,
|
||||||
|
filepath.to_str().unwrap(),
|
||||||
|
entry.name().unwrap()
|
||||||
|
),
|
||||||
|
None => format!(
|
||||||
|
"/{}/+/{}/{}",
|
||||||
|
path.to_str().unwrap(),
|
||||||
|
tree_name,
|
||||||
|
entry.name().unwrap()
|
||||||
|
),
|
||||||
|
};
|
||||||
entries.push(json!({
|
entries.push(json!({
|
||||||
"name": entry.name().unwrap(),
|
"name": entry.name().unwrap(),
|
||||||
|
"url": url,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
ctx.insert("entries", &entries);
|
ctx.insert("entries", &entries);
|
||||||
return Ok(Response::new(TERA.render("repo_tree.html", &ctx)?.into()));
|
return Ok(Response::new(TERA.render("repo_tree.html", &ctx)?.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle(self, req: Request<Body>) -> Result<Response<Body>> {
|
pub fn render_repo_blob(
|
||||||
|
&self,
|
||||||
|
path: PathBuf,
|
||||||
|
repo: &Repository,
|
||||||
|
tree_name: String,
|
||||||
|
filepath: impl AsRef<Path>,
|
||||||
|
blob_id: Oid,
|
||||||
|
) -> Result<Response<Body>> {
|
||||||
|
let filepath = filepath.as_ref();
|
||||||
|
let blob = repo.find_blob(blob_id)?;
|
||||||
|
let mut ctx = TeraContext::new();
|
||||||
|
ctx.insert("repo_name", &path);
|
||||||
|
ctx.insert("tree_name", &tree_name);
|
||||||
|
ctx.insert(
|
||||||
|
"blob",
|
||||||
|
&json!({
|
||||||
|
"name": filepath,
|
||||||
|
"binary": blob.is_binary(),
|
||||||
|
"contents": if blob.is_binary() {
|
||||||
|
json!(null)
|
||||||
|
} else {
|
||||||
|
json!(String::from_utf8(blob.content().to_vec())?)
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return Ok(Response::new(TERA.render("repo_blob.html", &ctx)?.into()));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle(self, req: Request<Body>) -> Result<Response<Body>> {
|
||||||
let repo_root = self.config.repo_root.clone();
|
let repo_root = self.config.repo_root.clone();
|
||||||
let uri = req.uri();
|
let uri = req.uri();
|
||||||
let path = uri.path();
|
let path = uri.path();
|
||||||
|
|
||||||
if path == "/" {
|
if path == "/" {
|
||||||
return self.render_index().await;
|
return self.render_index();
|
||||||
}
|
}
|
||||||
|
|
||||||
let repo_info = {
|
let repo_info = {
|
||||||
let mut repo_info = None;
|
let mut repo_info = None;
|
||||||
for repo_dir in self.get_dir_list().await? {
|
for repo_dir in self.get_dir_list()? {
|
||||||
let path = PathBuf::from(path.trim_start_matches("/"));
|
let path = PathBuf::from(path.trim_start_matches("/"));
|
||||||
if path.starts_with(&repo_dir) {
|
if path.starts_with(&repo_dir) {
|
||||||
let repo = Repository::open(repo_root.join(&repo_dir))?;
|
let repo = Repository::open(repo_root.join(&repo_dir))?;
|
||||||
|
@ -171,13 +223,53 @@ impl Fedhub {
|
||||||
|
|
||||||
if let Some((path, repo, remainder)) = repo_info {
|
if let Some((path, repo, remainder)) = repo_info {
|
||||||
if remainder == PathBuf::from("/") {
|
if remainder == PathBuf::from("/") {
|
||||||
return self.render_repo_index(path, repo).await;
|
return self.render_repo_index(path, &repo);
|
||||||
} else if remainder.starts_with("/tree") {
|
} else if remainder.starts_with("/+") {
|
||||||
let tree = remainder.strip_prefix("/tree").unwrap().components().next();
|
let mut iter = remainder.strip_prefix("/+").unwrap().components();
|
||||||
if let Some(PathComponent::Normal(tree)) = tree {
|
let tree_name = iter.next();
|
||||||
|
if let Some(PathComponent::Normal(tree_name)) = tree_name {
|
||||||
|
let tree_name = tree_name.to_str().unwrap().to_string();
|
||||||
|
let tree_name = if tree_name == "refs" {
|
||||||
|
let second = iter.next().unwrap();
|
||||||
|
let third = iter.next().unwrap();
|
||||||
|
format!(
|
||||||
|
"{}/{}/{}",
|
||||||
|
tree_name,
|
||||||
|
second.as_os_str().to_str().unwrap(),
|
||||||
|
third.as_os_str().to_str().unwrap()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
tree_name
|
||||||
|
};
|
||||||
|
let (tree_id, shortname) = {
|
||||||
|
let (tree, shortname) = get_tree_from_name(&repo, tree_name)?;
|
||||||
|
(tree.id(), shortname)
|
||||||
|
};
|
||||||
|
let filepath = iter.collect::<PathBuf>();
|
||||||
|
println!("filepath: {:?}", filepath);
|
||||||
|
if filepath.iter().collect::<Vec<_>>().len() == 0 {
|
||||||
|
return self.render_repo_tree(path, &repo, tree_id, shortname, None);
|
||||||
|
} else {
|
||||||
|
let (object_id, object_type) =
|
||||||
|
get_path_from_tree(&repo, tree_id, &filepath)?;
|
||||||
|
println!("{:?} {:?}", object_id, object_type);
|
||||||
|
match object_type {
|
||||||
|
ObjectType::Tree => {
|
||||||
|
return self.render_repo_tree(
|
||||||
|
path,
|
||||||
|
&repo,
|
||||||
|
object_id,
|
||||||
|
shortname,
|
||||||
|
Some(filepath),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ObjectType::Blob => {
|
||||||
return self
|
return self
|
||||||
.render_repo_tree(path, repo, tree.to_str().unwrap().to_string())
|
.render_repo_blob(path, &repo, shortname, filepath, object_id)
|
||||||
.await;
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,6 +281,44 @@ impl Fedhub {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_tree_from_name(repo: &Repository, name: impl AsRef<str>) -> Result<(Tree, String)> {
|
||||||
|
Ok(
|
||||||
|
if let Ok(commit_ref) = repo.resolve_reference_from_short_name(name.as_ref()) {
|
||||||
|
let tree_name = commit_ref.shorthand().unwrap().to_string();
|
||||||
|
(commit_ref.peel_to_commit()?.tree()?, tree_name)
|
||||||
|
} else if let Ok(commit) = repo.find_commit(Oid::from_str(name.as_ref())?) {
|
||||||
|
let tree_name = name.as_ref().to_string();
|
||||||
|
(commit.tree()?, tree_name)
|
||||||
|
} else {
|
||||||
|
anyhow::bail!("Fuck!");
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_path_from_tree<'a>(
|
||||||
|
repo: &Repository,
|
||||||
|
curr_oid: Oid,
|
||||||
|
path: impl AsRef<Path>,
|
||||||
|
) -> Result<(Oid, ObjectType)> {
|
||||||
|
let mut components = path.as_ref().components();
|
||||||
|
let tree = repo.find_tree(curr_oid)?;
|
||||||
|
if let Some(next_file) = components.next() {
|
||||||
|
if let Some(entry) = tree.get_name(next_file.as_os_str().to_str().unwrap()) {
|
||||||
|
let kind = entry.kind().unwrap();
|
||||||
|
match kind {
|
||||||
|
ObjectType::Tree => {
|
||||||
|
get_path_from_tree(repo, entry.id(), components.collect::<PathBuf>())
|
||||||
|
}
|
||||||
|
_ => Ok((entry.id(), kind)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
anyhow::bail!("doesn't exist bro: {:?}", next_file);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok((tree.id(), ObjectType::Tree))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
let mut config_file = File::open("config.toml").await?;
|
let mut config_file = File::open("config.toml").await?;
|
||||||
|
@ -204,9 +334,12 @@ async fn main() -> Result<()> {
|
||||||
let fedhub = fedhub.clone();
|
let fedhub = fedhub.clone();
|
||||||
let main = move |req| {
|
let main = move |req| {
|
||||||
let fedhub = fedhub.clone();
|
let fedhub = fedhub.clone();
|
||||||
fedhub.handle(req).or_else(|err| {
|
future::ready(match fedhub.handle(req) {
|
||||||
|
Ok(res) => Ok::<_, Infallible>(res),
|
||||||
|
Err(err) => {
|
||||||
eprintln!("Error: {:?}", err);
|
eprintln!("Error: {:?}", err);
|
||||||
future::ok::<_, Infallible>(Response::new(Body::from(format!("Error: {:?}", err))))
|
Ok(Response::new(Body::from(format!("Error: {:?}", err))))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
10
templates/repo_blob.html
Normal file
10
templates/repo_blob.html
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>{{ repo_name }}: {{ tree_name }}: {{ blob.name }}</h1>
|
||||||
|
|
||||||
|
<pre>{{ blob.contents }}</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -3,12 +3,12 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1>{{ name }}</h1>
|
<h1>{{ repo_name }}</h1>
|
||||||
|
|
||||||
<h3>branches:</h3>
|
<h3>branches:</h3>
|
||||||
<ul>
|
<ul>
|
||||||
{% for branch in branches %}
|
{% for branch in branches %}
|
||||||
{{ branch }}
|
<a href="{{ branch.url }}">{{ branch.name }}</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1>{{ name }}: {{ ref }}</h1>
|
<h1>{{ repo_name }}: {{ tree_name }}{% if filepath %}: {{ filepath }}{% endif %}</h1>
|
||||||
|
|
||||||
<h3>entries:</h3>
|
<h3>entries:</h3>
|
||||||
<ul>
|
<ul>
|
||||||
{% for entry in entries %}
|
{% for entry in entries %}
|
||||||
<li>{{ entry.name }}</li>
|
<li><a href="{{ entry.url }}">{{ entry.name }}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</body>
|
</body>
|
||||||
|
|
Loading…
Reference in a new issue