ordinary-app 0.8.0

Application server for Ordinary
// Copyright (C) 2026 Ordinary Labs, LLC.
//
// SPDX-License-Identifier: AGPL-3.0-only

use crate::server::middleware::apply_custom_to_router;
use crate::server::{OrdinaryAppRouter, OrdinaryAppServerState, ops};
use axum::Router;
use axum::http::header::ETAG;
use axum::routing::get;
use ordinary_config::OrdinaryConfig;
use ordinary_utils::middleware::{modify_etag_for_encoding, x_via};
use std::sync::Arc;
use tower::ServiceBuilder;
use tower_http::set_header::SetResponseHeaderLayer;

#[allow(clippy::ref_option)]
pub(crate) fn setup_router(
    config: &Arc<OrdinaryConfig>,
    state: &Arc<OrdinaryAppServerState>,
    api_domain: &Option<String>,
    forwarded_by: &str,
    forwarded_proto: &str,
) -> Option<OrdinaryAppRouter> {
    if let Some(assets) = &config.assets {
        let mut router = Router::new();

        // todo: handle timeouts

        let base_route = if assets.base_route == "/" {
            ""
        } else {
            assets.base_route.as_str()
        };

        router = router.route(&format!("{base_route}/{{*path}}"), get(ops::assets::get));

        if assets.append_index_html == Some(true) && assets.skip_base_route_index_html != Some(true)
        {
            if base_route.is_empty() {
                router = router.route("/", get(ops::assets::get));
            } else {
                router = router
                    .route(base_route, get(ops::assets::get))
                    .route(&format!("{base_route}/"), get(ops::assets::get));
            }
        }

        if let Some(names) = &assets.middlewares {
            router = apply_custom_to_router(
                router,
                config,
                state,
                names,
                config.domain.clone(),
                forwarded_by.to_string(),
                forwarded_proto.to_string(),
                api_domain.clone(),
            );
        }

        router = router.route_layer(
            ServiceBuilder::new()
                .layer(SetResponseHeaderLayer::overriding(
                    ETAG,
                    modify_etag_for_encoding,
                ))
                .layer(axum::middleware::from_fn(x_via)),
        );

        Some(router)
    } else {
        None
    }
}