94 lines
2.7 KiB
Rust
94 lines
2.7 KiB
Rust
mod cleaner;
|
|
mod cli;
|
|
mod config;
|
|
mod db;
|
|
mod helpers;
|
|
mod locales;
|
|
mod notify;
|
|
mod server;
|
|
|
|
use argon2::{
|
|
password_hash::{rand_core::OsRng, PasswordHasher, SaltString},
|
|
Argon2,
|
|
};
|
|
use clap::Parser;
|
|
use log::warn;
|
|
use std::{collections::HashMap, str::FromStr};
|
|
use unic_langid::LanguageIdentifier;
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let opt = cli::MainOpt::parse();
|
|
|
|
match opt.cmd {
|
|
cli::MainSubcommand::Init => {
|
|
init_all(opt.opt, cli::StartOpt { tmp: false });
|
|
}
|
|
cli::MainSubcommand::Start(subopt) => {
|
|
let (config, dbs, templates) = init_all(opt.opt, subopt);
|
|
|
|
// These will never be dropped nor mutated
|
|
let templates = Box::leak(Box::new(templates));
|
|
let config = Box::leak(Box::new(config));
|
|
let locales = Box::leak(Box::new(locales::Locales::new(config)));
|
|
|
|
templates.tera.register_function(
|
|
"tr",
|
|
Box::new(
|
|
|args: &HashMap<String, tera::Value>| -> tera::Result<tera::Value> {
|
|
let langs = if let Some(tera::Value::Array(langs)) = args.get("l") {
|
|
langs
|
|
.iter()
|
|
.filter_map(|lang| lang.as_str())
|
|
.filter_map(|lang| LanguageIdentifier::from_str(lang).ok())
|
|
.collect()
|
|
} else {
|
|
vec![locales.default_lang.clone()]
|
|
};
|
|
let key = args
|
|
.get("k")
|
|
.ok_or_else(|| tera::Error::from("Missing argument `k`"))?
|
|
.as_str()
|
|
.ok_or_else(|| tera::Error::from("Argument `k` must be string"))?;
|
|
let args_iter = fluent_bundle::FluentArgs::from_iter(
|
|
args.iter().map(|(k, v)| (k, locales::tera_to_fluent(v))),
|
|
);
|
|
let res = locales.tr(&langs, key, Some(&args_iter));
|
|
if res.is_none() {
|
|
warn!("(calling `tr` in template) translation key `{key}` not found");
|
|
}
|
|
Ok(res.into())
|
|
},
|
|
),
|
|
);
|
|
|
|
tokio::spawn(cleaner::run_cleaner(config, dbs.clone()));
|
|
server::run_server(config, dbs, templates, locales).await;
|
|
}
|
|
cli::MainSubcommand::Psw => {
|
|
let mut config = config::read_config(&opt.opt.dir.0);
|
|
let password = rpassword::prompt_password("Additional admin password: ").unwrap();
|
|
let salt = SaltString::generate(&mut OsRng);
|
|
let argon2 = Argon2::default();
|
|
let password_hash = argon2
|
|
.hash_password(password.as_bytes(), &salt)
|
|
.unwrap()
|
|
.to_string();
|
|
config.admin_passwords.push(password_hash);
|
|
config::write_config(&opt.opt.dir.0, &config);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn init_all(
|
|
opt: cli::MainCommonOpt,
|
|
subopt: cli::StartOpt,
|
|
) -> (config::Config, db::Dbs, server::page::templates::Templates) {
|
|
std::fs::create_dir_all(&opt.dir.0).expect("Cannot create dir");
|
|
let config = config::read_config(&opt.dir.0);
|
|
let dbs = db::load_dbs((!subopt.tmp).then_some(&opt.dir.0));
|
|
let templates = server::page::templates::Templates::new(&opt.dir.0, &config);
|
|
|
|
(config, dbs, templates)
|
|
}
|