wayver's git archive


a simple self-hosted git server
git clone https://git.wayver.dev/bile

src/handlers/git.rs@bd1a8f79badd439ab7a73e193be91ad175aafa67

raw
Date Commit Message Author Files + -
2026-02-17 21:07 initial mvp wayverd 74 10800 0
...

1use axum::{
2    extract::Path,
3    http::{HeaderValue, StatusCode, Uri, header},
4    response::{IntoResponse as _, Response},
5};
6
7use crate::utils::{
8    Error, Result, error::Context as _, extractor::repo_name_checks, git::Repository,
9    spawn_blocking,
10};
11
12#[tracing::instrument(skip_all)]
13pub async fn get_1(uri: Uri, Path(repo_name): Path<String>) -> Response {
14    spawn_blocking(move || inner(&uri, &repo_name).into_response()).await
15}
16
17#[tracing::instrument(skip_all)]
18pub async fn get_2(uri: Uri, Path((repo_name, _)): Path<(String, String)>) -> Response {
19    spawn_blocking(move || inner(&uri, &repo_name).into_response()).await
20}
21
22fn inner(uri: &Uri, repo_name: &str) -> Result {
23    repo_name_checks(repo_name)?;
24
25    let Some(repo) = Repository::open(repo_name).context("opening repository")? else {
26        return Err(Error::new(StatusCode::NOT_FOUND, "repo does not exist"));
27    };
28
29    let path = uri
30        .path()
31        .strip_prefix(&format!("/{repo_name}/"))
32        .unwrap_or_default();
33
34    let path = repo.path().join(path);
35
36    // cant canonicalize if it doesnt exist
37    if !path.exists() {
38        return Err(Error::new(
39            StatusCode::NOT_FOUND,
40            "This page does not exist.",
41        ));
42    }
43
44    let path = path.canonicalize().context("canonicalize new path")?;
45
46    // that path got us outside of the repository structure somehow
47    if !path.starts_with(repo.path()) {
48        tracing::warn!("Attempt to acces file outside of repo dir: {:?}", path);
49        return Err(Error::new(
50            StatusCode::FORBIDDEN,
51            "You do not have access to this file.",
52        ));
53    }
54
55    // Either the requested resource does not exist or it is not
56    // a file, i.e. a directory.
57    if !path.is_file() {
58        return Err(Error::new(
59            StatusCode::NOT_FOUND,
60            "This page does not exist.",
61        ));
62    }
63
64    let body = std::fs::read(&path).context("reading file")?;
65
66    Ok((
67        StatusCode::OK,
68        [(
69            header::CONTENT_TYPE,
70            HeaderValue::from_static(mime::APPLICATION_OCTET_STREAM.as_ref()),
71        )],
72        body,
73    )
74        .into_response())
75}
76