2025-09-14 16:19:44 -04:00
|
|
|
//! Hierarchical HTTP routing.
|
|
|
|
|
//!
|
|
|
|
|
//! Top level module establishes the overall
|
|
|
|
|
//! [`axum::Router`], and submodules organize nested subrouters into manageable
|
|
|
|
|
//! chunks. Pragmatically, the submodule tree should be kept fairly flat, lest
|
|
|
|
|
//! file paths grow exceedingly long. Deeply nested routers may still be
|
|
|
|
|
//! implemented, by use of the `super` keyword.
|
2025-08-13 18:52:37 -07:00
|
|
|
|
2025-09-14 16:19:44 -04:00
|
|
|
use axum::{
|
|
|
|
|
Router,
|
2025-10-01 22:36:19 -07:00
|
|
|
extract::State,
|
2025-09-14 16:19:44 -04:00
|
|
|
http::{HeaderValue, header::CACHE_CONTROL},
|
2025-10-01 22:36:19 -07:00
|
|
|
response::Redirect,
|
|
|
|
|
routing::get,
|
2025-09-14 16:19:44 -04:00
|
|
|
};
|
|
|
|
|
use tower::ServiceBuilder;
|
|
|
|
|
use tower_http::{
|
|
|
|
|
services::{ServeDir, ServeFile},
|
|
|
|
|
set_header::SetResponseHeaderLayer,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use crate::auth;
|
2025-09-23 13:08:51 -07:00
|
|
|
use crate::{app::App, settings::Settings};
|
2025-09-14 16:19:44 -04:00
|
|
|
|
2025-10-01 22:37:11 -07:00
|
|
|
mod forms;
|
2025-09-14 16:19:44 -04:00
|
|
|
mod relations_single;
|
|
|
|
|
mod workspaces_multi;
|
|
|
|
|
mod workspaces_single;
|
|
|
|
|
|
|
|
|
|
/// Create the root [`Router`] for the application, including nesting according
|
|
|
|
|
/// to the `root_path` [`crate::settings::Settings`] value, setting cache
|
|
|
|
|
/// headers, setting up static file handling, and defining fallback handlers.
|
|
|
|
|
pub(crate) fn new_router(app: App) -> Router<()> {
|
|
|
|
|
let root_path = app.settings.root_path.clone();
|
|
|
|
|
let router = Router::new()
|
|
|
|
|
.route(
|
|
|
|
|
"/",
|
|
|
|
|
get(
|
|
|
|
|
|State(Settings { root_path, .. }): State<Settings>| async move {
|
|
|
|
|
Redirect::to(&format!("{root_path}/workspaces/list/"))
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.nest("/workspaces", workspaces_multi::new_router())
|
2025-09-25 14:51:09 -07:00
|
|
|
.nest("/w", workspaces_single::new_router())
|
2025-10-01 22:37:11 -07:00
|
|
|
.nest("/f", forms::new_router())
|
2025-09-14 16:19:44 -04:00
|
|
|
.nest("/auth", auth::new_router())
|
2025-10-01 22:36:19 -07:00
|
|
|
.route("/__dev-healthz", get(|| async move { "ok" }))
|
2025-09-14 16:19:44 -04:00
|
|
|
.layer(SetResponseHeaderLayer::if_not_present(
|
|
|
|
|
CACHE_CONTROL,
|
|
|
|
|
HeaderValue::from_static("no-cache"),
|
|
|
|
|
))
|
|
|
|
|
.nest_service(
|
|
|
|
|
"/js_dist",
|
|
|
|
|
ServiceBuilder::new()
|
|
|
|
|
.layer(SetResponseHeaderLayer::if_not_present(
|
|
|
|
|
CACHE_CONTROL,
|
2025-12-11 03:50:10 +00:00
|
|
|
HeaderValue::from_static(if cfg!(debug_assertions) {
|
|
|
|
|
// Disable caching when developing locally.
|
|
|
|
|
"no-cache"
|
|
|
|
|
} else {
|
|
|
|
|
"max-age=120, stale-while-revalidate=86400"
|
|
|
|
|
}),
|
2025-09-14 16:19:44 -04:00
|
|
|
))
|
|
|
|
|
.service(
|
|
|
|
|
ServeDir::new("js_dist").not_found_service(
|
|
|
|
|
ServiceBuilder::new()
|
|
|
|
|
.layer(SetResponseHeaderLayer::if_not_present(
|
|
|
|
|
CACHE_CONTROL,
|
2025-12-11 03:50:10 +00:00
|
|
|
HeaderValue::from_static(if cfg!(debug_assertions) {
|
|
|
|
|
// Disable caching when developing locally.
|
|
|
|
|
"no-cache"
|
|
|
|
|
} else {
|
|
|
|
|
"max-age=120, stale-while-revalidate=86400"
|
|
|
|
|
}),
|
2025-09-14 16:19:44 -04:00
|
|
|
))
|
|
|
|
|
.service(ServeFile::new("static/_404.html")),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.nest_service(
|
|
|
|
|
"/css_dist",
|
|
|
|
|
ServiceBuilder::new()
|
|
|
|
|
.layer(SetResponseHeaderLayer::if_not_present(
|
|
|
|
|
CACHE_CONTROL,
|
2025-12-11 03:50:10 +00:00
|
|
|
HeaderValue::from_static(if cfg!(debug_assertions) {
|
|
|
|
|
// Disable caching when developing locally.
|
|
|
|
|
"no-cache"
|
|
|
|
|
} else {
|
|
|
|
|
"max-age=120, stale-while-revalidate=86400"
|
|
|
|
|
}),
|
2025-09-14 16:19:44 -04:00
|
|
|
))
|
|
|
|
|
.service(
|
|
|
|
|
ServeDir::new("css_dist").not_found_service(
|
|
|
|
|
ServiceBuilder::new()
|
|
|
|
|
.layer(SetResponseHeaderLayer::if_not_present(
|
|
|
|
|
CACHE_CONTROL,
|
|
|
|
|
HeaderValue::from_static("no-cache"),
|
|
|
|
|
))
|
|
|
|
|
.service(ServeFile::new("static/_404.html")),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.fallback_service(
|
|
|
|
|
ServiceBuilder::new()
|
|
|
|
|
.layer(SetResponseHeaderLayer::if_not_present(
|
|
|
|
|
CACHE_CONTROL,
|
2025-12-11 03:50:10 +00:00
|
|
|
HeaderValue::from_static(if cfg!(debug_assertions) {
|
|
|
|
|
// Disable caching when developing locally.
|
|
|
|
|
"no-cache"
|
|
|
|
|
} else {
|
|
|
|
|
"max-age=120, stale-while-revalidate=86400"
|
|
|
|
|
}),
|
2025-09-14 16:19:44 -04:00
|
|
|
))
|
|
|
|
|
.service(
|
|
|
|
|
ServeDir::new("static").not_found_service(
|
|
|
|
|
ServiceBuilder::new()
|
|
|
|
|
.layer(SetResponseHeaderLayer::if_not_present(
|
|
|
|
|
CACHE_CONTROL,
|
2025-12-11 03:50:10 +00:00
|
|
|
// Do not allow caching of paths if they return
|
|
|
|
|
// a 404 error.
|
2025-09-14 16:19:44 -04:00
|
|
|
HeaderValue::from_static("no-cache"),
|
|
|
|
|
))
|
|
|
|
|
.service(ServeFile::new("static/_404.html")),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.with_state(app);
|
|
|
|
|
if root_path.is_empty() {
|
|
|
|
|
router
|
|
|
|
|
} else {
|
|
|
|
|
Router::new()
|
|
|
|
|
.nest(&root_path, router)
|
|
|
|
|
.fallback(|| async move { Redirect::to(&root_path) })
|
|
|
|
|
}
|
|
|
|
|
}
|