mod config;
mod handlers;
mod network;
use config::*;
use network::error::Error as NetworkError;
use windmark::{context::RouteContext, response::Response};
fn user(cfg: &Config, ctx: RouteContext) -> Response {
let username: &str = ctx.parameters.get("user").expect("Path segment");
route(handlers::user::handler(cfg, username))
}
fn user_repo(cfg: &Config, ctx: RouteContext) -> Response {
let username: &str = ctx.parameters.get("user").expect("Path segment");
let repo: &str = ctx.parameters.get("repo").expect("Path segment");
route(handlers::repo::handler(cfg, username, repo))
}
fn branch(_cfg: &Config, ctx: RouteContext) -> Response {
let _username: &str = ctx.parameters.get("user").expect("Path segment");
let _repo: &str = ctx.parameters.get("repo").expect("Path segment");
let _branch: &str = ctx.parameters.get("branch").expect("Path segment");
Response::temporary_failure("That route is not implemented yet")
}
fn file(_cfg: &Config, ctx: RouteContext) -> Response {
let _username: &str = ctx.parameters.get("user").expect("Path segment");
let _repo: &str = ctx.parameters.get("repo").expect("Path segment");
let _branch: &str = ctx.parameters.get("branch").expect("Path segment");
let _file_path: &str = ctx.parameters.get("path").expect("Path segment");
Response::temporary_failure("That route is not implemented yet")
}
#[windmark::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let cfg: &Config = config();
let kind = cfg.forge_api().kind().to_string();
match cfg.forge_api().get_version() {
Ok(server_version) => {
let version = server_version.version;
println!("{kind} {version}");
}
Err(NetworkError::Unauthorized) => {
println!("Warning: We are not authorized to query metadata of this {kind} instance. Some routes may return errors.");
}
Err(err) => Err(err)?, };
windmark::router::Router::new()
.set_private_key_file(format!("{}/key.pem", cfg.certs_dir))
.set_certificate_file(format!("{}/cert.pem", cfg.certs_dir))
.add_header(move |_| format!("# {kind} Proxy"))
.add_footer(|ctx: RouteContext| {
let base: &str = ctx.url.path().split("/").collect::<Vec<&str>>()[1];
let should_list_users = cfg.forge_list_users;
let mode_dest: &str = if base == "users" {
"=> / Recent Activity\n\n"
} else if should_list_users {
"=> /users Users\n\n"
} else {
"\n"
};
let version = cfg.crate_version;
return format!("\n==========================\n{mode_dest}=> gemini://git.average.name/AverageHelper/git-gemini-forge Powered by git-gemini-forge v{version}")
})
.mount("/", |_| route(handlers::root::handler(cfg)))
.mount("/users", |_| route(handlers::users::handler(cfg)))
.mount("/users/", |_| route(handlers::users::handler(cfg)))
.mount("/:user", |ctx| user(cfg, ctx))
.mount("/:user/", |ctx| user(cfg, ctx))
.mount("/:user/:repo", |ctx| user_repo(cfg, ctx))
.mount("/:user/:repo/", |ctx| user_repo(cfg, ctx))
.mount("/:user/:repo/src/branch/:branch", |ctx| branch(cfg, ctx))
.mount("/:user/:repo/src/branch/:branch/", |ctx| branch(cfg, ctx))
.mount("/:user/:repo/src/branch/:branch/*path", |ctx| {
file(cfg, ctx)
})
.set_error_handler(|error| {
let failure_addr = error.url;
eprintln!("Failed to serve {failure_addr}");
Response::temporary_failure("Something went wrong!")
})
.run()
.await
}
fn route(res: Result<Response, NetworkError>) -> Response {
return match res {
Ok(res) => res,
Err(NetworkError::NetworkFailure) => {
Response::proxy_error("Couldn't communicate with the forge")
}
Err(NetworkError::ResourceNotFound) => Response::not_found("Not found"),
Err(NetworkError::Unauthorized) => Response::proxy_error(
"We aren't authorized to communicate with the forge on this route",
),
Err(NetworkError::UnexpectedResponse) => {
Response::temporary_failure("The upstream gave us a response we don't understand")
}
};
}