use std::{str::FromStr as _, time::Duration}; use sqlx::{ ConnectOptions as _, SqlitePool, sqlite::{SqliteConnectOptions, SqlitePoolOptions}, }; use tokio::task::JoinHandle; pub mod auth; pub mod options; pub mod share; pub mod warrens; #[derive(Debug, Clone)] pub struct SqliteConfig { database_url: String, } impl SqliteConfig { pub fn new(database_url: String) -> Self { Self { database_url } } } #[derive(Debug, Clone)] pub struct Sqlite { pool: SqlitePool, } impl Sqlite { pub async fn new(config: SqliteConfig) -> anyhow::Result { let opts = SqliteConnectOptions::from_str(&config.database_url)? .create_if_missing(true) .disable_statement_logging(); let pool = SqlitePoolOptions::new().connect_with(opts).await?; sqlx::migrate!("./migrations").run(&pool).await?; // 3600 seconds = 1 hour Self::start_cleanup_tasks(pool.clone(), Duration::from_secs(3600)); Ok(Self { pool }) } pub(super) fn start_cleanup_tasks(pool: SqlitePool, interval: Duration) -> JoinHandle<()> { tokio::spawn(async move { loop { { let Ok(mut connection) = pool.acquire().await else { break; }; if let Ok(count) = Self::delete_expired_auth_sessions(&mut connection).await { tracing::debug!("Removed {count} expired auth session(s)"); } if let Ok(count) = Self::delete_expired_shares(&mut connection).await { tracing::debug!("Deleted {count} expired share(s)"); } } tokio::time::sleep(interval).await; } tracing::debug!("Session cleanup task stopped"); }) } } pub(super) fn is_not_found_error(err: &sqlx::Error) -> bool { matches!(err, sqlx::Error::RowNotFound) }