Session & database boilerplate

This commit is contained in:
lemonsh 2022-04-03 17:08:20 +02:00
parent 5b8af3dbc9
commit a76610331e
6 changed files with 2256 additions and 4 deletions

3
.gitignore vendored
View File

@ -1 +1,4 @@
/target
tmtd.toml
.idea
.vs

2122
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,14 @@ name = "tmtd"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tokio = { version = "1", features = ["rt", "signal", "macros"] }
toml = "0.5"
serde = { version = "1.0", features = ["derive"] }
sqlx = { version = "0.5", default-features = false, features = ["runtime-tokio-rustls", "postgres"] }
tracing = "0.1"
tracing-subscriber = "0.3"
warp = { version = "0.3", default-features = false }
tera = "1.15"
async-sqlx-session = { version = "0.4", default-features = false, features = ["pg"] }
anyhow = "1.0"

36
src/config.rs Normal file
View File

@ -0,0 +1,36 @@
/*
* tmtd - Suckless To Do list
* Copyright (C) 2022 lemon.sh & famfo
*
* 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 <http://www.gnu.org/licenses/>.
*/
use serde::Deserialize;
use std::net::SocketAddr;
use tokio::fs;
#[derive(Deserialize)]
pub struct Config {
pub listen_addr: SocketAddr,
pub admin_pass: String,
pub connection_string: String,
pub log_level: Option<String>,
}
impl Config {
pub async fn load_from_file(filename: &str) -> anyhow::Result<Self> {
let config_str = fs::read_to_string(filename).await?;
Ok(toml::from_str(&config_str)?)
}
}

33
src/database.rs Normal file
View File

@ -0,0 +1,33 @@
use sqlx::postgres::{PgConnectOptions, PgConnectionInfo, PgPoolOptions};
use sqlx::{ConnectOptions, PgPool};
use tracing::info;
use tracing::log::LevelFilter;
pub struct Database {
pool: PgPool,
}
impl Database {
pub async fn connect(conn_string: &str) -> anyhow::Result<Self> {
let mut connect_options: PgConnectOptions = conn_string.parse()?;
connect_options.log_statements(LevelFilter::Debug);
info!("Connecting to the database");
let pool = PgPoolOptions::new().connect_with(connect_options).await?;
let pgver = pool
.acquire()
.await?
.server_version_num()
.map(|v| v.to_string());
info!(
"Database connected, PostgreSQL version {}",
pgver.as_deref().unwrap_or("unknown")
);
Ok(Self {
pool
})
}
pub fn pool(&self) -> PgPool {
self.pool.clone()
}
}

View File

@ -1,3 +1,53 @@
fn main() {
println!("Hello, world!");
/*
* tmtd - Suckless To Do list
* Copyright (C) 2022 lemon.sh & famfo
*
* 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 <http://www.gnu.org/licenses/>.
*/
use crate::{config::Config, database::Database};
use std::env;
use std::time::Duration;
use async_sqlx_session::PostgresSessionStore;
use tokio::task::JoinHandle;
use tokio::time::sleep;
use tracing::{error, info};
mod config;
mod database;
#[tokio::main(flavor = "current_thread")]
async fn main() -> anyhow::Result<()> {
let config_var = env::var("TMTD_CONFIG");
let config_path = config_var.as_deref().unwrap_or("tmtd.toml");
println!("Loading config from '{}'...", config_path);
let cfg = Config::load_from_file(config_path).await?;
info!(concat!("Initializing - tmtd ", env!("CARGO_PKG_VERSION")));
let database = Database::connect(&cfg.connection_string).await?;
let session_store = PostgresSessionStore::from_client(database.pool()).with_table_name("sessions");
session_store.migrate().await?;
Ok(())
}
fn spawn_session_cleanup_task(store: &PostgresSessionStore, period: Duration) -> JoinHandle<()> {
let store = store.clone();
tokio::spawn(async move {
loop {
sleep(period).await;
if let Err(error) = store.cleanup().await {
error!("cleanup error: {}", error);
}
}
})
}