2023-01-23 23:36:03 +01:00
|
|
|
use anyhow::Result;
|
2023-01-30 17:01:25 +01:00
|
|
|
use axum::{http::StatusCode, response::IntoResponse, Router};
|
2023-01-24 01:20:42 +01:00
|
|
|
use clap::Parser;
|
2023-01-23 23:36:03 +01:00
|
|
|
use diesel::{
|
|
|
|
prelude::*,
|
|
|
|
r2d2::{ConnectionManager, Pool, PooledConnection},
|
|
|
|
};
|
|
|
|
use lettre::{Message, SmtpTransport, Transport};
|
2023-01-30 17:01:25 +01:00
|
|
|
use std::{env, net::SocketAddr};
|
2023-01-23 23:36:03 +01:00
|
|
|
use time::{Duration, 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-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};
|
|
|
|
|
|
|
|
fn remind(
|
|
|
|
db: &mut PooledConnection<ConnectionManager<PgConnection>>,
|
|
|
|
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-01-30 17:01:25 +01:00
|
|
|
.load::<Reminder>(db)?;
|
|
|
|
|
|
|
|
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())))
|
|
|
|
.execute(db)?;
|
|
|
|
|
|
|
|
debug!("email sent to {}", reminder.receiver);
|
|
|
|
} else {
|
|
|
|
let duration = reminder.planned - OffsetDateTime::now_utc();
|
|
|
|
info!(?duration, "parking reminder");
|
|
|
|
|
|
|
|
std::thread::park_timeout(<std::time::Duration>::try_from(duration)?);
|
|
|
|
return Ok(());
|
|
|
|
}
|
2023-01-23 23:36:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-01-30 17:01:25 +01:00
|
|
|
#[allow(clippy::unused_async)]
|
|
|
|
async fn not_found() -> impl IntoResponse {
|
|
|
|
(StatusCode::NOT_FOUND, "Page not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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-01-24 00:46:25 +01:00
|
|
|
let db_pool = Pool::builder().build(ConnectionManager::<PgConnection>::new(format!(
|
|
|
|
"postgresql://{}:{}@{}:{}/{}",
|
|
|
|
config.database.user,
|
|
|
|
config.database.pass,
|
|
|
|
config.database.host,
|
|
|
|
config.database.port,
|
|
|
|
config.database.name
|
|
|
|
)))?;
|
2023-01-23 23:36:03 +01:00
|
|
|
|
|
|
|
let test_reminder = NewReminder {
|
|
|
|
created: OffsetDateTime::now_utc(),
|
|
|
|
planned: (OffsetDateTime::now_utc() + Duration::MINUTE),
|
|
|
|
title: "Test title",
|
|
|
|
message: "Test message",
|
|
|
|
receiver: "finga@localhost",
|
|
|
|
};
|
|
|
|
|
|
|
|
diesel::insert_into(schema::reminders::table)
|
|
|
|
.values(&test_reminder)
|
|
|
|
.execute(&mut db_pool.get()?)?;
|
|
|
|
|
2023-01-30 17:01:25 +01:00
|
|
|
std::thread::spawn(move || {
|
|
|
|
let mailer = SmtpTransport::unencrypted_localhost();
|
2023-01-23 23:36:03 +01:00
|
|
|
|
2023-01-30 17:01:25 +01:00
|
|
|
loop {
|
|
|
|
remind(&mut db_pool.get().unwrap(), &mailer).unwrap();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let app = Router::new()
|
|
|
|
.layer(ServiceBuilder::new().layer(TraceLayer::new_for_http()))
|
|
|
|
.fallback(not_found);
|
|
|
|
|
|
|
|
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
|
|
|
}
|