Ferrit Explore
中文·繁體·EN·日本語 Sign in Register
cielxl / ferrit / src / main.rs
//! 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");
}