Creation of a reminder via web api

Concurrently run the reminder service as well as an web endpoint to
create a new reminder. Note that a new reminder does not notify the
reminder service yet.

Cargo update dependencies
This commit is contained in:
finga 2023-02-02 06:51:04 +01:00
parent 6b6f5aa48a
commit 7ded3ef430
5 changed files with 123 additions and 56 deletions

View file

@ -1,17 +1,18 @@
use anyhow::Result;
use axum::{http::StatusCode, response::IntoResponse, Router};
use anyhow::{Error, Result};
use axum::{routing::post, Router};
use clap::Parser;
use diesel::{
prelude::*,
r2d2::{ConnectionManager, Pool, PooledConnection},
r2d2::{ConnectionManager, Pool},
};
use lettre::{Message, SmtpTransport, Transport};
use std::{env, net::SocketAddr};
use time::{Duration, OffsetDateTime};
use std::{env, net::SocketAddr, time::Duration};
use time::OffsetDateTime;
use tower::ServiceBuilder;
use tower_http::trace::TraceLayer;
use tracing::{debug, info, trace};
mod api;
mod args;
mod config;
mod models;
@ -21,8 +22,22 @@ use args::Args;
use config::Config;
use models::{NewReminder, Reminder};
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)]
fn remind(
db: &mut PooledConnection<ConnectionManager<PgConnection>>,
db_pool: &mut Pool<ConnectionManager<PgConnection>>,
mailer: &SmtpTransport,
) -> Result<()> {
info!("checking for reminders");
@ -30,7 +45,7 @@ fn remind(
let result = schema::reminders::dsl::reminders
.filter(schema::reminders::executed.is_null())
.order(schema::reminders::planned.asc())
.load::<Reminder>(db)?;
.load::<Reminder>(&mut db_pool.get()?)?;
if result.is_empty() {
info!("no reminders present, parking indefinitely");
@ -49,14 +64,14 @@ fn remind(
diesel::update(&reminder)
.set(schema::reminders::executed.eq(Some(OffsetDateTime::now_utc())))
.execute(db)?;
.execute(&mut db_pool.get()?)?;
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)?);
std::thread::park_timeout(<Duration>::try_from(duration)?);
return Ok(());
}
}
@ -65,11 +80,6 @@ fn remind(
Ok(())
}
#[allow(clippy::unused_async)]
async fn not_found() -> impl IntoResponse {
(StatusCode::NOT_FOUND, "Page not found")
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Args::parse();
@ -87,38 +97,24 @@ async fn main() -> Result<()> {
);
let config = Config::load_config(args.config)?;
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
)))?;
let test_reminder = NewReminder {
created: OffsetDateTime::now_utc(),
planned: (OffsetDateTime::now_utc() + Duration::MINUTE),
title: "Test title",
message: "Test message",
receiver: "finga@localhost",
};
let mut db_pool = get_connection_pool(&config)?;
diesel::insert_into(schema::reminders::table)
.values(&test_reminder)
.execute(&mut db_pool.get()?)?;
std::thread::spawn(move || {
std::thread::spawn(move || -> Result<(), Error> {
let mailer = SmtpTransport::unencrypted_localhost();
loop {
remind(&mut db_pool.get().unwrap(), &mailer).unwrap();
remind(&mut db_pool, &mailer)?;
}
});
let db_pool = get_connection_pool(&config)?;
let app = Router::new()
.route("/v1/reminder", post(api::create_reminder))
.layer(ServiceBuilder::new().layer(TraceLayer::new_for_http()))
.fallback(not_found);
.fallback(api::not_found)
.with_state(db_pool);
let addr = SocketAddr::from((config.server.address, config.server.port));