//! HTTP handlers for daemon binary downloads.
//!
//! Serves pre-compiled daemon binaries for download. Binaries are read from
//! disk at a configurable path (default: `/app/daemon-binaries`), overridable
//! via the `DAEMON_BINARIES_DIR` environment variable.
use axum::{
extract::Path,
http::{header, StatusCode},
response::IntoResponse,
Json,
};
use serde::Serialize;
/// Default directory where daemon binaries are stored.
const DEFAULT_BINARIES_DIR: &str = "/app/daemon-binaries";
/// Supported platforms for daemon binary downloads.
const SUPPORTED_PLATFORMS: &[&str] = &[
"linux-x86_64",
"linux-arm64",
"macos-x86_64",
"macos-arm64",
];
/// Response for listing available daemon platforms.
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PlatformInfo {
/// Platform identifier (e.g., "linux-x86_64")
pub platform: String,
/// Whether a binary is available for this platform
pub available: bool,
/// Download URL path for this platform
pub download_url: String,
}
/// Response for the list platforms endpoint.
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ListPlatformsResponse {
/// List of supported platforms and their availability
pub platforms: Vec<PlatformInfo>,
}
/// Get the binaries directory from environment or use default.
fn get_binaries_dir() -> String {
std::env::var("DAEMON_BINARIES_DIR").unwrap_or_else(|_| DEFAULT_BINARIES_DIR.to_string())
}
/// Get the binary file path for a given platform.
fn get_binary_path(platform: &str) -> std::path::PathBuf {
let dir = get_binaries_dir();
std::path::PathBuf::from(dir).join(format!("makima-{}", platform))
}
/// List available daemon platforms and their download availability.
///
/// Returns a list of all supported platforms with availability status
/// based on whether the binary file exists on disk.
pub async fn list_daemon_platforms() -> impl IntoResponse {
let mut platforms = Vec::with_capacity(SUPPORTED_PLATFORMS.len());
for &platform in SUPPORTED_PLATFORMS {
let path = get_binary_path(platform);
let available = path.exists();
platforms.push(PlatformInfo {
platform: platform.to_string(),
available,
download_url: format!("/api/v1/daemon/download/{}", platform),
});
}
(
StatusCode::OK,
Json(ListPlatformsResponse { platforms }),
)
.into_response()
}
/// Download a daemon binary for the specified platform.
///
/// Reads the binary from disk and returns it with appropriate headers
/// for file download. Returns 404 if the binary is not available.
pub async fn download_daemon(
Path(platform): Path<String>,
) -> impl IntoResponse {
// Validate platform
if !SUPPORTED_PLATFORMS.contains(&platform.as_str()) {
return (
StatusCode::BAD_REQUEST,
Json(serde_json::json!({
"code": "INVALID_PLATFORM",
"message": format!(
"Unsupported platform '{}'. Supported platforms: {}",
platform,
SUPPORTED_PLATFORMS.join(", ")
)
})),
)
.into_response();
}
let binary_path = get_binary_path(&platform);
// Read binary from disk
let binary_data = match tokio::fs::read(&binary_path).await {
Ok(data) => data,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
return (
StatusCode::NOT_FOUND,
Json(serde_json::json!({
"code": "BINARY_NOT_FOUND",
"message": format!(
"Daemon binary for platform '{}' is not available for download",
platform
)
})),
)
.into_response();
}
Err(e) => {
tracing::error!(
platform = %platform,
path = %binary_path.display(),
error = %e,
"Failed to read daemon binary"
);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(serde_json::json!({
"code": "READ_ERROR",
"message": "Failed to read daemon binary"
})),
)
.into_response();
}
};
let filename = format!("makima-{}", platform);
// Return binary with download headers
(
StatusCode::OK,
[
(
header::CONTENT_TYPE,
"application/octet-stream".to_string(),
),
(
header::CONTENT_DISPOSITION,
format!("attachment; filename=\"{}\"", filename),
),
(
header::CONTENT_LENGTH,
binary_data.len().to_string(),
),
],
binary_data,
)
.into_response()
}