repos
/ repos-rust master

repos-rust

mirror archived upstream

A minimal self-hosted git browser on Rust axum: bare repos rendered as a website with commits, diffs, syntax-highlighted blobs, atom feeds, and clone over HTTPS.

axumdockergitgit-browsergitoxidegixrustself-hosted

2.2 KB · 66 lines · Rust Raw History
 1use axum::{
 2    extract::{Path, State},
 3    response::{IntoResponse, Response},
 4    routing::get,
 5    Router,
 6};
 7
 8use crate::render::{not_found, render};
 9use crate::AppState;
10
11pub fn router() -> Router<AppState> {
12    Router::new().route("/{name}", get(repo_page))
13}
14
15async fn repo_page(Path(name): Path<String>, State(state): State<AppState>) -> Response {
16    let repo_root = state.config.repo_root.clone();
17    let clone_base = state.config.clone_base.clone();
18    let name_for_blocking = name.clone();
19    let result = tokio::task::spawn_blocking(move || -> anyhow::Result<_> {
20        let repo = crate::git::open(&repo_root, &name_for_blocking)?;
21        let summary = crate::git::repo_summary(
22            &crate::git::resolve_path(&repo_root, &name_for_blocking)?,
23            &clone_base,
24        )?;
25        let head = repo.head_commit().ok();
26        let commits = if let Some(h) = head.as_ref() {
27            crate::git::recent_commits(&repo, h.id, 10).unwrap_or_default()
28        } else {
29            Vec::new()
30        };
31        let readme_html = head.as_ref().and_then(|h| {
32            crate::git::read_readme(&repo, h.id).map(|(_, bytes)| {
33                let text = String::from_utf8_lossy(&bytes).into_owned();
34                let link_base =
35                    format!("/{}/blob/{}", name_for_blocking, summary.default_branch);
36                let image_base =
37                    format!("/{}/raw/{}", name_for_blocking, summary.default_branch);
38                crate::markdown::render(&text, &link_base, &image_base)
39            })
40        });
41        Ok((summary, commits, readme_html))
42    })
43    .await;
44
45    let (summary, commits, readme_html) = match result {
46        Ok(Ok(v)) => v,
47        Ok(Err(e)) => return not_found(&state, &name, e),
48        Err(e) => {
49            tracing::error!("repo_page join: {e}");
50            return (axum::http::StatusCode::INTERNAL_SERVER_ERROR, "render error")
51                .into_response();
52        }
53    };
54
55    render(
56        &state,
57        "repo.html",
58        &format!("/{name}"),
59        minijinja::context! {
60            repo => &summary,
61            commits => &commits,
62            readme_html => readme_html.map(minijinja::Value::from_safe_string),
63        },
64    )
65}