// SPDX-License-Identifier: AGPL-3.0-only #![feature(lazy_cell)] use std::collections::BTreeMap; use std::fs::File; use std::sync::Arc; use lfm_embed::CONFIG; use lfm_embed::ctx::get_ctx; use lfm_embed::cache::user::get_userinfo; use lfm_embed::cache::font::FontQuery; use lfm_embed::theming::render_theme; use log::LevelFilter; use dotenv::var; use warp::Filter; #[derive(serde::Deserialize, Debug)] #[serde(rename = "kebab-case")] struct UserQuery { #[serde(default)] theme: Option>, #[serde(flatten)] #[serde(default)] font: Option, #[serde(flatten)] rest: BTreeMap } #[tokio::main] async fn main() { env_logger::Builder::new() .filter_level(LevelFilter::Warn) .parse_filters(&var("LFME_LOG_LEVEL").unwrap_or_default()) .target( var("LFME_LOG_FILE").ok() .map( |f| env_logger::Target::Pipe( Box::new(File::options() .append(true) .open(f) .expect("couldn't open LFME_LOG_FILE"))) ) .unwrap_or(env_logger::Target::Stderr) ).init(); let user = warp::path!("user" / String) .and(warp::query::()) .then(|s, q: UserQuery| async move { log::debug!(target: "lfm_embed::main::user", "Handling request for user `{s}` with {q:?}"); let (userinfo, refresh) = get_userinfo(&s).await; let (ctx, status) = get_ctx(userinfo, q.font, q.rest).await; let (theme, res) = tokio::task::spawn_blocking(move || render_theme(q.theme.as_deref(), &ctx)).await.expect("fatal error in theme rendering"); log::debug!(target: "lfm_embed::main::user", "Using theme {theme}"); match res { Err(status) => http::Response::builder() .status(status) .header("Content-Type", "text/html") .body("

Internal Server Error.

Occurred when templating, check the logs.

".into()) .unwrap(), Ok(contents) => http::Response::builder() .status(status) .header("Refresh", refresh.as_secs()) .header("X-Selected-Theme", theme) .header("Content-Type", "text/html") .body(contents) .unwrap() } }); warp::serve(user) .bind(([127,0,0,1], CONFIG.port())).await; }