126 lines
3.9 KiB
Rust
126 lines
3.9 KiB
Rust
use async_session::SessionStore;
|
|
use axum_login::{AuthManagerLayer, AuthManagerLayerBuilder, AuthSession, AuthUser, AuthnBackend};
|
|
use julid::Julid;
|
|
use sqlx::{
|
|
migrate::Migrator,
|
|
sqlite::{SqliteConnectOptions, SqliteJournalMode, SqlitePoolOptions},
|
|
SqlitePool,
|
|
};
|
|
use tower_sessions::{
|
|
cookie::time::Duration, session_store::ExpiredDeletion, Expiry, Session, SessionManagerLayer,
|
|
SqliteStore,
|
|
};
|
|
|
|
use crate::User;
|
|
|
|
const MAX_CONNS: u32 = 200;
|
|
const MIN_CONNS: u32 = 5;
|
|
const TIMEOUT: u64 = 11;
|
|
const SESSION_TTL: Duration = Duration::new((365.2422 * 24. * 3600.0) as i64, 0);
|
|
|
|
pub fn get_db_pool() -> SqlitePool {
|
|
let db_filename = {
|
|
std::env::var("DATABASE_FILE").unwrap_or_else(|_| {
|
|
#[cfg(not(test))]
|
|
{
|
|
let home =
|
|
std::env::var("HOME").expect("Could not determine $HOME for finding db file");
|
|
format!("{home}/.what2watch.db")
|
|
}
|
|
#[cfg(test)]
|
|
{
|
|
use rand::RngCore;
|
|
let mut rng = rand::thread_rng();
|
|
let id = rng.next_u64();
|
|
// see https://www.sqlite.org/inmemorydb.html for meaning of the string;
|
|
// it allows each separate test to have its own dedicated memory-backed db that
|
|
// will live as long as the whole process
|
|
format!("file:testdb-{id}?mode=memory&cache=shared")
|
|
}
|
|
})
|
|
};
|
|
|
|
tracing::info!("Connecting to DB at {db_filename}");
|
|
|
|
let conn_opts = SqliteConnectOptions::new()
|
|
.foreign_keys(true)
|
|
.auto_vacuum(sqlx::sqlite::SqliteAutoVacuum::Incremental)
|
|
.journal_mode(SqliteJournalMode::Wal)
|
|
.synchronous(sqlx::sqlite::SqliteSynchronous::Normal)
|
|
.filename(&db_filename)
|
|
// need to build this out of band and put it in the project root; see instructions at
|
|
// https://gitlab.com/nebkor/julid
|
|
.extension("./libjulid")
|
|
.busy_timeout(Duration::from_secs(TIMEOUT))
|
|
.create_if_missing(true);
|
|
|
|
let pool = SqlitePoolOptions::new()
|
|
.max_connections(MAX_CONNS)
|
|
.min_connections(MIN_CONNS)
|
|
.idle_timeout(Some(Duration::from_secs(3)))
|
|
.max_lifetime(Some(Duration::from_secs(3600)))
|
|
.connect_with(conn_opts);
|
|
|
|
let pool = {
|
|
let rt = tokio::runtime::Builder::new_multi_thread()
|
|
.enable_all()
|
|
.build()
|
|
.unwrap();
|
|
|
|
rt.block_on(pool).unwrap()
|
|
};
|
|
|
|
{
|
|
let rt = tokio::runtime::Builder::new_multi_thread()
|
|
.enable_all()
|
|
.build()
|
|
.unwrap();
|
|
|
|
let m = Migrator::new(std::path::Path::new("./migrations"));
|
|
let mut m = rt.block_on(m).unwrap();
|
|
let m = m.set_locking(false);
|
|
rt.block_on(m.run(&pool)).unwrap();
|
|
tracing::info!("Ran migrations");
|
|
}
|
|
|
|
pool
|
|
}
|
|
|
|
pub async fn session_layer(pool: SqlitePool) -> SessionManagerLayer<SqliteStore> {
|
|
let store = SqliteStore::new(pool);
|
|
store
|
|
.migrate()
|
|
.await
|
|
.expect("Calling `migrate()` should be reliable, is the DB gone?");
|
|
|
|
SessionManagerLayer::new(store)
|
|
.with_secure(true)
|
|
.with_expiry(Expiry::OnInactivity(SESSION_TTL.into()))
|
|
}
|
|
|
|
pub async fn auth_layer(
|
|
pool: SqlitePool,
|
|
secret: &[u8],
|
|
) -> AuthManagerLayer<SqliteStore, SessionManagerLayer<SqliteStore>> {
|
|
todo!()
|
|
}
|
|
|
|
//-************************************************************************
|
|
// Tests for `db` module.
|
|
//-************************************************************************
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use tokio::runtime::Runtime;
|
|
|
|
#[test]
|
|
fn it_migrates_the_db() {
|
|
let rt = Runtime::new().unwrap();
|
|
let db = super::get_db_pool();
|
|
rt.block_on(async {
|
|
let r = sqlx::query("select count(*) from users")
|
|
.fetch_one(&db)
|
|
.await;
|
|
assert!(r.is_ok());
|
|
});
|
|
}
|
|
}
|