🔥 Okibi

yuki / okibi

焚き火の仲間だけの自前Gitホスティング(このサイト自身)

git clone https://okibi.fly.dev/yuki/okibi.git

okibi / src / main.rs

mod auth;
mod ci;
mod config;
mod db;
mod email;
mod git_http;
mod mcp;
mod repos;
mod util;
mod web;

use axum::routing::{get, post};
use axum::Router;
use config::Config;
use db::Db;

/// Shared application state.
#[derive(Clone)]
pub struct AppState {
    pub db: Db,
    pub cfg: Config,
}

async fn healthz() -> &'static str {
    "ok\n"
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    tracing_subscriber::fmt()
        .with_env_filter(
            tracing_subscriber::EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| "okibi=info,tower_http=warn".into()),
        )
        .init();

    let cfg = Config::from_env();
    std::fs::create_dir_all(&cfg.repos_dir).ok();
    let db = db::init(&cfg.db_path, &cfg.seed_members).await?;
    tracing::info!("members seeded: {:?}", cfg.seed_members);

    let state = AppState { db, cfg: cfg.clone() };

    let app = Router::new()
        .route("/", get(web::index))
        .route("/about", get(web::about))
        .route("/mcp", get(mcp::mcp_info).post(mcp::mcp_post))
        .route("/healthz", get(healthz))
        .route("/login", get(web::login_form).post(web::login_submit))
        .route("/auth/verify", get(web::verify))
        .route("/auth/bootstrap", get(web::bootstrap))
        .route("/members", get(web::members_page))
        .route("/members/invite", post(web::invite_submit))
        .route("/admin/import", post(web::admin_import))
        .route("/logout", post(web::logout))
        .route("/new", get(web::new_repo_form).post(web::new_repo_submit))
        .route("/settings", get(web::settings))
        .route("/settings/tokens", post(web::new_token))
        // git smart-HTTP (must come before the generic repo view)
        .route("/{owner}/{repo}/info/refs", get(git_http::info_refs))
        .route("/{owner}/{repo}/git-upload-pack", post(git_http::upload_pack))
        .route("/{owner}/{repo}/git-receive-pack", post(git_http::receive_pack))
        // web views
        .route("/{owner}", get(web::user_view))
        .route("/{owner}/{repo}", get(web::repo_view))
        .with_state(state);

    let listener = tokio::net::TcpListener::bind(&cfg.bind).await?;
    tracing::info!("🔥 Okibi listening on {} (base {})", cfg.bind, cfg.base_url);
    axum::serve(listener, app).await?;
    Ok(())
}