feat(gateway): serve the mod jar under GET /downloads/*

This commit is contained in:
loki5512344 2026-09-29 18:39:39 +02:00
parent 192ef52eed
commit 54af71589a
Signed by: boba
GPG key ID: 253067914055423B
8 changed files with 127 additions and 2 deletions

View file

@ -9,6 +9,9 @@ pub struct Config {
pub accounts_grpc_url: String,
pub configs_http_url: String,
pub site_origin: String,
/// Directory served read-only under `GET /downloads/*` — today just
/// `lovisual.jar`, the mod's direct download (see TODO.md «Скачивание»).
pub downloads_dir: String,
/// Behind a reverse proxy (nginx/caddy) that appends the client IP to
/// X-Forwarded-For. Never enable when the gateway is exposed directly.
pub trust_proxy: bool,
@ -31,6 +34,7 @@ impl Config {
accounts_grpc_url: var("ACCOUNTS_GRPC_URL")?,
configs_http_url: var("CONFIGS_HTTP_URL")?,
site_origin: var("SITE_ORIGIN")?,
downloads_dir: std::env::var("DOWNLOADS_DIR").unwrap_or_else(|_| "downloads".into()),
trust_proxy: std::env::var("TRUST_PROXY").is_ok_and(|v| v == "true"),
})
}
@ -68,6 +72,7 @@ mod tests {
accounts_grpc_url: String::new(),
configs_http_url: String::new(),
site_origin: "http://localhost:5173".into(),
downloads_dir: "downloads".into(),
trust_proxy: false,
}
}

View file

@ -15,7 +15,7 @@ use axum::{
use config::Config;
use identity::device::DeviceAuthenticator;
use std::sync::Arc;
use tower_http::cors::CorsLayer;
use tower_http::{cors::CorsLayer, services::ServeDir};
pub fn build_app(cfg: &Config, devices: Arc<dyn DeviceAuthenticator>) -> Router {
let upstreams = Arc::new(proxy::forward::Upstreams::new(cfg).expect("valid upstream config"));
@ -45,6 +45,14 @@ pub fn build_app(cfg: &Config, devices: Arc<dyn DeviceAuthenticator>) -> Router
// limit before it gets rejected, and CORS preflights skip all of it.
Router::new()
.route("/health", get(|| async { "ok" }))
// The mod jar: one flat file from DOWNLOADS_DIR, streamed (no buffering
// of a ~60 MB body in memory). ServeDir resolves paths inside that
// directory only and 404s anything else; the dot-segment guard above
// already refuses `..`.
.nest_service(
"/downloads",
ServeDir::new(&cfg.downloads_dir).append_index_html_on_directories(false),
)
.fallback(proxy::forward::proxy)
.with_state(upstreams)
.layer(axum::middleware::from_fn(identity::authorize))