changes for version 0.1.5 and release of version 0.1.5 #23
5 changed files with 123 additions and 44 deletions
move confirmation email template to a template file
* this allows for easier changes to the template, but also introduces a new source for runtime errors * split the sending of the confirmation email of into a separate function
commit
3c5550376f
|
|
@ -3,3 +3,7 @@
|
|||
[login_provider]
|
||||
type = 'Simple'
|
||||
file_path = 'config/login.toml'
|
||||
|
||||
[email]
|
||||
from = "jobs@localhost"
|
||||
subject = "Test"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use crate::error::default_error_response;
|
||||
use actix_web::body::BoxBody;
|
||||
use actix_web::error::UrlGenerationError;
|
||||
use actix_web::ResponseError;
|
||||
use actix_web::{HttpResponse, ResponseError};
|
||||
use http::StatusCode;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
|
|
@ -14,10 +16,8 @@ pub(crate) enum SaveError {
|
|||
Serialize(#[from] toml::ser::Error),
|
||||
#[error("Could not generate url for confirmation link: {0}")]
|
||||
Url(#[from] UrlGenerationError),
|
||||
#[error("Construction of confirmation E-mail failed!")]
|
||||
Email(#[from] lettre::error::Error),
|
||||
#[error("Sending of confirmation E-mail failed!")]
|
||||
SendMail(#[from] lettre::transport::sendmail::Error),
|
||||
#[error("Could not send Confirmation E-Mail: {0}")]
|
||||
Email(#[from] EmailError),
|
||||
#[error("The Runtime encountered an error!")]
|
||||
Runtime(#[from] tokio::task::JoinError),
|
||||
}
|
||||
|
|
@ -44,10 +44,48 @@ impl ResponseError for SaveError {
|
|||
| SaveError::IO(_)
|
||||
| SaveError::Serialize(_)
|
||||
| SaveError::Url(_)
|
||||
| SaveError::Runtime(_)
|
||||
| SaveError::SendMail(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
| SaveError::Runtime(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
SaveError::AlreadyExists => StatusCode::CONFLICT,
|
||||
SaveError::Email(_) => StatusCode::BAD_REQUEST,
|
||||
SaveError::Email(inner) => inner.status_code(),
|
||||
}
|
||||
}
|
||||
fn error_response(&self) -> HttpResponse<BoxBody> {
|
||||
let status_code = self.status_code();
|
||||
match self {
|
||||
SaveError::AlreadyExists => default_error_response(self, status_code),
|
||||
SaveError::IO(inner) => default_error_response(inner, status_code),
|
||||
SaveError::Persist(inner) => default_error_response(inner, status_code),
|
||||
SaveError::Serialize(inner) => default_error_response(inner, status_code),
|
||||
SaveError::Url(inner) => default_error_response(inner, status_code),
|
||||
SaveError::Email(inner) => inner.error_response(),
|
||||
SaveError::Runtime(inner) => default_error_response(inner, status_code),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub(crate) enum EmailError {
|
||||
#[error("Construction of confirmation E-mail failed!")]
|
||||
Email(#[from] lettre::error::Error),
|
||||
#[error("Sending of confirmation E-mail failed!")]
|
||||
SendMail(#[from] lettre::transport::sendmail::Error),
|
||||
#[error("Error in Confirmation E-Mail template!")]
|
||||
Template(#[from] handlebars::RenderError),
|
||||
}
|
||||
|
||||
impl ResponseError for EmailError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match self {
|
||||
EmailError::Email(_) => StatusCode::BAD_REQUEST,
|
||||
EmailError::SendMail(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
EmailError::Template(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
fn error_response(&self) -> HttpResponse<BoxBody> {
|
||||
let status_code = self.status_code();
|
||||
match self {
|
||||
EmailError::Email(inner) => default_error_response(inner, status_code),
|
||||
EmailError::SendMail(inner) => default_error_response(inner, status_code),
|
||||
EmailError::Template(inner) => default_error_response(inner, status_code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -64,4 +102,10 @@ impl ResponseError for DeleteError {
|
|||
DeleteError::IO(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
fn error_response(&self) -> HttpResponse<BoxBody> {
|
||||
let status_code = self.status_code();
|
||||
match self {
|
||||
DeleteError::IO(inner) => default_error_response(inner, status_code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,18 +13,20 @@ use log::{debug, error, warn};
|
|||
use rand::distributions::DistString;
|
||||
use serde_json::json;
|
||||
use tempfile::NamedTempFile;
|
||||
use url::Url;
|
||||
|
||||
use crate::auth::User;
|
||||
use crate::error::{MultipartFieldError, PresentationError};
|
||||
use crate::job_offers::error::SaveError;
|
||||
use crate::job_offers::error::{EmailError, SaveError};
|
||||
use crate::job_offers::{
|
||||
Attachment, ConfirmationStatus, JobOffer, JobOfferStatus, JobOffers, Link, MutBorrowedJobOffer,
|
||||
ReviewStatus,
|
||||
};
|
||||
use crate::route::job_offer::confirmation::JOBOFFER_CONFIRM_ROUTE;
|
||||
use crate::route::job_offer::create::multipart_form::{multi_field, multi_file, once_field};
|
||||
use crate::route::job_offer::error::SubmissionError;
|
||||
use crate::route::HTML_CONTENT;
|
||||
use crate::server_config::ServerConfig;
|
||||
use crate::server_config::{EmailConfig, ServerConfig};
|
||||
use crate::{template, SubmissionLimiter};
|
||||
|
||||
mod multipart_form;
|
||||
|
|
@ -113,7 +115,7 @@ pub(crate) async fn create_joboffer_post(
|
|||
debug!("validation successful, saving submitted job offer");
|
||||
|
||||
let created_offer =
|
||||
match create_job_offer(&req, job_offer_form, &offers, &config, user.as_ref()).await {
|
||||
match create_job_offer(&req, &hb, job_offer_form, &offers, &config, user.as_ref()).await {
|
||||
Ok(offer) => offer,
|
||||
Err(err) => {
|
||||
if let Some(lease) = submission_lease {
|
||||
|
|
@ -169,13 +171,12 @@ pub(crate) struct JobOfferSubmitForm {
|
|||
|
||||
pub(crate) async fn create_job_offer<'data, 'config>(
|
||||
req: &HttpRequest,
|
||||
hb: &Handlebars<'_>,
|
||||
job_offer_form: JobOfferSubmitForm,
|
||||
offers: &'data JobOffers,
|
||||
config: &'config ServerConfig,
|
||||
user: Option<&User>,
|
||||
) -> Result<MutBorrowedJobOffer<'data, 'static, 'config>, SaveError> {
|
||||
use crate::route::job_offer::confirmation::JOBOFFER_CONFIRM_ROUTE;
|
||||
|
||||
let now_date = crate::util::now();
|
||||
|
||||
let token: String =
|
||||
|
|
@ -217,44 +218,72 @@ pub(crate) async fn create_job_offer<'data, 'config>(
|
|||
|
||||
if !skip_confirmation {
|
||||
if let Some(email_config) = &config.config.email {
|
||||
let to_mailbox = Mailbox::new(None, job_offer_form.contact);
|
||||
|
||||
let confirm_url = req.url_for(JOBOFFER_CONFIRM_ROUTE, &[created_offer.id(), &token])?;
|
||||
|
||||
let message = lettre::Message::builder()
|
||||
.from(email_config.from.to_owned())
|
||||
.to(to_mailbox)
|
||||
.subject(&email_config.subject)
|
||||
.singlepart(SinglePart::plain(format!(
|
||||
include_str!("confirmation_email_template.txt"),
|
||||
confirm_url
|
||||
)))?;
|
||||
lettre::AsyncSendmailTransport::new().send(message).await?;
|
||||
|
||||
let message = lettre::Message::builder()
|
||||
.from(email_config.from.to_owned())
|
||||
.to(email_config.from.to_owned())
|
||||
.subject(&email_config.subject)
|
||||
.singlepart(SinglePart::plain(
|
||||
"Automatischer Hinweis: Eine neue Stellenausschreibung wurde zur Jobbörse eingereicht!".to_owned()
|
||||
));
|
||||
|
||||
match message {
|
||||
Ok(msg) => {
|
||||
if let Err(err) = lettre::AsyncSendmailTransport::new().send(msg).await {
|
||||
warn!("Failed to send remainder {}", err);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Failed to construct reminder {}", err)
|
||||
}
|
||||
}
|
||||
send_confirmation_email(
|
||||
hb,
|
||||
job_offer_form.contact,
|
||||
email_config,
|
||||
&EmailData {
|
||||
confirmation_link: confirm_url,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(created_offer)
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
struct EmailData {
|
||||
#[serde(serialize_with = "crate::route::url_as_string")]
|
||||
confirmation_link: Url,
|
||||
}
|
||||
|
||||
async fn send_confirmation_email(
|
||||
hb: &Handlebars<'_>,
|
||||
contact_address: Address,
|
||||
email_config: &EmailConfig,
|
||||
email_data: &EmailData,
|
||||
) -> Result<(), EmailError> {
|
||||
let to_mailbox = Mailbox::new(None, contact_address);
|
||||
|
||||
let email_body = hb
|
||||
.render(template::EMAIL_PLAIN, &email_data)
|
||||
.map_err(EmailError::from)?;
|
||||
|
||||
let message = lettre::Message::builder()
|
||||
.from(email_config.from.to_owned())
|
||||
.to(to_mailbox)
|
||||
.subject(&email_config.subject)
|
||||
.singlepart(SinglePart::plain(email_body))
|
||||
.map_err(EmailError::from)?;
|
||||
lettre::AsyncSendmailTransport::new().send(message).await?;
|
||||
|
||||
let message = lettre::Message::builder()
|
||||
.from(email_config.from.to_owned())
|
||||
.to(email_config.from.to_owned())
|
||||
.subject(&email_config.subject)
|
||||
.singlepart(SinglePart::plain(
|
||||
"Automatischer Hinweis: Eine neue Stellenausschreibung wurde zur Jobbörse eingereicht!"
|
||||
.to_owned(),
|
||||
));
|
||||
|
||||
match message {
|
||||
Ok(msg) => {
|
||||
if let Err(err) = lettre::AsyncSendmailTransport::new().send(msg).await {
|
||||
warn!("Failed to send remainder {}", err);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Failed to construct reminder {}", err)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl JobOfferSubmitForm {
|
||||
/// Convert the Multipart struct representing multipart form-data
|
||||
/// into structured form data
|
||||
|
|
|
|||
|
|
@ -7,3 +7,5 @@ pub(crate) const JOBOFFER_CONFIRM_SUBMISSION_SUCCESS: &str = "job_offer/submissi
|
|||
pub(crate) const JOBOFFER_REJECT_SUBMISSION_SUCCESS: &str = "job_offer/submission-rejected-success";
|
||||
pub(crate) const AUTH_LOGIN: &str = "auth/login";
|
||||
pub(crate) const LICENCES: &str = "licenses";
|
||||
|
||||
pub const EMAIL_PLAIN: &'static str = "email/plaintext";
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Es wurde eine Stellenausschreibung mit dieser E-Mail Address als Kontakt-Address
|
|||
|
||||
Bitte überprüfen Sie die Stellenausschreibung unter dem folgenden Link.
|
||||
|
||||
{}
|
||||
{{{confirmation_link}}}
|
||||
|
||||
Dort können sie die Stellenausschreibung bestätigen oder zurückziehen.
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue