~aleteoryx/lfm_embed

a8046f11e551c69d1e45cd432694b4c8a04ee7b7 — alyx 5 months ago 79999ce
Lua dev mode functional
M src/cache.rs => src/cache.rs +5 -0
@@ 82,3 82,8 @@ where
pub type CacheFuture<Output> = Pin<Box<(dyn Future<Output = Result<Output, (StatusCode, &'static str)>> + Send + Sync)>>;
pub type CacheGetter<Output> = fn(&String) -> CacheFuture<Output>;
pub type Cache<Output>       = Arc<RwLock<AsyncCache<String, Output, CacheGetter<Output>>>>;

pub fn touch() {
  user::touch();
  font::touch();
}

M src/cache/font.rs => src/cache/font.rs +4 -0
@@ 57,3 57,7 @@ static FONT_CACHE: LazyLock<FontCache> =  LazyLock::new(|| {
pub async fn get_fontinfo(font: &String) -> Result<Arc<str>, (StatusCode, &'static str)> {
  FONT_CACHE.write().await.get_owned(font).await
}

pub fn touch() {
  LazyLock::force(&FONT_CACHE);
}

M src/cache/user.rs => src/cache/user.rs +4 -0
@@ 101,3 101,7 @@ pub async fn get_userinfo(user: &String) -> (Result<Arc<(User, Track, TrackStub)
    }
  }
}

pub fn touch() {
  LazyLock::force(&ALLOWLIST);
}

M src/config.rs => src/config.rs +2 -2
@@ 23,7 23,7 @@ pub struct Config {
  pub(crate) theme_dir: Option<Arc<str>>,
  pub(crate) theme_ext_hbs: Arc<str>,
  pub(crate) theme_ext_lua: Arc<str>,
  pub(crate) theme_debug: bool,
  pub(crate) theme_dev: bool,

  pub(crate) allowlist: BTreeSet<String>,
  pub(crate) allowlist_mode: String,


@@ 48,7 48,7 @@ impl Config {
      theme_dir: var("LFME_THEME_DIR").ok().map(Into::into),
      theme_ext_hbs: var("LFME_THEME_EXT_HBS").unwrap_or_else(|_| ".hbs".into()).into(),
      theme_ext_lua: var("LFME_THEME_EXT_LUA").unwrap_or_else(|_| ".lua".into()).into(),
      theme_debug: var("LFME_THEME_DEV").map(|h| &h == "1").unwrap_or(false),
      theme_dev: var("LFME_THEME_DEV").map(|h| &h == "1").unwrap_or(false),

      allowlist: var("LFME_WHITELIST").or_else(|_| var("LFME_ALLOWLIST")).ok().map(|w| w.split(',').map(|s| s.trim().to_string()).collect()).unwrap_or_default(),
      allowlist_mode: var("LFME_WHITELIST_MODE").or_else(|_| var("LFME_ALLOWLIST_MODE")).map(|m| m.to_ascii_lowercase()).unwrap_or_else(|_| "open".into()),

M src/lib.rs => src/lib.rs +5 -0
@@ 10,3 10,8 @@ pub mod ctx;
pub mod theming;

pub use config::CONFIG;

pub fn touch() {
  cache::touch();
  theming::touch();
}

M src/main.rs => src/main.rs +2 -0
@@ 45,6 45,8 @@ async fn main() {
        .unwrap_or(env_logger::Target::Stderr)
    ).init();

  lfm_embed::touch();

  let user = warp::path!("user" / String)
    .and(warp::query::<UserQuery>())
    .then(|s, q: UserQuery| async move {

M src/theming.rs => src/theming.rs +5 -0
@@ 24,3 24,8 @@ pub fn render_theme(name: Option<&str>, ctx: &crate::ctx::Ctx) -> (String, Resul

  (theme.into(), res)
}

pub fn touch() {
  hbs::touch();
  lua::touch();
}

M src/theming/hbs.rs => src/theming/hbs.rs +5 -1
@@ 18,7 18,7 @@ static HANDLEBARS: LazyLock<Handlebars> = LazyLock::new(|| {
    log::info!("Registering internal handlebars theme `{key}`");
    hb.register_template_string(key, fulltext).unwrap();
  }
  hb.set_dev_mode(CONFIG.theme_debug);
  hb.set_dev_mode(CONFIG.theme_dev);

  if let Some(themes_dir) = CONFIG.theme_dir.as_ref() {
    log::info!("Registering handlebars theme dir `{themes_dir}` with extension `{}`.", CONFIG.theme_ext_hbs);


@@ 34,3 34,7 @@ pub fn render_theme(name: &str, ctx: &crate::ctx::Ctx) -> Option<Result<String, 
  let render = templ.renders(&HANDLEBARS, &ctx, &mut RenderContext::new(Some(&name.into()))).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR);
  Some(render)
}

pub fn touch() {
  LazyLock::force(&HANDLEBARS);
}

M src/theming/lua.rs => src/theming/lua.rs +35 -10
@@ 18,7 18,7 @@ static LUA: LazyLock<Mutex<Lua>> = LazyLock::new(|| {
  lua.sandbox(true).expect("lua initialization");

  lua.set_compiler(
    if CONFIG.theme_debug { Compiler::new().set_optimization_level(0).set_debug_level(2) }
    if CONFIG.theme_dev { Compiler::new().set_optimization_level(0).set_debug_level(2) }
    else { Compiler::new().set_optimization_level(2).set_debug_level(1) }
  );



@@ 38,14 38,19 @@ static LUA: LazyLock<Mutex<Lua>> = LazyLock::new(|| {

  for (k, v) in INTERNAL_THEMES {
    log::info!("Registering compiled lua theme `{k}`.");
    let _ = themes.set(*k, lua.load(*v).into_function().expect("loading internal theme"));
    let _ = themes.set(*k, lua.load(*v).eval::<Value>().expect("loading internal theme"));
  }

  if let Some(theme_dir) = CONFIG.theme_dir.as_ref() {
    log::info!("Registering lua theme dir `{theme_dir}` with extension `{}`.", CONFIG.theme_ext_lua);
    for (k, v) in walk_dir(theme_dir.as_ref().as_ref()).expect("walking theme dir") {
      log::info!("Registering runtime lua theme `{k}`.");
      let _ = themes.set(k, lua.load(v).eval::<Value>().expect("loading internal theme"));
    if !CONFIG.theme_dev {
      log::info!("Registering lua theme dir `{theme_dir}` with extension `{}`.", CONFIG.theme_ext_lua);
      for (k, v) in walk_dir(theme_dir.as_ref().as_ref()).expect("walking theme dir") {
        log::info!("Registering runtime lua theme `{k}`.");
        let _ = themes.set(k, lua.load(v).eval::<Value>().expect("loading external theme"));
      }
    }
    else {
      log::info!("Ready to dev-load lua themes from `{theme_dir}` with extension `{}`.", CONFIG.theme_ext_lua);
    }
  }



@@ 86,13 91,29 @@ fn walk_dir(path: &Path) -> std::io::Result<Vec<(String, String)>> {
}

pub fn render_theme(name: &str, ctx: &crate::ctx::Ctx) -> Option<Result<String, StatusCode>> {
  if name == ".." || name.starts_with("../") || name.ends_with("/..") || name.contains("/../") {
    return Some(Err(StatusCode::BAD_REQUEST))
  }

  LUA.clear_poison();
  let lua = LUA.lock().expect("FIXME: Mutex poisoning race condition.");
  let themes: Table = lua.globals().get("__themes").unwrap();
  let Ok(Value::Function(theme)) = themes.get(name)
  else {
    return None;
  };
  let theme;
  match themes.get(name) {
    Ok(Value::Function(themefn)) => { theme = themefn; },
    Ok(Value::Nil) if !CONFIG.theme_dev || CONFIG.theme_dir.is_none() => { return None; },
    Ok(Value::Nil) => {
      let code = fs::read_to_string(CONFIG.theme_dir.as_ref().unwrap().to_string() + "/" + name + &CONFIG.theme_ext_lua).ok()?;
      let res = lua.load(code).eval::<Value>();
      match res {
        Ok(Value::Function(themefn)) => { theme = themefn; },
        Ok(v) => { log::error!("Got `{v:?}` instead of Function when dev-loading `{name}`."); return Some(Err(StatusCode::INTERNAL_SERVER_ERROR)); },
        Err(e) => { log::error!("Error dev-loading `{name}`: {e}"); return Some(Err(StatusCode::INTERNAL_SERVER_ERROR)); },
      }
    },
    Ok(v) => { log::error!("Got `{v:?}` instead of Function when loading `{name}`."); return Some(Err(StatusCode::INTERNAL_SERVER_ERROR)); }
    Err(e) => { log::error!("Error loading `{name}`: {e}"); return Some(Err(StatusCode::INTERNAL_SERVER_ERROR)); }, //TODO: gate behind flag
  }

  let ctx = match lua.to_value(ctx) {
    Ok(ok) => ok,


@@ 104,3 125,7 @@ pub fn render_theme(name: &str, ctx: &crate::ctx::Ctx) -> Option<Result<String, 

  Some(theme.call((ctx,)).map_err(|e| { log::error!("Lua theme execution error: {e}"); StatusCode::INTERNAL_SERVER_ERROR }))
}

pub fn touch() {
  LazyLock::force(&LUA);
}