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
1use axum::{
2 extract::State,
3 http::{header, StatusCode},
4 response::{IntoResponse, Response},
5 routing::get,
6 Router,
7};
8
9use crate::AppState;
10
11pub fn router() -> Router<AppState> {
12 Router::new()
13 .route("/favicon.ico", get(favicon))
14 .route("/favicon.svg", get(favicon))
15 .route("/robots.txt", get(robots))
16}
17
18/// Serve the favicon from the built `dist/` directory. Vite copies anything in
19/// `frontend/static_src/public/` into the build output, so `dist/favicon.svg`
20/// is what ships in the runtime image (the `frontend/` source tree is not).
21async fn favicon(State(state): State<AppState>) -> Response {
22 let full = state.config.root.join("dist/favicon.svg");
23 match std::fs::read(&full) {
24 Ok(data) => {
25 let mut resp = data.into_response();
26 resp.headers_mut()
27 .insert(header::CONTENT_TYPE, "image/svg+xml".parse().unwrap());
28 resp.headers_mut().insert(
29 header::CACHE_CONTROL,
30 "public, max-age=86400".parse().unwrap(),
31 );
32 resp
33 }
34 Err(_) => (StatusCode::NOT_FOUND, "no favicon").into_response(),
35 }
36}
37
38async fn robots() -> Response {
39 let body = "User-agent: *\nAllow: /\n";
40 let mut resp = body.into_response();
41 resp.headers_mut().insert(
42 header::CONTENT_TYPE,
43 "text/plain; charset=utf-8".parse().unwrap(),
44 );
45 resp
46}