101 lines
3.6 KiB
Rust
101 lines
3.6 KiB
Rust
use dioxus::{
|
|
cli_config,
|
|
fullstack::axum::{self, Router, middleware::from_fn},
|
|
prelude::{DioxusRouterExt, ServeConfig},
|
|
server::axum::Extension,
|
|
};
|
|
use tokio::net::TcpListener;
|
|
use tower_http::services::ServeFile;
|
|
|
|
use crate::App;
|
|
use crate::app::LOGO_ICO;
|
|
use crate::server::{
|
|
auth::build_auth_layer, config::Config, database, key_val_store,
|
|
require_auth_mw::require_auth_middleware,
|
|
};
|
|
use crate::util::error::{Contextualize, Error, ErrorType, Result};
|
|
|
|
pub async fn main() -> Result<std::convert::Infallible> {
|
|
if let Err(e) = dotenvy::dotenv() {
|
|
tracing::warn!("Error reading .env: {e}");
|
|
}
|
|
|
|
tracing::debug!("Loading configuration...");
|
|
let config = Config::from_env()
|
|
.map_err(|e| Error::message_here(e.to_string()))
|
|
.err_context("Failed to load config")?;
|
|
|
|
tracing::debug!("Loaded configuration: {config:#?}");
|
|
|
|
// 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")?;
|
|
|
|
let key_val_pool = key_val_store::setup(&config.key_val_store.connection_uri())
|
|
.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);
|
|
|
|
#[cfg(debug_assertions)]
|
|
if let Some(dx_port) = cli_config::server_port()
|
|
&& dx_port != config.server.port
|
|
{
|
|
tracing::warn!(
|
|
"Your configured server port ({}) doesn't match the one specified from environment \
|
|
variables set by the Dioxus CLI ({dx_port}). If you are intending to use the dx tool, \
|
|
please do not specify a port in the configuration.",
|
|
config.server.port
|
|
);
|
|
}
|
|
|
|
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));
|
|
|
|
tracing::info!("Listening on {addr}...");
|
|
let listener = TcpListener::bind(addr)
|
|
.await
|
|
.map_err(|e| ErrorType::HttpServer(e.to_string()))
|
|
.err_context(format!("Failed to bind to {addr}"))?;
|
|
|
|
axum::serve(listener, router)
|
|
.await
|
|
.map_err(|e| ErrorType::HttpServer(e.to_string()))
|
|
.err_context("HTTP server error")?;
|
|
|
|
Err(Error::new_here(ErrorType::HttpServer(
|
|
"axum::serve should never return".to_owned(),
|
|
)))
|
|
}
|