119 lines
3.1 KiB
Rust
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();
|
|
}
|
|
}
|
|
}
|