use std::sync::Arc;
use std::time::Duration;
use arcature::assets::{Assets, AssetsConfig};
use arcature::auth::{CsrfConfig, DbSessionStore, PasswordConfig, PasswordHasher, SessionConfig};
use arcature::http::{ErrorMapping, SecurityHeaders};
use arcature::inertia::{InertiaConfig, vite_root_document};
use arcature::jobs::Scheduler;
use arcature::prelude::*;
const JS_ENTRY: &str = "__JS_ENTRY__";
pub struct Bootstrapped {
pub application: Application<crate::bootstrap::AppState>,
pub state_fn: Arc<dyn Fn(&Resources, &Lifecycle) -> crate::bootstrap::AppState + Send + Sync>,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct BootOptions {
pub scheduler: bool,
}
pub fn app(options: BootOptions) -> Result<Bootstrapped> {
dotenvy::dotenv().ok();
let config = crate::config::load()?;
let hasher = Arc::new(
PasswordHasher::new(PasswordConfig::default()).map_err(|e| Error::Config(e.to_string()))?,
);
let assets_config = AssetsConfig::new();
let assets = Assets::detect(&assets_config).map_err(|e| Error::Config(e.to_string()))?;
let inertia = InertiaConfig::new(
env!("CARGO_PKG_VERSION"),
vite_root_document(&config.app_name, &assets, JS_ENTRY),
)
.map_err(|e| Error::Config(e.to_string()))?;
let mut session = if config.production {
SessionConfig::new(&config.app_key)
} else {
SessionConfig::dev(&config.app_key)
}
.map_err(|e| Error::Config(e.to_string()))?;
session = session.with_max_age(Duration::from_secs(60 * 60 * 8));
let sessions = DbSessionStore::connect_lazy(&config.database);
let state_fn = crate::bootstrap::state_fn(&config, hasher);
let mut builder = Application::new()
.routes(crate::routes::app_routes())
.merge_routes(crate::app::modules::routes())
.bind(&config.bind_addr)
.port(config.port)
.database(config.database)
.storage(config.storage)
.mail(config.mail)
.jobs(jobs_registry())
.compression()
.security_headers(SecurityHeaders::new())
.request_id()
.access_log()
.catch_panic()
.error_mapping(ErrorMapping::new())
.body_limit(2 * 1024 * 1024)
.timeout(Duration::from_secs(30))
.inertia(inertia)
.session(session, sessions)
.map_err(|e| Error::Config(e.to_string()))?
.csrf(
CsrfConfig::inertia()
.with_secure(config.production)
.map_err(|e| Error::Config(e.to_string()))?,
)
.static_files(&assets_config)
.health(true)
.page_contracts(crate::app::page_contracts())
.layer(arcature::from_fn(
crate::bootstrap::error_pages::error_pages,
));
if let Some(cache) = config.cache {
builder = builder.cache(cache);
}
#[cfg(feature = "dev")]
{
builder = builder.uag_endpoint(crate::app::graph());
}
if options.scheduler {
builder = builder.scheduler(schedule());
}
Ok(Bootstrapped {
application: builder.build(),
state_fn,
})
}
fn schedule() -> Scheduler {
Scheduler::new()
}
fn jobs_registry() -> Registry {
let mut registry = Registry::new();
let _ = &mut registry;
registry
}