a73x

07c63032

wire up HTTP + SSH concurrent startup in main

a73x   2026-03-31 13:26


diff --git a/src/server/main.rs b/src/server/main.rs
index dfe6648..6fadd41 100644
--- a/src/server/main.rs
+++ b/src/server/main.rs
@@ -1,8 +1,97 @@
use std::path::PathBuf;

use clap::Parser;
use tracing::info;

mod config;
mod http;
mod repos;
mod ssh;

fn main() {
    println!("git-collab-server: not yet implemented");
#[derive(Parser)]
#[command(name = "git-collab-server", about = "Minimal git hosting server")]
struct Args {
    #[arg(short, long)]
    config: PathBuf,
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt::init();

    let args = Args::parse();

    let config = match config::ServerConfig::from_file(&args.config) {
        Ok(c) => c,
        Err(e) => {
            eprintln!("Failed to load config from {:?}: {}", args.config, e);
            std::process::exit(1);
        }
    };

    if !config.repos_dir.exists() {
        eprintln!("repos_dir {:?} does not exist", config.repos_dir);
        std::process::exit(1);
    }

    let host_key_path = config.repos_dir.join(".server").join("host_key");
    let host_key = match ssh::load_or_generate_host_key(&host_key_path) {
        Ok(k) => k,
        Err(e) => {
            eprintln!("Failed to load/generate SSH host key: {}", e);
            std::process::exit(1);
        }
    };

    let fingerprint = match host_key.clone_public_key() {
        Ok(pub_key) => pub_key.fingerprint(),
        Err(e) => {
            eprintln!("Failed to get host key fingerprint: {}", e);
            std::process::exit(1);
        }
    };

    info!("HTTP listening on {}", config.http_bind);
    info!("SSH listening on {}", config.ssh_bind);
    info!("Repos dir: {:?}", config.repos_dir);
    info!("Site title: {}", config.site_title);
    info!("SSH host key fingerprint: SHA256:{}", fingerprint);

    let app_state = http::AppState {
        repos_dir: config.repos_dir.clone(),
        site_title: config.site_title.clone(),
    };
    let router = http::router(app_state);

    let ssh_config = ssh::session::SshServerConfig {
        repos_dir: config.repos_dir.clone(),
        authorized_keys_path: config.authorized_keys.clone(),
    };

    let http_bind = config.http_bind;
    let ssh_bind = config.ssh_bind;

    let http_listener = match tokio::net::TcpListener::bind(http_bind).await {
        Ok(l) => l,
        Err(e) => {
            eprintln!("Failed to bind HTTP on {}: {}", http_bind, e);
            std::process::exit(1);
        }
    };

    let http_fut = axum::serve(http_listener, router);
    let ssh_fut = ssh::serve(ssh_bind, host_key, ssh_config);

    tokio::select! {
        result = http_fut => {
            if let Err(e) = result {
                eprintln!("HTTP server error: {}", e);
            }
        }
        result = ssh_fut => {
            if let Err(e) = result {
                eprintln!("SSH server error: {}", e);
            }
        }
    }
}