use std::sync::Arc;
use axum::{
Extension, Router,
http::StatusCode,
middleware,
routing::{delete, get, post},
};
use tower_http::timeout::TimeoutLayer;
use utoipa::OpenApi as _;
use utoipa_swagger_ui::SwaggerUi;
use super::ServerState;
use super::api_doc::ApiDoc;
use super::auth_middleware::{
AuthRateLimiter, rate_limit, strip_body_for_logs,
};
use super::handlers;
pub fn build_router(state: Arc<ServerState>) -> Router {
let api = Router::new()
.route("/sessions", get(handlers::get_sessions))
.route("/analysis/images", get(handlers::get_image_analysis))
.route("/configurations", get(handlers::get_configurations))
.route("/nodes", get(handlers::get_nodes))
.route("/groups", get(handlers::get_groups))
.route("/groups/available", get(handlers::get_available_groups))
.route("/images", get(handlers::get_images))
.route("/templates", get(handlers::get_templates))
.route("/boot-parameters", get(handlers::get_boot_parameters))
.route("/kernel-parameters", get(handlers::get_kernel_parameters))
.route("/redfish-endpoints", get(handlers::get_redfish_endpoints))
.route("/groups/nodes", get(handlers::get_groups_nodes))
.route("/groups/hardware", get(handlers::get_groups_hardware))
.route("/clusters", get(handlers::get_clusters_deprecated))
.route(
"/hardware-clusters",
get(handlers::get_hardware_clusters_deprecated),
)
.route(
"/hardware-nodes-list",
get(handlers::get_hardware_nodes_list),
)
.route("/nodes", post(handlers::add_node))
.route("/nodes/{id}", delete(handlers::delete_node))
.route("/groups", post(handlers::create_group))
.route("/groups/{label}", delete(handlers::delete_group))
.route(
"/groups/{name}/members",
post(handlers::add_nodes_to_group).delete(handlers::delete_group_members),
)
.route(
"/boot-parameters",
post(handlers::add_boot_parameters)
.put(handlers::update_boot_parameters)
.delete(handlers::delete_boot_parameters),
)
.route(
"/redfish-endpoints",
post(handlers::add_redfish_endpoint)
.put(handlers::update_redfish_endpoint),
)
.route(
"/redfish-endpoints/{id}",
delete(handlers::delete_redfish_endpoint),
)
.route("/sessions/{name}", delete(handlers::delete_session))
.route("/sessions", post(handlers::create_session))
.route("/images", delete(handlers::delete_images))
.route("/configurations", delete(handlers::delete_configurations))
.route("/boot-config", post(handlers::apply_boot_config))
.route(
"/kernel-parameters/apply",
post(handlers::apply_kernel_parameters),
)
.route(
"/kernel-parameters/add",
post(handlers::add_kernel_parameters),
)
.route(
"/kernel-parameters",
delete(handlers::delete_kernel_parameters),
)
.route("/migrate/nodes", post(handlers::migrate_nodes))
.route("/migrate/backup", post(handlers::migrate_backup))
.route("/migrate/restore", post(handlers::migrate_restore))
.route("/ephemeral-env", post(handlers::create_ephemeral_env))
.route("/power", post(handlers::post_power))
.route(
"/power/transitions/{id}",
get(handlers::get_power_transition),
)
.route(
"/templates/{name}/sessions",
post(handlers::post_template_session),
)
.route("/sessions/{name}/logs", get(handlers::get_session_logs))
.route(
"/sat-file/configurations",
post(handlers::post_sat_configuration),
)
.route(
"/sat-file/images/cfs-session",
post(handlers::post_sat_image_cfs_session),
)
.route(
"/sat-file/images/stamp",
post(handlers::post_sat_image_stamp),
)
.route(
"/sat-file/session-templates",
post(handlers::post_sat_session_template),
)
.route("/sat-file/validate", post(handlers::post_sat_validate))
.route("/health", get(handlers::health))
.route(
"/hardware-clusters/{target}/members",
post(handlers::add_hw_component).delete(handlers::delete_hw_component),
)
.route(
"/hardware-clusters/{target}/configuration",
post(handlers::apply_hw_configuration),
)
.merge(build_ws_routes())
.layer(TimeoutLayer::with_status_code(
StatusCode::REQUEST_TIMEOUT,
state.request_timeout,
));
let limiter = AuthRateLimiter::new();
let auth = Router::new()
.route("/token", post(handlers::auth_token))
.route("/validate", post(handlers::auth_validate))
.layer(middleware::from_fn(strip_body_for_logs))
.layer(middleware::from_fn_with_state(state.clone(), rate_limit))
.layer(Extension(limiter));
Router::new()
.nest("/api/v1", api)
.nest("/api/v1/auth", auth)
.merge(SwaggerUi::new("/docs").url("/openapi.json", ApiDoc::openapi()))
.layer(middleware::from_fn(add_hsts_header))
.with_state(state)
}
async fn add_hsts_header(
request: axum::extract::Request,
next: middleware::Next,
) -> axum::response::Response {
let mut response = next.run(request).await;
response.headers_mut().insert(
axum::http::header::STRICT_TRANSPORT_SECURITY,
axum::http::HeaderValue::from_static("max-age=31536000; includeSubDomains"),
);
response
}
fn build_ws_routes() -> Router<Arc<ServerState>> {
Router::new()
.route("/nodes/{xname}/console", get(handlers::console_node_ws))
.route(
"/sessions/{name}/console",
get(handlers::console_session_ws),
)
}