94 lines
2.7 KiB
Rust
94 lines
2.7 KiB
Rust
use std::{future::ready, net::SocketAddr, path::PathBuf, sync::Arc};
|
|
|
|
use anyhow::{Context, bail};
|
|
use axum::{
|
|
Router,
|
|
http::StatusCode,
|
|
middleware::{self},
|
|
routing::{get, post},
|
|
};
|
|
use clap::Parser;
|
|
use tokio::sync::Mutex;
|
|
use tower_http::{compression::CompressionLayer, services::ServeDir, trace::TraceLayer};
|
|
use tracing_subscriber::EnvFilter;
|
|
|
|
mod flatpak;
|
|
mod upload;
|
|
mod util;
|
|
|
|
#[derive(Default)]
|
|
struct SrvState {
|
|
repo: Mutex<PathBuf>,
|
|
}
|
|
|
|
type SharedState = Arc<SrvState>;
|
|
|
|
#[derive(Parser)]
|
|
struct Opt {
|
|
/// IP and port to bind to.
|
|
#[clap(long, env = "FR_BIND", default_value = "127.0.0.1:3000")]
|
|
bind: SocketAddr,
|
|
|
|
/// Path to the ostree repository directory
|
|
#[clap(long, env = "FR_REPO_PATH")]
|
|
repo: PathBuf,
|
|
|
|
#[clap(long, env = "RUST_LOG", default_value = "debug")]
|
|
log_level: String,
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() -> anyhow::Result<()> {
|
|
let opt = Opt::parse();
|
|
|
|
tracing_subscriber::fmt()
|
|
.with_env_filter(EnvFilter::new(&opt.log_level))
|
|
.init();
|
|
|
|
// TODO: Create an empty archive-z2 repository
|
|
// ostree init --repo=~/my-apps --mode=archive-z2
|
|
|
|
// Sanity check that opt.repo looks like a valid ostree dir
|
|
let repo = opt.repo;
|
|
let repo_looks_valid =
|
|
repo.is_dir() && repo.join("objects").is_dir() && repo.join("refs").is_dir();
|
|
if !repo_looks_valid {
|
|
bail!("{repo:?} must be a valid ostree directory.")
|
|
}
|
|
|
|
let serve_dir = ServeDir::new(&repo);
|
|
|
|
let state = SrvState {
|
|
repo: Mutex::new(repo.clone()),
|
|
};
|
|
let state: SharedState = Arc::new(state);
|
|
|
|
let app = Router::new()
|
|
.route("/flatpak-bundle/upload", post(upload::flatpak_bundle))
|
|
// Explicitly deny access to internal OSTree directories.
|
|
.route("/state/{*path}", get(|| ready(StatusCode::NOT_FOUND)))
|
|
.route("/tmp/{*path}", get(|| ready(StatusCode::NOT_FOUND)))
|
|
// Optional: serve a .flatpakrepo descriptor at a known endpoint.
|
|
.route(
|
|
"/repo.flatpakrepo",
|
|
get(move || flatpak::serve_flatpakrepo(repo.clone())),
|
|
)
|
|
// Everything else is served from the repo directory.
|
|
.fallback_service(serve_dir)
|
|
// Add Flatpak-specific cache headers and content types.
|
|
.layer(middleware::from_fn(flatpak::flatpak_headers_middleware))
|
|
// Gzip compression for refs and small files.
|
|
.layer(CompressionLayer::new())
|
|
// Request logging.
|
|
.layer(TraceLayer::new_for_http())
|
|
.with_state(state);
|
|
|
|
let listener = tokio::net::TcpListener::bind(&opt.bind)
|
|
.await
|
|
.context("Failed to bind tcp listener")?;
|
|
|
|
tracing::info!("Listening on http://{}", opt.bind);
|
|
axum::serve(listener, app).await?;
|
|
Ok(())
|
|
}
|