Initial commit

This commit is contained in:
2026-08-01 20:56:44 +02:00
commit 7840f38a7b
9 changed files with 2628 additions and 0 deletions
+93
View File
@@ -0,0 +1,93 @@
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(())
}