/* * tmtd - Suckless To Do list * Copyright (C) 2022 C4TG1RL5 * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ use crate::{config::Config, database::Database}; use async_sqlx_session::PostgresSessionStore; use std::str::FromStr; use std::time::Duration; use std::{env, sync::Arc}; use tokio::sync::broadcast; use tokio::task::JoinHandle; use tokio::time::sleep; use tracing::{error, info, Level}; mod config; mod database; mod task; mod templates; mod web; #[tokio::main(flavor = "current_thread")] async fn main() -> anyhow::Result<()> { let cfg = Config::load().await?; tracing_subscriber::fmt::fmt() .with_max_level({ if let Some(o) = cfg.log_level.as_deref() { Level::from_str(o)? } else { Level::INFO } }) .init(); info!(concat!("Initializing - tmtd ", env!("CARGO_PKG_VERSION"))); let (ctx, _) = broadcast::channel(1); let database = Arc::new(Database::connect(&cfg.connection_string).await?); let session_store = PostgresSessionStore::from_client(database.pool()).with_table_name("sessions"); session_store.migrate().await?; let cleanup_task = spawn_session_cleanup_task(&session_store, Duration::from_secs(600), ctx.subscribe()); info!("Started session cleanup task"); let web = web::App::new(cfg.listen_addr, database.clone()).await?; info!("Started the web app at http://{}", cfg.listen_addr); web.server.await?; ctx.send(()).unwrap(); cleanup_task .await .unwrap_or_else(|e| error!("Couldn't join cleanup task: {}", e)); database.close().await; Ok(()) } fn spawn_session_cleanup_task( store: &PostgresSessionStore, period: Duration, mut cancel: broadcast::Receiver<()>, ) -> JoinHandle<()> { let store = store.clone(); tokio::spawn(async move { loop { tokio::select! { _ = sleep(period) => { if let Err(error) = store.cleanup().await { error!("Error in cleanup task: {}", error); } } _ = cancel.recv() => break } } info!("Cleanup task has been shut down"); }) }