add mail
This commit is contained in:
65
src/model/mail.rs
Normal file
65
src/model/mail.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use std::error::Error;
|
||||
|
||||
use lettre::{
|
||||
message::{
|
||||
header::{self, ContentType},
|
||||
MultiPart, SinglePart,
|
||||
},
|
||||
transport::smtp::authentication::Credentials,
|
||||
Message, SmtpTransport, Transport,
|
||||
};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::tera::admin::mail::MailToSend;
|
||||
|
||||
use super::role::Role;
|
||||
|
||||
pub struct Mail {}
|
||||
|
||||
impl Mail {
|
||||
pub async fn send(db: &SqlitePool, data: MailToSend<'_>, smtp_pw: String) -> bool {
|
||||
let mut email = Message::builder()
|
||||
.from(
|
||||
"ASKÖ Ruderverein Donau Linz <no-reply@rudernlinz.at>"
|
||||
.parse()
|
||||
.unwrap(),
|
||||
)
|
||||
.reply_to(
|
||||
"ASKÖ Ruderverein Donau Linz <info@rudernlinz.at>"
|
||||
.parse()
|
||||
.unwrap(),
|
||||
)
|
||||
.to("ASKÖ Ruderverein Donau Linz <no-reply@rudernlinz.at>"
|
||||
.parse()
|
||||
.unwrap());
|
||||
let role = Role::find_by_id(db, data.role_id).await.unwrap();
|
||||
for rec in role.mails_from_role(db).await {
|
||||
let splitted = rec.split(',');
|
||||
for single_rec in splitted {
|
||||
email = email.bcc(single_rec.parse().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: handle attachments
|
||||
|
||||
let email = email
|
||||
.subject(data.subject)
|
||||
.header(ContentType::TEXT_PLAIN)
|
||||
.body(String::from(data.body))
|
||||
.unwrap();
|
||||
|
||||
let creds = Credentials::new("no-reply@rudernlinz.at".to_owned(), smtp_pw);
|
||||
|
||||
let mailer = SmtpTransport::relay("mail.your-server.de")
|
||||
.unwrap()
|
||||
.credentials(creds)
|
||||
.build();
|
||||
|
||||
// Send the email
|
||||
match mailer.send(&email) {
|
||||
Ok(_) => return true,
|
||||
Err(e) => println!("{:?}", e.source()),
|
||||
};
|
||||
false
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ pub mod location;
|
||||
pub mod log;
|
||||
pub mod logbook;
|
||||
pub mod logtype;
|
||||
pub mod mail;
|
||||
pub mod planned_event;
|
||||
pub mod role;
|
||||
pub mod rower;
|
||||
|
@ -14,4 +14,32 @@ impl Role {
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub async fn find_by_id(db: &SqlitePool, name: i32) -> Option<Self> {
|
||||
sqlx::query_as!(
|
||||
Self,
|
||||
"
|
||||
SELECT id, name
|
||||
FROM role
|
||||
WHERE id like ?
|
||||
",
|
||||
name
|
||||
)
|
||||
.fetch_one(db)
|
||||
.await
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub async fn mails_from_role(&self, db: &SqlitePool) -> Vec<String> {
|
||||
let query = format!(
|
||||
"SELECT u.mail
|
||||
FROM user u
|
||||
JOIN user_role ur ON u.id = ur.user_id
|
||||
JOIN role r ON ur.role_id = r.id
|
||||
WHERE r.id = {}",
|
||||
self.id
|
||||
);
|
||||
|
||||
sqlx::query_scalar(&query).fetch_all(db).await.unwrap()
|
||||
}
|
||||
}
|
||||
|
64
src/tera/admin/mail.rs
Normal file
64
src/tera/admin/mail.rs
Normal file
@ -0,0 +1,64 @@
|
||||
use rocket::form::Form;
|
||||
use rocket::fs::TempFile;
|
||||
use rocket::response::{Flash, Redirect};
|
||||
use rocket::{get, request::FlashMessage, routes, Route, State};
|
||||
use rocket::{post, FromForm};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::model::mail::Mail;
|
||||
use crate::model::role::Role;
|
||||
use crate::model::user::AdminUser;
|
||||
use crate::model::user::UserWithRoles;
|
||||
use crate::tera::Config;
|
||||
|
||||
#[get("/mail")]
|
||||
async fn index(
|
||||
db: &State<SqlitePool>,
|
||||
admin: AdminUser,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
) -> Template {
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
let roles = Role::all(db).await;
|
||||
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithRoles::from_user(admin.user, db).await,
|
||||
);
|
||||
context.insert("roles", &roles);
|
||||
|
||||
Template::render("admin/mail", context.into_json())
|
||||
}
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
pub struct MailToSend<'a> {
|
||||
pub(crate) role_id: i32,
|
||||
pub(crate) subject: String,
|
||||
pub(crate) body: String,
|
||||
pub(crate) files: Vec<TempFile<'a>>,
|
||||
}
|
||||
|
||||
#[post("/mail", data = "<data>")]
|
||||
async fn update(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<MailToSend<'_>>,
|
||||
config: &State<Config>,
|
||||
_admin: AdminUser,
|
||||
) -> Flash<Redirect> {
|
||||
let d = data.into_inner();
|
||||
if Mail::send(db, d, config.smtp_pw.clone()).await {
|
||||
return Flash::success(Redirect::to("/admin/mail"), "Mail versendet");
|
||||
} else {
|
||||
return Flash::error(Redirect::to("/admin/mail"), "Fehler");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![index, update]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {}
|
@ -7,6 +7,7 @@ use crate::{
|
||||
};
|
||||
|
||||
pub mod boat;
|
||||
pub mod mail;
|
||||
pub mod planned_event;
|
||||
pub mod user;
|
||||
|
||||
@ -28,6 +29,7 @@ pub fn routes() -> Vec<Route> {
|
||||
let mut ret = Vec::new();
|
||||
ret.append(&mut user::routes());
|
||||
ret.append(&mut boat::routes());
|
||||
ret.append(&mut mail::routes());
|
||||
ret.append(&mut planned_event::routes());
|
||||
ret.append(&mut routes![rss, show_rss]);
|
||||
ret
|
||||
|
@ -206,6 +206,7 @@ fn unauthorized_error() -> Redirect {
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct Config {
|
||||
rss_key: String,
|
||||
smtp_pw: String,
|
||||
}
|
||||
|
||||
pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||
|
Reference in New Issue
Block a user