Compare commits

...

3 Commits

Author SHA1 Message Date
c4cba925a7 Manually serve static files
All checks were successful
Push Workflows / rustfmt (push) Successful in 6s
Push Workflows / tailwind-build (push) Successful in 6s
Push Workflows / docs (push) Successful in 20s
Push Workflows / test (push) Successful in 24s
Push Workflows / clippy (push) Successful in 18s
Push Workflows / build (push) Successful in 46s
Push Workflows / nix-build (push) Successful in 5m11s
Remove unnecessary paths from require_auth_mw due to layer ordering
2026-07-19 10:53:34 -04:00
dcbac8a9ba Enable precompression 2026-07-19 10:53:34 -04:00
5e498195ce Move router setup to build_router 2026-07-19 10:53:28 -04:00
3 changed files with 56 additions and 45 deletions

View File

@@ -18,3 +18,6 @@ script = []
# Javascript code file
# serve: [dev-server] only
script = []
[web]
pre_compress = true

View File

@@ -4,16 +4,65 @@ use dioxus::{
server::axum::Extension,
};
use tokio::net::TcpListener;
use tower_http::services::ServeFile;
use tower_http::services::{ServeDir, ServeFile};
use crate::App;
use crate::app::LOGO_ICO;
use crate::server::{
auth::build_auth_layer, config::Config, database, key_val_store,
auth::build_auth_layer,
config::Config,
database::{self, DbPool},
key_val_store::{self, KeyValPool},
require_auth_mw::require_auth_middleware,
};
use crate::util::error::{Contextualize, Error, ErrorType, Result};
// Build the `axum::Router` and attach config, database, and key/val store as extensions
pub fn build_router(config: Config, db_pool: DbPool, key_val_pool: KeyValPool) -> Router {
let favicon_path = {
// Resolve the favicon path
let asset_path = LOGO_ICO.resolve();
// If the asset path starts with "/", strip it. Otherwise it behaves as an "absolute path"
// and replaces the base path in the join operation. This is necessary because Dioxus will
// produce a path like "/assets/" in the call to `resolve`
let asset_path_rel = asset_path.strip_prefix("/").unwrap_or(&asset_path);
config.server.public_path.join(asset_path_rel)
};
let auth_layer = build_auth_layer(db_pool.clone(), key_val_pool, config.auth.cookies_secure);
let public_path = config.server.public_path.clone();
let serve_assets = ServeDir::new(public_path.join("assets"));
let serve_index = ServeFile::new(public_path.join("index.html"));
let serve_favicon = ServeFile::new(favicon_path);
let router = Router::new()
.serve_api_application(ServeConfig::new(), App)
.layer(from_fn(require_auth_middleware))
.layer(Extension(config))
.layer(Extension(db_pool))
.layer(auth_layer);
// In release mode, serve precompressed assets. In debug mode, serve WASM patch files.
if cfg!(debug_assertions) {
let serve_wasm = ServeDir::new(public_path.join("wasm"));
router
.nest_service("/wasm", serve_wasm)
.nest_service("/assets", serve_assets)
.nest_service("/index.html", serve_index)
.nest_service("/favicon.ico", serve_favicon)
} else {
router
.nest_service("/assets", serve_assets.precompressed_br())
.nest_service("/index.html", serve_index)
.nest_service("/favicon.ico", serve_favicon.precompressed_br())
}
}
pub async fn main() -> Result<std::convert::Infallible> {
if let Err(e) = dotenvy::dotenv() {
tracing::warn!("Error reading .env: {e}");
@@ -27,14 +76,6 @@ pub async fn main() -> Result<std::convert::Infallible> {
tracing::debug!("Loaded configuration: {config:#?}");
config.log_warnings();
// Dioxus doesn't expose a way to configure the public path other than this environment
// variable, and also doesn't provide a way to read what the public path is. As a workaround we
// expose a config option and set this variable that Dioxus expects.
// SAFETY: "This function is safe to call in a single-threaded program."
unsafe {
std::env::set_var("DIOXUS_PUBLIC_PATH", config.server.public_path.clone());
}
let db_pool = database::setup(config.database.connection_uri())
.await
.err_context("Failed database setup")?;
@@ -43,33 +84,10 @@ pub async fn main() -> Result<std::convert::Infallible> {
.await
.err_context("Failed key-value store setup")?;
let favicon_path = {
// Resolve the favicon path
let asset_path = LOGO_ICO.resolve();
// If the asset path starts with "/", strip it. Otherwise it behaves as an "absolute path"
// and replaces the base path in the join operation. This is necessary because Dioxus will
// produce a path like "/assets/" in the call to `resolve`
let asset_path_rel = asset_path.strip_prefix("/").unwrap_or(&asset_path);
config.server.public_path.join(asset_path_rel)
};
tracing::debug!("Favicon path: {}", favicon_path.display());
let auth_layer = build_auth_layer(db_pool.clone(), key_val_pool, config.auth.cookies_secure);
let addr = config.server.serve_addr();
tracing::info!("Setup complete, building router...");
let router = Router::new()
.serve_dioxus_application(ServeConfig::new(), App)
.layer(from_fn(require_auth_middleware))
.layer(Extension(config))
.layer(Extension(db_pool))
.layer(auth_layer)
.nest_service("/favicon.ico", ServeFile::new(favicon_path));
let router = build_router(config, db_pool, key_val_pool);
tracing::info!("Listening on {addr}...");
let listener = TcpListener::bind(addr)

View File

@@ -7,12 +7,6 @@ use dioxus::prelude::*;
use crate::server::auth::AuthSession;
#[cfg(not(debug_assertions))]
const ALLOWED_PATH_PREFIX: [&str; 1] = ["/assets/"];
#[cfg(debug_assertions)]
const ALLOWED_PATH_PREFIX: [&str; 2] = ["/assets/", "/wasm/"];
const ALLOWED_PATHS: [&str; 6] = [
"/login",
"/signup",
@@ -29,11 +23,7 @@ pub async fn require_auth_middleware(
) -> Result<Response<Body>, (StatusCode, &'static str)> {
let path = req.uri().path();
if ALLOWED_PATH_PREFIX
.iter()
.any(|prefix| path.starts_with(prefix))
|| ALLOWED_PATHS.contains(&path)
{
if ALLOWED_PATHS.contains(&path) {
let response = next.run(req).await;
return Ok(response);
}