//! Ferrit — a minimal self-hosted Git service written in Rust.
//!
//! Ferrit is an independent, from-scratch reimplementation of the core ideas of
//! a self-hosted git forge: user accounts, repositories, a web code browser and
//! the git smart-HTTP transport for clone/fetch/push. It is not derived from any
//! other project's source code.
mod githttp;
mod gitio;
mod i18n;
mod store;
mod web;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
/// Shared application state handed to every request handler.
pub struct App {
pub store: store::Store,
/// session token -> user id
pub sessions: Mutex<HashMap<String, u64>>,
pub data_dir: PathBuf,
}
impl App {
pub fn current_user(&self, token: Option<&str>) -> Option<store::User> {
let token = token?;
let id = *self.sessions.lock().unwrap().get(token)?;
self.store.user_by_id(id)
}
pub fn start_session(&self, user_id: u64) -> String {
use rand::RngCore;
let mut buf = [0u8; 24];
rand::thread_rng().fill_bytes(&mut buf);
let token = hex::encode(buf);
self.sessions.lock().unwrap().insert(token.clone(), user_id);
token
}
pub fn end_session(&self, token: &str) {
self.sessions.lock().unwrap().remove(token);
}
}
#[tokio::main]
async fn main() {
let data_dir = std::env::var("FERRIT_DATA")
.map(PathBuf::from)
.unwrap_or_else(|_| PathBuf::from("data"));
std::fs::create_dir_all(data_dir.join("repos")).expect("create data dir");
let app = Arc::new(App {
store: store::Store::load(data_dir.join("db.json")),
sessions: Mutex::new(HashMap::new()),
data_dir,
});
let addr = std::env::var("FERRIT_ADDR").unwrap_or_else(|_| "127.0.0.1:3000".to_string());
let router = web::router(app.clone());
let listener = tokio::net::TcpListener::bind(&addr)
.await
.unwrap_or_else(|e| panic!("cannot bind {addr}: {e}"));
println!("Ferrit listening on http://{addr} (data dir: {:?})", app.data_dir);
axum::serve(listener, router).await.expect("server error");
}