calathea/src/main.rs

119 lines
3.1 KiB
Rust

mod cli;
mod db;
mod server;
use std::{
fs,
path::{Path, PathBuf},
sync::Arc,
};
use clap::Parser;
use tera::Tera;
use tokio::sync::Mutex;
use tower_http::compression::CompressionLayer;
use cli::UserCmd;
use db::DbError;
const DB_FILENAME: &str = "db.sqlite3";
const IMG_DIR: &str = "imgs";
const TMP_DIR: &str = "tmp";
#[derive(Clone)]
pub struct Context {
pool: db::DbPool,
tera: Arc<Tera>,
data_dir: PathBuf,
tmp_dir_counter: Arc<Mutex<u16>>,
}
impl Context {
pub async fn tmp_file_path(&self) -> PathBuf {
let mut guard = self.tmp_dir_counter.lock().await;
let path = self.data_dir.join(TMP_DIR).join(guard.to_string());
*guard = guard.wrapping_add(1);
path
}
}
fn run_user_cli(data_dir: impl AsRef<Path>, cmd: UserCmd) -> Result<(), DbError> {
let pool = db::initialize_db(data_dir.as_ref().join(DB_FILENAME), false)?;
match cmd.cmd {
cli::UserSubCmd::List => {
let users = db::User::all(&pool)?;
println!("id\tusername\tis admin");
for user in users {
println!("{}\t{}\t{}", user.id, user.username, user.admin);
}
}
cli::UserSubCmd::Add {
username,
password,
admin,
} => {
db::NewUser::new(username, password, admin).insert(&pool)?;
}
cli::UserSubCmd::Remove { username } => {
db::User::remove_by_username(&pool, &username)?;
}
}
Ok(())
}
#[tokio::main]
async fn main() {
let args = cli::Cli::parse();
match args.cmd {
cli::Subcommands::Serve { domain, port } => {
tracing_subscriber::fmt::init();
if !fs::exists(&args.data_dir).unwrap() {
fs::create_dir_all(&args.data_dir).unwrap();
}
if !fs::exists(args.data_dir.join(IMG_DIR)).unwrap() {
fs::create_dir(args.data_dir.join(IMG_DIR)).unwrap();
}
if !fs::exists(args.data_dir.join(TMP_DIR)).unwrap() {
fs::create_dir(args.data_dir.join(TMP_DIR)).unwrap();
}
let pool = db::initialize_db(args.data_dir.join(DB_FILENAME), true).unwrap();
let tera =
Tera::new(&format!("{}/**/*", args.templates_dir.to_string_lossy())).unwrap();
let ctx = Context {
pool,
tera: Arc::new(tera),
data_dir: args.data_dir.clone(),
tmp_dir_counter: Arc::new(Mutex::new(0)),
};
let app = server::app(ctx, &args.static_dir)
.layer(CompressionLayer::new().br(true).gzip(true));
let address = format!("{}:{}", domain, port);
tracing::info!("Starting server on {address}");
let listener = tokio::net::TcpListener::bind(address).await.unwrap();
axum::serve(listener, app.into_make_service())
.await
.unwrap();
}
cli::Subcommands::User(cmd) => {
run_user_cli(&args.data_dir, cmd).unwrap();
}
}
}