2023-02-02 06:51:04 +01:00
|
|
|
use anyhow::{Error, Result};
|
|
|
|
use axum::{routing::post, Router};
|
2023-01-24 01:20:42 +01:00
|
|
|
use clap::Parser;
|
2023-01-23 23:36:03 +01:00
|
|
|
use diesel::{
|
|
|
|
prelude::*,
|
2023-02-02 06:51:04 +01:00
|
|
|
r2d2::{ConnectionManager, Pool},
|
2023-01-23 23:36:03 +01:00
|
|
|
};
|
|
|
|
use lettre::{Message, SmtpTransport, Transport};
|
2023-02-02 06:51:04 +01:00
|
|
|
use std::{env, net::SocketAddr, time::Duration};
|
|
|
|
use time::OffsetDateTime;
|
2023-01-30 17:01:25 +01:00
|
|
|
use tower::ServiceBuilder;
|
|
|
|
use tower_http::trace::TraceLayer;
|
2023-01-23 23:36:03 +01:00
|
|
|
use tracing::{debug, info, trace};
|
|
|
|
|
2023-02-02 06:51:04 +01:00
|
|
|
mod api;
|
2023-01-24 01:20:42 +01:00
|
|
|
mod args;
|
2023-01-24 00:46:25 +01:00
|
|
|
mod config;
|
2023-01-23 23:36:03 +01:00
|
|
|
mod models;
|
|
|
|
mod schema;
|
|
|
|
|
2023-01-24 01:20:42 +01:00
|
|
|
use args::Args;
|
2023-01-24 00:46:25 +01:00
|
|
|
use config::Config;
|
2023-01-23 23:36:03 +01:00
|
|
|
use models::{NewReminder, Reminder};
|
|
|
|
|
2023-02-02 06:51:04 +01:00
|
|
|
fn get_connection_pool(config: &Config) -> Result<Pool<ConnectionManager<PgConnection>>> {
|
|
|
|
Ok(
|
|
|
|
Pool::builder().build(ConnectionManager::<PgConnection>::new(format!(
|
|
|
|
"postgresql://{}:{}@{}:{}/{}",
|
|
|
|
config.database.user,
|
|
|
|
config.database.pass,
|
|
|
|
config.database.host,
|
|
|
|
config.database.port,
|
|
|
|
config.database.name
|
|
|
|
)))?,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::cognitive_complexity)]
|
2023-01-23 23:36:03 +01:00
|
|
|
fn remind(
|
2023-02-02 06:51:04 +01:00
|
|
|
db_pool: &mut Pool<ConnectionManager<PgConnection>>,
|
2023-01-23 23:36:03 +01:00
|
|
|
mailer: &SmtpTransport,
|
|
|
|
) -> Result<()> {
|
2023-01-24 00:46:25 +01:00
|
|
|
info!("checking for reminders");
|
2023-01-23 23:36:03 +01:00
|
|
|
|
2023-01-30 17:01:25 +01:00
|
|
|
let result = schema::reminders::dsl::reminders
|
2023-01-23 23:36:03 +01:00
|
|
|
.filter(schema::reminders::executed.is_null())
|
|
|
|
.order(schema::reminders::planned.asc())
|
2023-02-02 06:51:04 +01:00
|
|
|
.load::<Reminder>(&mut db_pool.get()?)?;
|
2023-01-30 17:01:25 +01:00
|
|
|
|
|
|
|
if result.is_empty() {
|
|
|
|
info!("no reminders present, parking indefinitely");
|
|
|
|
std::thread::park();
|
|
|
|
} else {
|
|
|
|
for reminder in result {
|
|
|
|
trace!(?reminder, "checking reminder");
|
|
|
|
if reminder.planned <= OffsetDateTime::now_utc() {
|
|
|
|
let email = Message::builder()
|
|
|
|
.from("whakarite@localhost".parse()?)
|
|
|
|
.to(reminder.receiver.parse()?)
|
|
|
|
.subject(&reminder.title)
|
|
|
|
.body(reminder.message.to_string())?;
|
|
|
|
|
|
|
|
mailer.send(&email)?;
|
|
|
|
|
|
|
|
diesel::update(&reminder)
|
|
|
|
.set(schema::reminders::executed.eq(Some(OffsetDateTime::now_utc())))
|
2023-02-02 06:51:04 +01:00
|
|
|
.execute(&mut db_pool.get()?)?;
|
2023-01-30 17:01:25 +01:00
|
|
|
|
|
|
|
debug!("email sent to {}", reminder.receiver);
|
|
|
|
} else {
|
|
|
|
let duration = reminder.planned - OffsetDateTime::now_utc();
|
|
|
|
info!(?duration, "parking reminder");
|
|
|
|
|
2023-02-02 06:51:04 +01:00
|
|
|
std::thread::park_timeout(<Duration>::try_from(duration)?);
|
2023-01-30 17:01:25 +01:00
|
|
|
return Ok(());
|
|
|
|
}
|
2023-01-23 23:36:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-01-30 17:01:25 +01:00
|
|
|
#[tokio::main]
|
|
|
|
async fn main() -> Result<()> {
|
2023-01-24 01:20:42 +01:00
|
|
|
let args = Args::parse();
|
|
|
|
|
|
|
|
if env::var("RUST_LOG").is_err() {
|
|
|
|
env::set_var("RUST_LOG", format!("{}", args.log_level));
|
|
|
|
}
|
|
|
|
|
2023-01-23 23:36:03 +01:00
|
|
|
tracing_subscriber::fmt::init();
|
|
|
|
|
|
|
|
info!(
|
|
|
|
"{} {} started",
|
|
|
|
env!("CARGO_BIN_NAME"),
|
|
|
|
env!("CARGO_PKG_VERSION")
|
|
|
|
);
|
|
|
|
|
2023-01-25 22:11:32 +01:00
|
|
|
let config = Config::load_config(args.config)?;
|
2023-02-02 06:51:04 +01:00
|
|
|
|
|
|
|
let mut db_pool = get_connection_pool(&config)?;
|
|
|
|
|
|
|
|
std::thread::spawn(move || -> Result<(), Error> {
|
2023-01-30 17:01:25 +01:00
|
|
|
let mailer = SmtpTransport::unencrypted_localhost();
|
2023-01-23 23:36:03 +01:00
|
|
|
|
2023-01-30 17:01:25 +01:00
|
|
|
loop {
|
2023-02-02 06:51:04 +01:00
|
|
|
remind(&mut db_pool, &mailer)?;
|
2023-01-30 17:01:25 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-02-02 06:51:04 +01:00
|
|
|
let db_pool = get_connection_pool(&config)?;
|
|
|
|
|
2023-01-30 17:01:25 +01:00
|
|
|
let app = Router::new()
|
2023-02-02 06:51:04 +01:00
|
|
|
.route("/v1/reminder", post(api::create_reminder))
|
2023-01-30 17:01:25 +01:00
|
|
|
.layer(ServiceBuilder::new().layer(TraceLayer::new_for_http()))
|
2023-02-02 06:51:04 +01:00
|
|
|
.fallback(api::not_found)
|
|
|
|
.with_state(db_pool);
|
2023-01-30 17:01:25 +01:00
|
|
|
|
|
|
|
let addr = SocketAddr::from((config.server.address, config.server.port));
|
|
|
|
|
|
|
|
info!("{} listening on {}", env!("CARGO_PKG_NAME"), addr);
|
|
|
|
|
|
|
|
axum::Server::bind(&addr)
|
|
|
|
.serve(app.into_make_service())
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(())
|
2023-01-23 23:36:03 +01:00
|
|
|
}
|