getting things to work a bit
This commit is contained in:
parent
760e46a2d1
commit
6913426619
20 changed files with 4607 additions and 1880 deletions
3013
Cargo.lock
generated
3013
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
17
Cargo.toml
17
Cargo.toml
|
@ -21,3 +21,20 @@ uuid = "1.6.1"
|
||||||
sanitize-filename = "0.5.0"
|
sanitize-filename = "0.5.0"
|
||||||
lettre = { version = "0.11.3", features = ["smtp-transport"] }
|
lettre = { version = "0.11.3", features = ["smtp-transport"] }
|
||||||
markup = "0.15.0"
|
markup = "0.15.0"
|
||||||
|
maud = { version = "0.26.0", features = ["actix-web"] }
|
||||||
|
log4rs = "1.3.0"
|
||||||
|
actix-cors = "0.7.0"
|
||||||
|
tracing = "0.1.40"
|
||||||
|
tracing-subscriber = { version = "0.3.18", features = ["fmt", "std", "chrono", "time", "local-time", "env-filter"] }
|
||||||
|
tracing-appender = "0.2.3"
|
||||||
|
actix-files = "0.6.6"
|
||||||
|
tracing-actix-web = "0.7.14"
|
||||||
|
color-eyre = "0.6.3"
|
||||||
|
pretty_assertions = "1.4.1"
|
||||||
|
sqlx = { version = "0.8.2", features = ["sqlite"] }
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
opt-level = 0
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = 3
|
||||||
|
|
|
@ -59,4 +59,4 @@ Our vision is to change the world from the heart of America by providing discipl
|
||||||
## Our Mission
|
## Our Mission
|
||||||
We do that by, connecting teens with the truth of Jesus Christ…teaching and equipping them to live out the Great Commission.
|
We do that by, connecting teens with the truth of Jesus Christ…teaching and equipping them to live out the Great Commission.
|
||||||
|
|
||||||
{{ disciplemaking }}
|
{{ disciplemaking() }}
|
||||||
|
|
0
data.db
Normal file
0
data.db
Normal file
2
justfile
2
justfile
|
@ -1,7 +1,7 @@
|
||||||
default:
|
default:
|
||||||
just --list
|
just --list
|
||||||
build:
|
build:
|
||||||
zola build
|
tailwindcss -i static/css/base.css -o static/css/main.css && zola build
|
||||||
serve:
|
serve:
|
||||||
zola serve
|
zola serve
|
||||||
uglify:
|
uglify:
|
||||||
|
|
|
@ -1,318 +1,360 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use actix_multipart::form::{text::Text, MultipartForm};
|
use actix_multipart::form::{text::Text, MultipartForm};
|
||||||
use actix_web::{post, HttpResponse};
|
use actix_web::{http::StatusCode, post, HttpResponse, HttpResponseBuilder};
|
||||||
use lettre::{
|
use color_eyre::eyre::{Context, Result};
|
||||||
message::MultiPart,
|
use futures::FutureExt;
|
||||||
transport::smtp::authentication::{Credentials, Mechanism},
|
use lettre::{message::SinglePart, Message};
|
||||||
Message, SmtpTransport, Transport,
|
use maud::{html, Markup, DOCTYPE};
|
||||||
};
|
use reqwest::Client;
|
||||||
|
use tracing::{error, info};
|
||||||
|
|
||||||
use super::errors::ApiError;
|
#[derive(Debug, MultipartForm)]
|
||||||
|
|
||||||
#[derive(Debug, MultipartForm, Default)]
|
|
||||||
struct CampForm {
|
struct CampForm {
|
||||||
#[multipart(rename = "first-name")]
|
#[multipart(rename = "first-name")]
|
||||||
first_name: Option<Text<String>>,
|
first_name: Text<String>,
|
||||||
#[multipart(rename = "last-name")]
|
#[multipart(rename = "last-name")]
|
||||||
last_name: Option<Text<String>>,
|
last_name: Text<String>,
|
||||||
#[multipart(rename = "parent-first-name")]
|
#[multipart(rename = "parent-first-name")]
|
||||||
parent_first_name: Option<Text<String>>,
|
parent_first_name: Text<String>,
|
||||||
#[multipart(rename = "parent-last-name")]
|
#[multipart(rename = "parent-last-name")]
|
||||||
parent_last_name: Option<Text<String>>,
|
parent_last_name: Text<String>,
|
||||||
birthdate: Option<Text<String>>,
|
#[multipart(rename = "birth-date")]
|
||||||
gender: Option<Text<String>>,
|
birthdate: Text<String>,
|
||||||
street: Option<Text<String>>,
|
gender: Text<String>,
|
||||||
city: Option<Text<String>>,
|
street: Text<String>,
|
||||||
state: Option<Text<String>>,
|
city: Text<String>,
|
||||||
zip: Option<Text<i32>>,
|
state: Text<String>,
|
||||||
|
zip: Text<i32>,
|
||||||
#[multipart(rename = "parent-phone")]
|
#[multipart(rename = "parent-phone")]
|
||||||
parent_phone: Option<Text<String>>,
|
parent_phone: Text<String>,
|
||||||
#[multipart(rename = "parent-email")]
|
#[multipart(rename = "parent-email")]
|
||||||
parent_email: Option<Text<String>>,
|
parent_email: Text<String>,
|
||||||
grade: Option<Text<String>>,
|
grade: Text<String>,
|
||||||
shirt: Option<Text<String>>,
|
shirt: Text<String>,
|
||||||
allergies: Option<Text<String>>,
|
allergies: Text<String>,
|
||||||
week: Option<Text<String>>,
|
week: Text<String>,
|
||||||
registration: Option<Text<String>>,
|
registration: Text<String>,
|
||||||
|
#[multipart(rename = "health-form")]
|
||||||
|
health_form: Text<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/camp-form")]
|
impl From<&CampForm> for HashMap<i32, String> {
|
||||||
|
fn from(form: &CampForm) -> Self {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
map.insert(63, format!("{} {}", form.first_name.0, form.last_name.0));
|
||||||
|
map.insert(
|
||||||
|
64,
|
||||||
|
format!("{} {}", form.parent_first_name.0, form.parent_last_name.0),
|
||||||
|
);
|
||||||
|
map.insert(65, form.parent_phone.0.clone());
|
||||||
|
map.insert(66, form.parent_email.0.clone().clone());
|
||||||
|
map.insert(67, form.birthdate.0.clone());
|
||||||
|
map.insert(69, form.gender.0.clone());
|
||||||
|
map.insert(70, form.street.0.clone());
|
||||||
|
map.insert(71, form.city.0.clone());
|
||||||
|
map.insert(72, form.state.0.clone());
|
||||||
|
map.insert(73, form.zip.0.clone().to_string());
|
||||||
|
map.insert(74, form.grade.0.clone());
|
||||||
|
map.insert(75, form.week.0.clone());
|
||||||
|
map.insert(76, form.shirt.0.clone());
|
||||||
|
map.insert(77, form.registration.0.clone());
|
||||||
|
map.insert(115, form.health_form.0.clone());
|
||||||
|
map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CampForm {
|
||||||
|
fn build_email(&self) -> Markup {
|
||||||
|
html! {
|
||||||
|
(DOCTYPE)
|
||||||
|
meta charset="utf-8";
|
||||||
|
html {
|
||||||
|
head {
|
||||||
|
title { (self.first_name.0) " " (self.last_name.0) " signed up for camp!" }
|
||||||
|
style {
|
||||||
|
"table { border-collapse: collapse; width: 100% }"
|
||||||
|
"td, th { padding: 8px }"
|
||||||
|
"td { text-align: left; width: 70%; word-wrap: break-word }"
|
||||||
|
"th { text-align: right; border-right: 1px solid #ddd }"
|
||||||
|
"tr { border-bottom: 1px solid #ddd }"
|
||||||
|
"h1 { text-align: center }"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
h1 { "Camp form for " (self.first_name.0) " " (self.last_name.0) "!" }
|
||||||
|
hr;
|
||||||
|
table {
|
||||||
|
tr {
|
||||||
|
th { "Name" }
|
||||||
|
td { (self.first_name.0) " " (self.last_name.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Parent" }
|
||||||
|
td { (self.parent_first_name.0) " " (self.parent_last_name.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Birthdate" }
|
||||||
|
td { (self.birthdate.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Gender" }
|
||||||
|
td { (self.gender.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Street" }
|
||||||
|
td { (self.street.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "City" }
|
||||||
|
td { (self.city.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "State" }
|
||||||
|
td { (self.state.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Zip" }
|
||||||
|
td { (self.zip.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Parent Phone" }
|
||||||
|
td { (self.parent_phone.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Parent Email" }
|
||||||
|
td { (self.parent_email.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Grade" }
|
||||||
|
td { (self.grade.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Camper Allergies" }
|
||||||
|
td { (self.allergies.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "T-Shirt Size" }
|
||||||
|
td { (self.shirt.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Week Choice" }
|
||||||
|
td { (self.week.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Health Form" }
|
||||||
|
td { (self.health_form.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Registration" }
|
||||||
|
td { (self.registration.0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_email(&self) -> Result<Message> {
|
||||||
|
let first = self.first_name.clone();
|
||||||
|
let last = self.last_name.clone();
|
||||||
|
let email_subject = format!("{} {} signed up for camp!", first, last);
|
||||||
|
info!("{first} {last} signed up for camp!");
|
||||||
|
let email = self.build_email();
|
||||||
|
// let temp_file = self.get_temp_file();
|
||||||
|
// let multi = if let Some((file, path, content_type)) = temp_file {
|
||||||
|
// let filebody = fs::read(path);
|
||||||
|
// let content_type =
|
||||||
|
// ContentType::parse(&content_type.unwrap_or(String::from("image/jpg"))).unwrap();
|
||||||
|
// let attachment = Attachment::new(file).body(filebody.unwrap(), content_type);
|
||||||
|
// // info!(?attachment);
|
||||||
|
// MultiPart::mixed()
|
||||||
|
// .singlepart(SinglePart::html(email.into_string()))
|
||||||
|
// .singlepart(attachment)
|
||||||
|
// } else {
|
||||||
|
// MultiPart::alternative_plain_html(String::from("Testing"), email.into_string())
|
||||||
|
// };
|
||||||
|
|
||||||
|
let singlepart = SinglePart::html(email.into_string());
|
||||||
|
|
||||||
|
Message::builder()
|
||||||
|
.from(
|
||||||
|
"TFC ADMIN <no-reply@mail.tfcconnection.org>"
|
||||||
|
.parse()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.to("Chris Cochrun <chris@tfcconnection.org>".parse().unwrap())
|
||||||
|
.to("Ethan Rose <ethan@tfcconnection.org>".parse().unwrap())
|
||||||
|
.subject(email_subject)
|
||||||
|
.singlepart(singlepart)
|
||||||
|
// .multipart(multi)
|
||||||
|
.wrap_err("problemss")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/api/camp-form")]
|
||||||
pub async fn camp_form(MultipartForm(form): MultipartForm<CampForm>) -> HttpResponse {
|
pub async fn camp_form(MultipartForm(form): MultipartForm<CampForm>) -> HttpResponse {
|
||||||
log::info!("a new form");
|
let full_name = format!("{} {}", form.first_name.0, form.last_name.0);
|
||||||
let first = form
|
let map = (&form).into();
|
||||||
.first_name
|
let future = store_camp_form(map);
|
||||||
.as_ref()
|
actix_rt::spawn(future.map(|s| match s {
|
||||||
.unwrap_or(&Text(String::from("")))
|
Ok(_) => info!("Successfully posted to nextcloud tables"),
|
||||||
.0
|
Err(e) => log::error!("Error in posting camp data: {:?}", e),
|
||||||
.clone();
|
}));
|
||||||
let last = form
|
|
||||||
.last_name
|
let email = form.prepare_email();
|
||||||
.as_ref()
|
match email {
|
||||||
.unwrap_or(&Text(String::from("")))
|
Ok(m) => {
|
||||||
.0
|
let sent = crate::email::send_email(m);
|
||||||
.clone();
|
actix_rt::spawn(sent.map(|s| match s {
|
||||||
let full_name = format!("{} {}", first, last);
|
Ok(_) => info!("Successfully sent form to email!"),
|
||||||
let email_subject = format!("{} {} signed up for camp!", first, last);
|
Err(e) => error!("There was an erroring sending form to email: {e}"),
|
||||||
let parent = format!(
|
}));
|
||||||
"{} {}",
|
}
|
||||||
form.parent_first_name
|
Err(e) => error!("error sending email {e}"),
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone(),
|
|
||||||
form.parent_last_name
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone()
|
|
||||||
);
|
|
||||||
let birthdate = form
|
|
||||||
.birthdate
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let gender = form
|
|
||||||
.gender
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let street = form
|
|
||||||
.street
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let city = form
|
|
||||||
.city
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let state = form
|
|
||||||
.state
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let zip = form.zip.as_ref().unwrap_or(&Text(0)).0;
|
|
||||||
let parent_phone = form
|
|
||||||
.parent_phone
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let parent_email = form
|
|
||||||
.parent_email
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let grade = form
|
|
||||||
.grade
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let shirt = form
|
|
||||||
.shirt
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let allergies = form
|
|
||||||
.allergies
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let week = form
|
|
||||||
.week
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let registration = form
|
|
||||||
.registration
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let reg = registration.clone();
|
|
||||||
log::info!("{first} {last} signed up for camp!");
|
|
||||||
let email = markup::new! {
|
|
||||||
@markup::doctype()
|
|
||||||
html {
|
|
||||||
head {
|
|
||||||
title { @format!("{} {} signed up for camp!", first, last) }
|
|
||||||
style {
|
|
||||||
"table { border-collapse: collapse; width: 100% }"
|
|
||||||
"td, th { padding: 8px }"
|
|
||||||
"td { text-align: left; width: 70%; word-wrap: break-word }"
|
|
||||||
"th { text-align: right; border-right: 1px solid #ddd }"
|
|
||||||
"tr { border-bottom: 1px solid #ddd }"
|
|
||||||
"h1 { text-align: center }"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
h1 { @format!("Camp form for {} {}!", first, last) }
|
|
||||||
hr;
|
|
||||||
table {
|
|
||||||
tr {
|
|
||||||
th { "Name" }
|
|
||||||
td { @format!("{} {}", first, last) }
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
th { "Parent" }
|
|
||||||
td { @parent }
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
th { "Birthdate" }
|
|
||||||
td { @birthdate }
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
th { "Gender" }
|
|
||||||
td { @gender }
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
th { "Street" }
|
|
||||||
td { @street }
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
th { "City" }
|
|
||||||
td { @city }
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
th { "State" }
|
|
||||||
td { @state }
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
th { "Zip" }
|
|
||||||
td { @zip }
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
th { "Parent Phone" }
|
|
||||||
td { @parent_phone }
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
th { "Parent Email" }
|
|
||||||
td { @parent_email }
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
th { "Grade" }
|
|
||||||
td { @grade }
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
th { "Camper Allergies" }
|
|
||||||
td { @allergies }
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
th { "T-Shirt Size" }
|
|
||||||
td { @shirt }
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
th { "Week Choice" }
|
|
||||||
td { @week }
|
|
||||||
}
|
|
||||||
tr {
|
|
||||||
th { "Registration" }
|
|
||||||
td { @registration }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let multi = MultiPart::alternative_plain_html(
|
|
||||||
String::from("A camp form was filled out!"),
|
|
||||||
email.to_string(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Ok(m) = Message::builder()
|
match form.health_form.0.as_str() {
|
||||||
.from(
|
|
||||||
"TFC ADMIN <no-reply@mail.tfcconnection.org>"
|
|
||||||
.parse()
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
.to("Chris Cochrun <chris@tfcconnection.org>".parse().unwrap())
|
|
||||||
// .to("Ethan Rose <ethan@tfcconnection.org>".parse().unwrap())
|
|
||||||
.subject(email_subject)
|
|
||||||
.multipart(multi)
|
|
||||||
{
|
|
||||||
let sender = SmtpTransport::relay("mail.tfcconnection.org")
|
|
||||||
.ok()
|
|
||||||
.unwrap()
|
|
||||||
.credentials(Credentials::new(
|
|
||||||
"no-reply@mail.tfcconnection.org".to_owned(),
|
|
||||||
"r9f36mNZFtiW4f".to_owned(),
|
|
||||||
))
|
|
||||||
.authentication(vec![Mechanism::Plain])
|
|
||||||
.build();
|
|
||||||
match sender.send(&m) {
|
|
||||||
Ok(res) => log::info!("{:?}", res),
|
|
||||||
Err(e) => log::error!("{e}"),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log::info!("Email incorrect");
|
|
||||||
}
|
|
||||||
|
|
||||||
match reg.as_str() {
|
|
||||||
"now" => {
|
"now" => {
|
||||||
log::info!("Sending them to pay for registration now");
|
info!("Sending them to fill out the health form");
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.insert_header(("Access-Control-Expose-Headers", "*"))
|
.insert_header(("Access-Control-Expose-Headers", "*"))
|
||||||
.insert_header((
|
.insert_header((
|
||||||
"HX-Redirect",
|
"HX-Redirect",
|
||||||
"https://secure.myvanco.com/L-Z772/campaign/C-13JPJ",
|
format!(
|
||||||
|
"/camp-health-form/?registration={}",
|
||||||
|
form.registration.0.as_str()
|
||||||
|
),
|
||||||
))
|
))
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
"full" => {
|
"later" => match form.registration.0.as_str() {
|
||||||
log::info!("Sending them to pay for the full registration now");
|
"now" => {
|
||||||
HttpResponse::Ok()
|
info!("Sending them to pay for registration now");
|
||||||
.insert_header(("Access-Control-Expose-Headers", "*"))
|
HttpResponse::Ok()
|
||||||
.insert_header((
|
.insert_header(("Access-Control-Expose-Headers", "*"))
|
||||||
"HX-Redirect",
|
.insert_header((
|
||||||
"https://secure.myvanco.com/L-Z772/campaign/C-13JQE",
|
"HX-Redirect",
|
||||||
))
|
"https://secure.myvanco.com/L-Z772/campaign/C-13JPJ",
|
||||||
.finish()
|
))
|
||||||
}
|
.finish()
|
||||||
"later" => {
|
}
|
||||||
log::info!("{} would like to pay later", full_name);
|
"full" => {
|
||||||
let html = markup::new! {
|
info!("Sending them to pay for the full registration now");
|
||||||
div {
|
HttpResponse::Ok()
|
||||||
class { "mt-8" }
|
.insert_header(("Access-Control-Expose-Headers", "*"))
|
||||||
h2 {
|
.insert_header((
|
||||||
@format!("Thank you, {}!", full_name)
|
"HX-Redirect",
|
||||||
|
"https://secure.myvanco.com/L-Z772/campaign/C-13JQE",
|
||||||
|
))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
"later" => {
|
||||||
|
info!("{} would like to pay later", full_name);
|
||||||
|
let html = html! {
|
||||||
|
div class="mt-8" {
|
||||||
|
h2 {
|
||||||
|
"Thank you, " (full_name) "!"
|
||||||
|
}
|
||||||
|
p { "Can't wait to see you at camp!" }
|
||||||
|
p {
|
||||||
|
class { "" }
|
||||||
|
"If you'd like to pay for your registration go to the donate tab in the top right when you are ready and find the camp registration option."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
p { "Can't wait to see you at camp!" }
|
};
|
||||||
p {
|
HttpResponse::Ok().body(html.into_string())
|
||||||
class { "" }
|
}
|
||||||
"If you'd like to pay for your registration go to the donate tab in the top right when you are ready and find the camp registration option."
|
_ => {
|
||||||
|
log::error!("Got registration error.....");
|
||||||
|
let html = html! {
|
||||||
|
div class="mt-8" {
|
||||||
|
h2 {
|
||||||
|
"Thank you, " (full_name) "!"
|
||||||
|
}
|
||||||
|
p { "Can't wait to see you at camp!" }
|
||||||
|
p {
|
||||||
|
class { "" }
|
||||||
|
"If you'd like to pay for your registration go to the donate tab in the top right when you are ready and find the camp registration option."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
};
|
HttpResponse::Ok().body(html.into_string())
|
||||||
HttpResponse::Ok().body(html.to_string())
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
log::error!("Got registration error.....");
|
log::error!("Unknown selection for health. We don't know where to send the user.");
|
||||||
let html = markup::new! {
|
HttpResponseBuilder::new(StatusCode::IM_A_TEAPOT)
|
||||||
div {
|
.body("Unknown selection for health. We don't know where to send the user.")
|
||||||
class { "mt-8" }
|
|
||||||
h2 {
|
|
||||||
@format!("Thank you, {}!", full_name)
|
|
||||||
}
|
|
||||||
p { "Can't wait to see you at camp!" }
|
|
||||||
p {
|
|
||||||
class { "" }
|
|
||||||
"If you'd like to pay for your registration go to the donate tab in the top right when you are ready and find the camp registration option."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
HttpResponse::Ok().body(html.to_string())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn store_camp_form(form: CampForm) -> Result<(), ApiError> {
|
async fn store_camp_form(map: HashMap<i32, String>) -> Result<()> {
|
||||||
|
let request = Client::new();
|
||||||
|
let mut json = HashMap::new();
|
||||||
|
json.insert("data", map);
|
||||||
|
request
|
||||||
|
.post("https://staff.tfcconnection.org/apps/tables/api/1/tables/5/rows")
|
||||||
|
.basic_auth("chris", Some("2VHeGxeC^Zf9KqFK^G@Pt!zu2q^6@b"))
|
||||||
|
.header("OCS-APIRequest", "true")
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.json(&json)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use actix_web::test;
|
||||||
|
|
||||||
|
fn form() -> CampForm {
|
||||||
|
CampForm {
|
||||||
|
first_name: Text("Frodo".into()),
|
||||||
|
last_name: Text("Braggins".into()),
|
||||||
|
parent_first_name: Text("Bilbo".into()),
|
||||||
|
parent_last_name: Text("Braggins".into()),
|
||||||
|
birthdate: Text(String::from("1845-09-12")),
|
||||||
|
gender: Text(String::from("male")),
|
||||||
|
street: Text(String::from("1234 Bag End")),
|
||||||
|
city: Text(String::from("The Shire")),
|
||||||
|
state: Text(String::from("Hobbiton")),
|
||||||
|
zip: Text(88888),
|
||||||
|
parent_phone: Text(String::from("1234567898")),
|
||||||
|
parent_email: Text(String::from("bilbo@hobbits.com")),
|
||||||
|
grade: Text(String::from("junior")),
|
||||||
|
shirt: Text(String::from("medium")),
|
||||||
|
allergies: Text(String::from("Cool beans")),
|
||||||
|
week: Text(String::from("1")),
|
||||||
|
registration: Text(String::from("later")),
|
||||||
|
health_form: Text(String::from("I guess")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
async fn test_nc_post() {
|
||||||
|
let form = form();
|
||||||
|
assert!(!form.first_name.is_empty());
|
||||||
|
let map = HashMap::from(&form);
|
||||||
|
let res = store_camp_form(map).await;
|
||||||
|
match res {
|
||||||
|
Ok(_) => assert!(true),
|
||||||
|
Err(e) => assert!(false, "Failed storing test: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
async fn test_email() {
|
||||||
|
let form = form();
|
||||||
|
assert!(!form.first_name.is_empty());
|
||||||
|
match form.prepare_email() {
|
||||||
|
Ok(m) => {
|
||||||
|
assert!(crate::email::send_email(m).await.is_ok())
|
||||||
|
}
|
||||||
|
Err(e) => assert!(false, "Failed emailing test: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
168
src/api/contact.rs
Normal file
168
src/api/contact.rs
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
use std::{
|
||||||
|
collections::{BTreeMap, HashMap},
|
||||||
|
fs,
|
||||||
|
};
|
||||||
|
|
||||||
|
use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
|
||||||
|
use actix_web::{post, web, HttpResponse};
|
||||||
|
use color_eyre::{eyre::eyre, Result};
|
||||||
|
use lettre::{
|
||||||
|
message::{header::ContentType, Attachment, MultiPart, SinglePart},
|
||||||
|
Message,
|
||||||
|
};
|
||||||
|
use maud::{html, Markup, PreEscaped, DOCTYPE};
|
||||||
|
use reqwest::Client;
|
||||||
|
use serde_json::json;
|
||||||
|
use sqlx::SqliteConnection;
|
||||||
|
use tracing::{error, info};
|
||||||
|
|
||||||
|
#[derive(Debug, MultipartForm)]
|
||||||
|
struct ContactForm {
|
||||||
|
name: Text<String>,
|
||||||
|
email: Text<String>,
|
||||||
|
staff: Text<String>,
|
||||||
|
message: Text<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&ContactForm> for HashMap<i32, String> {
|
||||||
|
fn from(form: &ContactForm) -> Self {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
map.insert(169, form.name.0.clone());
|
||||||
|
map.insert(169, form.email.0.clone());
|
||||||
|
map.insert(169, form.staff.0.clone());
|
||||||
|
map.insert(169, form.message.0.clone());
|
||||||
|
map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContactForm {
|
||||||
|
async fn build_email(&self) -> Markup {
|
||||||
|
html! {
|
||||||
|
(DOCTYPE)
|
||||||
|
meta charset="utf-8";
|
||||||
|
html {
|
||||||
|
head {
|
||||||
|
title { (self.name.0) " filled out a contact form!" }
|
||||||
|
style {
|
||||||
|
"table { border-collapse: collapse; width: 100% }"
|
||||||
|
"td, th { padding: 8px }"
|
||||||
|
"td { text-align: left; width: 70%; word-wrap: break-word }"
|
||||||
|
"th { text-align: right; border-right: 1px solid #ddd }"
|
||||||
|
"tr { border-bottom: 1px solid #ddd }"
|
||||||
|
"h1 { text-align: center }"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
h1 { "Contact form for " (self.name.0) "!" }
|
||||||
|
hr;
|
||||||
|
p { "Email: " (self.email.0) }
|
||||||
|
p { "To: " (self.staff.0) }
|
||||||
|
hr;
|
||||||
|
p { (self.message.0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn store_form(&self) -> Result<()> {
|
||||||
|
let client = Client::new();
|
||||||
|
let map = HashMap::from(self);
|
||||||
|
let mut json = HashMap::new();
|
||||||
|
json.insert("data", map);
|
||||||
|
|
||||||
|
let res = client
|
||||||
|
.post("https://staff.tfcconnection.org/ocs/v2.php/apps/tables/api/2/tables/140/rows")
|
||||||
|
.basic_auth("chris", Some("2VHeGxeC^Zf9KqFK^G@Pt!zu2q^6@b"))
|
||||||
|
.header("OCS-APIRequest", "true")
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.json(&json)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
if res.status().is_success() {
|
||||||
|
let res = res.text().await.unwrap();
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(eyre!(
|
||||||
|
"Problem in storing data: {:?}",
|
||||||
|
res.error_for_status()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_email(&mut self) -> Result<()> {
|
||||||
|
let name = self.name.clone();
|
||||||
|
let email_subject = format!("Contact form for {}!", name);
|
||||||
|
info!("{name} contact form!");
|
||||||
|
let email = self.build_email().await;
|
||||||
|
let email = SinglePart::html(email.into_string());
|
||||||
|
|
||||||
|
if let Ok(m) = Message::builder()
|
||||||
|
.from(
|
||||||
|
"TFC ADMIN <no-reply@mail.tfcconnection.org>"
|
||||||
|
.parse()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.to("Chris Cochrun <chris@tfcconnection.org>".parse().unwrap())
|
||||||
|
.to("Ethan Rose <ethan@tfcconnection.org>".parse().unwrap())
|
||||||
|
.subject(email_subject)
|
||||||
|
.singlepart(email)
|
||||||
|
{
|
||||||
|
crate::email::send_email(m).await
|
||||||
|
} else {
|
||||||
|
Err(eyre!("Email incorrect"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/api/contact-form")]
|
||||||
|
pub async fn contact_form(MultipartForm(mut form): MultipartForm<ContactForm>) -> HttpResponse {
|
||||||
|
// match form.store_form().await {
|
||||||
|
// Ok(_) => info!("Successfully sent form to nextcloud!"),
|
||||||
|
// Err(e) => error!("There was an erroring sending form to nextcloud: {e}"),
|
||||||
|
// }
|
||||||
|
match form.send_email().await {
|
||||||
|
Ok(_) => info!("Successfully sent email"),
|
||||||
|
Err(e) => error!("There was an error sending the email: {e}"),
|
||||||
|
}
|
||||||
|
HttpResponse::Ok().body("Thank you! We will get back with you shortly!")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use actix_web::test;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use sqlx::Connection;
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn form() -> ContactForm {
|
||||||
|
ContactForm {
|
||||||
|
name: Text(String::from("Bilbo Braggins")),
|
||||||
|
email: Text(String::from("biblo@hobbits.us")),
|
||||||
|
staff: Text(String::from("Uncle")),
|
||||||
|
message: Text(String::from("Very")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
async fn test_nc_post() {
|
||||||
|
let form = form();
|
||||||
|
assert!(!form.name.is_empty());
|
||||||
|
let res = form.store_form().await;
|
||||||
|
match res {
|
||||||
|
Ok(_) => assert!(true, "passed storing test"),
|
||||||
|
Err(e) => assert!(false, "Failed storing test: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
async fn test_email() {
|
||||||
|
let mut form = form();
|
||||||
|
assert!(!form.name.is_empty());
|
||||||
|
match form.send_email().await {
|
||||||
|
Ok(_) => assert!(true, "passed emailing test"),
|
||||||
|
Err(e) => assert!(false, "Failed emailing test: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,8 +18,3 @@ pub struct ApiError {
|
||||||
pub cause: Option<String>,
|
pub cause: Option<String>,
|
||||||
pub error_type: ApiErrorType,
|
pub error_type: ApiErrorType,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
pub struct ApiErrorResponse {
|
|
||||||
pub error: String,
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,454 +1,381 @@
|
||||||
use std::fs;
|
use std::{collections::HashMap, fs};
|
||||||
|
|
||||||
use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
|
use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
|
||||||
use actix_web::{post, HttpResponse};
|
use actix_web::{post, HttpResponse};
|
||||||
|
use color_eyre::{
|
||||||
|
eyre::{eyre, Context},
|
||||||
|
Result,
|
||||||
|
};
|
||||||
|
use futures::FutureExt;
|
||||||
use lettre::{
|
use lettre::{
|
||||||
message::{header::ContentType, Attachment, MultiPart, SinglePart},
|
message::{header::ContentType, Attachment, MultiPart, SinglePart},
|
||||||
transport::smtp::authentication::{Credentials, Mechanism},
|
Message,
|
||||||
Message, SmtpTransport, Transport,
|
|
||||||
};
|
};
|
||||||
|
use maud::{html, Markup, DOCTYPE};
|
||||||
|
use reqwest::Client;
|
||||||
|
use tracing::{error, info};
|
||||||
|
|
||||||
#[derive(Debug, MultipartForm, Default)]
|
use crate::email::send_email;
|
||||||
|
|
||||||
|
#[derive(Debug, MultipartForm)]
|
||||||
struct HealthForm {
|
struct HealthForm {
|
||||||
#[multipart(rename = "first-name")]
|
#[multipart(rename = "first-name")]
|
||||||
first_name: Option<Text<String>>,
|
first_name: Text<String>,
|
||||||
#[multipart(rename = "last-name")]
|
#[multipart(rename = "last-name")]
|
||||||
last_name: Option<Text<String>>,
|
last_name: Text<String>,
|
||||||
#[multipart(rename = "parent-first-name")]
|
#[multipart(rename = "parent-first-name")]
|
||||||
parent_first_name: Option<Text<String>>,
|
parent_first_name: Text<String>,
|
||||||
#[multipart(rename = "parent-last-name")]
|
#[multipart(rename = "parent-last-name")]
|
||||||
parent_last_name: Option<Text<String>>,
|
parent_last_name: Text<String>,
|
||||||
birthdate: Option<Text<String>>,
|
#[multipart(rename = "birth-date")]
|
||||||
street: Option<Text<String>>,
|
birthdate: Text<String>,
|
||||||
city: Option<Text<String>>,
|
street: Text<String>,
|
||||||
state: Option<Text<String>>,
|
city: Text<String>,
|
||||||
zip: Option<Text<String>>,
|
state: Text<String>,
|
||||||
|
zip: Text<String>,
|
||||||
#[multipart(rename = "cell-phone")]
|
#[multipart(rename = "cell-phone")]
|
||||||
parent_cellphone: Option<Text<String>>,
|
parent_cellphone: Text<String>,
|
||||||
homephone: Option<Text<String>>,
|
#[multipart(rename = "home-phone")]
|
||||||
|
homephone: Text<String>,
|
||||||
#[multipart(rename = "additional-emergency-contact")]
|
#[multipart(rename = "additional-emergency-contact")]
|
||||||
contact: Option<Text<String>>,
|
contact: Text<String>,
|
||||||
#[multipart(rename = "addtional-emergency-contact-phone")]
|
#[multipart(rename = "additional-emergency-contact-phone")]
|
||||||
contact_phone: Option<Text<String>>,
|
contact_phone: Text<String>,
|
||||||
doctorname: Option<Text<String>>,
|
#[multipart(rename = "doctor-name")]
|
||||||
doctorcity: Option<Text<String>>,
|
doctorname: Text<String>,
|
||||||
doctorphone: Option<Text<String>>,
|
#[multipart(rename = "doctor-city")]
|
||||||
|
doctorcity: Text<String>,
|
||||||
|
#[multipart(rename = "doctor-phone")]
|
||||||
|
doctorphone: Text<String>,
|
||||||
#[multipart(rename = "medical-coverage")]
|
#[multipart(rename = "medical-coverage")]
|
||||||
medical: Option<Text<String>>,
|
medical: Text<String>,
|
||||||
#[multipart(rename = "insurance-name")]
|
#[multipart(rename = "insurance-name")]
|
||||||
insurance: Option<Text<String>>,
|
insurance: Text<String>,
|
||||||
#[multipart(rename = "policy-number")]
|
#[multipart(rename = "policy-number")]
|
||||||
policy_number: Option<Text<String>>,
|
policy_number: Text<String>,
|
||||||
allergies: Option<Text<String>>,
|
allergies: Text<String>,
|
||||||
#[multipart(rename = "allergies-other")]
|
#[multipart(rename = "allergies-other")]
|
||||||
allergies_other: Option<Text<String>>,
|
allergies_other: Text<String>,
|
||||||
#[multipart(rename = "specific-allergies")]
|
#[multipart(rename = "specific-allergies")]
|
||||||
specific_allergies: Option<Text<String>>,
|
specific_allergies: Text<String>,
|
||||||
#[multipart(rename = "allergic-treatment")]
|
#[multipart(rename = "allergic-treatment")]
|
||||||
treatment: Option<Text<String>>,
|
treatment: Text<String>,
|
||||||
conditions: Option<Text<String>>,
|
conditions: Text<String>,
|
||||||
#[multipart(rename = "tetanus-shot")]
|
#[multipart(rename = "tetanus-shot")]
|
||||||
tetanus: Option<Text<String>>,
|
tetanus: Text<String>,
|
||||||
#[multipart(rename = "swimming-ability")]
|
#[multipart(rename = "swimming-ability")]
|
||||||
swimming: Option<Text<String>>,
|
swimming: Text<String>,
|
||||||
#[multipart(rename = "medication-schedule")]
|
#[multipart(rename = "medication-schedule")]
|
||||||
medication: Option<Text<String>>,
|
medication: Text<String>,
|
||||||
#[multipart(rename = "other-notes")]
|
#[multipart(rename = "other-notes")]
|
||||||
notes: Option<Text<String>>,
|
notes: Text<String>,
|
||||||
agreement: Option<Text<String>>,
|
agreement: Text<String>,
|
||||||
#[multipart(rename = "image")]
|
#[multipart(rename = "image")]
|
||||||
file: Option<TempFile>,
|
file: Option<TempFile>,
|
||||||
registration: Option<Text<String>>,
|
registration: Text<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/health-form")]
|
impl From<&HealthForm> for HashMap<i32, String> {
|
||||||
pub async fn health_form(MultipartForm(form): MultipartForm<HealthForm>) -> HttpResponse {
|
fn from(form: &HealthForm) -> Self {
|
||||||
let first = form
|
let mut map = HashMap::new();
|
||||||
.first_name
|
map.insert(37, format!("{} {}", form.first_name.0, form.last_name.0));
|
||||||
.as_ref()
|
map.insert(
|
||||||
.unwrap_or(&Text(String::from("")))
|
38,
|
||||||
.0
|
format!("{} {}", form.parent_first_name.0, form.parent_last_name.0),
|
||||||
.clone();
|
);
|
||||||
let last = form
|
map.insert(39, form.birthdate.0.clone());
|
||||||
.last_name
|
map.insert(40, form.street.0.clone());
|
||||||
.as_ref()
|
map.insert(41, form.city.0.clone());
|
||||||
.unwrap_or(&Text(String::from("")))
|
map.insert(42, form.state.0.clone());
|
||||||
.0
|
map.insert(43, form.zip.0.clone());
|
||||||
.clone();
|
map.insert(44, form.parent_cellphone.0.clone());
|
||||||
let full_name = format!("{} {}", first, last);
|
map.insert(45, form.homephone.0.clone());
|
||||||
let registration = form
|
map.insert(46, format!("{} {}", form.contact.0, form.contact_phone.0));
|
||||||
.registration
|
map.insert(47, form.doctorname.0.clone());
|
||||||
.as_ref()
|
map.insert(48, form.doctorcity.0.clone());
|
||||||
.unwrap_or(&Text(String::from("")))
|
map.insert(49, form.doctorphone.0.clone());
|
||||||
.0
|
map.insert(50, form.medical.0.clone());
|
||||||
.clone();
|
map.insert(51, form.insurance.0.clone());
|
||||||
let email_subject = format!("{} {} filled out a health form!", first, last);
|
map.insert(52, form.policy_number.0.clone());
|
||||||
let filename_noext = format!("{}_{}", first, last);
|
map.insert(54, form.agreement.0.clone());
|
||||||
let parent = format!(
|
map.insert(
|
||||||
"{} {}",
|
55,
|
||||||
form.parent_first_name
|
format!("{} \n {}", form.allergies.0, form.allergies_other.0),
|
||||||
.as_ref()
|
);
|
||||||
.unwrap_or(&Text(String::from("")))
|
map.insert(56, form.specific_allergies.0.clone());
|
||||||
.0
|
map.insert(57, form.treatment.0.clone());
|
||||||
.clone(),
|
map.insert(58, form.conditions.0.clone());
|
||||||
form.parent_last_name
|
map.insert(59, form.tetanus.0.clone());
|
||||||
.as_ref()
|
map.insert(60, form.medication.0.clone());
|
||||||
.unwrap_or(&Text(String::from("")))
|
map.insert(61, form.notes.0.clone());
|
||||||
.0
|
map.insert(62, form.swimming.0.clone());
|
||||||
.clone()
|
map
|
||||||
);
|
}
|
||||||
let birthdate = form
|
}
|
||||||
.birthdate
|
|
||||||
.as_ref()
|
impl HealthForm {
|
||||||
.unwrap_or(&Text(String::from("")))
|
fn build_email(&self) -> Markup {
|
||||||
.0
|
html! {
|
||||||
.clone();
|
(DOCTYPE)
|
||||||
let street = form
|
meta charset="utf-8";
|
||||||
.street
|
html {
|
||||||
.as_ref()
|
head {
|
||||||
.unwrap_or(&Text(String::from("")))
|
title { (self.first_name.0) " " (self.last_name.0) " filled out a health form!" }
|
||||||
.0
|
style {
|
||||||
.clone();
|
"table { border-collapse: collapse; width: 100% }"
|
||||||
let city = form
|
"td, th { padding: 8px }"
|
||||||
.city
|
"td { text-align: left; width: 70%; word-wrap: break-word }"
|
||||||
.as_ref()
|
"th { text-align: right; border-right: 1px solid #ddd }"
|
||||||
.unwrap_or(&Text(String::from("")))
|
"tr { border-bottom: 1px solid #ddd }"
|
||||||
.0
|
"h1 { text-align: center }"
|
||||||
.clone();
|
}
|
||||||
let state = form
|
|
||||||
.state
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let zip = form
|
|
||||||
.zip
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let parent_cellphone = form
|
|
||||||
.parent_cellphone
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let homephone = form
|
|
||||||
.homephone
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let contact = form
|
|
||||||
.contact
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let contact_phone = form
|
|
||||||
.contact_phone
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let doctorname = form
|
|
||||||
.doctorname
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let doctorcity = form
|
|
||||||
.doctorcity
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let doctorphone = form
|
|
||||||
.doctorphone
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let medical = form
|
|
||||||
.medical
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let insurance = form
|
|
||||||
.insurance
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let policy_number = form
|
|
||||||
.policy_number
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let agreement = form
|
|
||||||
.agreement
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let allergies = form
|
|
||||||
.allergies
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let allergies_other = form
|
|
||||||
.allergies_other
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let specific_allergies = form
|
|
||||||
.specific_allergies
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let treatment = form
|
|
||||||
.treatment
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let conditions = form
|
|
||||||
.conditions
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let tetanus = form
|
|
||||||
.tetanus
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let swimming = form
|
|
||||||
.swimming
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let medication = form
|
|
||||||
.medication
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let notes = form
|
|
||||||
.notes
|
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
log::info!("{first} {last} filled out a health form!");
|
|
||||||
let email = markup::new! {
|
|
||||||
@markup::doctype()
|
|
||||||
html {
|
|
||||||
head {
|
|
||||||
title { @format!("{} {} filled out a health form!", first, last) }
|
|
||||||
style {
|
|
||||||
"table { border-collapse: collapse; width: 100% }"
|
|
||||||
"td, th { padding: 8px }"
|
|
||||||
"td { text-align: left; width: 70%; word-wrap: break-word }"
|
|
||||||
"th { text-align: right; border-right: 1px solid #ddd }"
|
|
||||||
"tr { border-bottom: 1px solid #ddd }"
|
|
||||||
"h1 { text-align: center }"
|
|
||||||
}
|
}
|
||||||
}
|
body {
|
||||||
body {
|
h1 { "Health form for " (self.first_name.0) " " (self.last_name.0) "!" }
|
||||||
h1 { @format!("Health form for {} {}!", first, last) }
|
hr;
|
||||||
hr;
|
table {
|
||||||
table {
|
tr {
|
||||||
tr {
|
th { "Name" }
|
||||||
th { "Name" }
|
td { (self.first_name.0) " " (self.last_name.0) }
|
||||||
td { @format!("{} {}", first, last) }
|
}
|
||||||
}
|
tr {
|
||||||
tr {
|
th { "Parent" }
|
||||||
th { "Parent" }
|
td { (self.parent_first_name.0) " " (self.parent_last_name.0) }
|
||||||
td { @parent }
|
}
|
||||||
}
|
tr {
|
||||||
tr {
|
th { "Birthdate" }
|
||||||
th { "Birthdate" }
|
td { (self.birthdate.0) }
|
||||||
td { @birthdate }
|
}
|
||||||
}
|
tr {
|
||||||
tr {
|
th { "Street" }
|
||||||
th { "Street" }
|
td { (self.street.0) }
|
||||||
td { @street }
|
}
|
||||||
}
|
tr {
|
||||||
tr {
|
th { "City" }
|
||||||
th { "City" }
|
td { (self.city.0) }
|
||||||
td { @city }
|
}
|
||||||
}
|
tr {
|
||||||
tr {
|
th { "State" }
|
||||||
th { "State" }
|
td { (self.state.0) }
|
||||||
td { @state }
|
}
|
||||||
}
|
tr {
|
||||||
tr {
|
th { "Zip" }
|
||||||
th { "Zip" }
|
td { (self.zip.0) }
|
||||||
td { @zip }
|
}
|
||||||
}
|
tr {
|
||||||
tr {
|
th { "Parent Cell Phone" }
|
||||||
th { "Phone" }
|
td { (self.parent_cellphone.0) }
|
||||||
td { @parent_cellphone }
|
}
|
||||||
}
|
tr {
|
||||||
tr {
|
th { "Homephone" }
|
||||||
th { "Home Phone" }
|
td { (self.homephone.0) }
|
||||||
td { @homephone }
|
}
|
||||||
}
|
tr {
|
||||||
tr {
|
th { "Additional Emergency Contact" }
|
||||||
th { "Additional Emergency Contact" }
|
td { (self.contact.0) }
|
||||||
td { @contact }
|
}
|
||||||
}
|
tr {
|
||||||
tr {
|
th { "Emegency Contact Phone" }
|
||||||
th { "Contact Phone" }
|
td { (self.contact_phone.0) }
|
||||||
td { @contact_phone }
|
}
|
||||||
}
|
tr {
|
||||||
tr {
|
th { "Doctor" }
|
||||||
th { "Doctor" }
|
td { (self.doctorname.0) }
|
||||||
td { @doctorname }
|
}
|
||||||
}
|
tr {
|
||||||
tr {
|
th { "Doctor City" }
|
||||||
th { "Doctor City" }
|
td { (self.doctorcity.0) }
|
||||||
td { @doctorcity }
|
}
|
||||||
}
|
tr {
|
||||||
tr {
|
th { "Doctor Phone" }
|
||||||
th { "Doctor Phone" }
|
td { (self.doctorphone.0) }
|
||||||
td { @doctorphone }
|
}
|
||||||
}
|
tr {
|
||||||
tr {
|
th { "Medical Coverage" }
|
||||||
th { "Medical Coverage" }
|
td { (self.medical.0) }
|
||||||
td { @medical }
|
}
|
||||||
}
|
tr {
|
||||||
tr {
|
th { "Insurance Provider" }
|
||||||
th { "Insurance Provider" }
|
td { (self.insurance.0) }
|
||||||
td { @insurance }
|
}
|
||||||
}
|
tr {
|
||||||
tr {
|
th { "Policy Number" }
|
||||||
th { "Policy Number" }
|
td { (self.policy_number.0) }
|
||||||
td { @policy_number }
|
}
|
||||||
}
|
tr {
|
||||||
tr {
|
th { "Allergies" }
|
||||||
th { "Allergies" }
|
td { (self.allergies.0)
|
||||||
td { @allergies }
|
"\n\n"
|
||||||
}
|
(self.allergies_other.0)
|
||||||
tr {
|
}
|
||||||
th { "Other Allergies" }
|
}
|
||||||
td { @allergies_other }
|
tr {
|
||||||
}
|
th { "Specific Allergies" }
|
||||||
tr {
|
td { (self.specific_allergies.0) }
|
||||||
th { "Specific Allergies" }
|
}
|
||||||
td { @specific_allergies }
|
tr {
|
||||||
}
|
th { "Allergic Treatments" }
|
||||||
tr {
|
td { (self.treatment.0) }
|
||||||
th { "Treatments" }
|
}
|
||||||
td { @treatment }
|
tr {
|
||||||
}
|
th { "Conditions" }
|
||||||
tr {
|
td { (self.conditions.0) }
|
||||||
th { "Physical or mental conditions" }
|
}
|
||||||
td { @conditions }
|
tr {
|
||||||
}
|
th { "Date of last Tetanus Shot" }
|
||||||
tr {
|
td { (self.tetanus.0) }
|
||||||
th { "Last tetanus shot" }
|
}
|
||||||
td { @tetanus }
|
tr {
|
||||||
}
|
th { "Swimming Ability" }
|
||||||
tr {
|
td { (self.swimming.0) }
|
||||||
th { "Swimming Ability" }
|
}
|
||||||
td { @swimming }
|
tr {
|
||||||
}
|
th { "Medication Schedule" }
|
||||||
tr {
|
td { (self.medication.0) }
|
||||||
th { "Medication Schedule" }
|
}
|
||||||
td { @medication }
|
tr {
|
||||||
}
|
th { "Other Notes" }
|
||||||
tr {
|
td { (self.notes.0) }
|
||||||
th { "Other Relevant Info" }
|
}
|
||||||
td { @notes }
|
tr {
|
||||||
}
|
th { "Final Agreement" }
|
||||||
tr {
|
td { (self.agreement.0) }
|
||||||
th { "Final Agreement" }
|
}
|
||||||
td { @agreement }
|
tr {
|
||||||
|
th { "Registration" }
|
||||||
|
td { (self.registration.0) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
let mut path: Option<String> = Some(String::from(""));
|
|
||||||
let mut file_exists = false;
|
|
||||||
let mut filename = String::from("");
|
|
||||||
log::info!("{:?}", file_exists);
|
|
||||||
if let Some(f) = form.file {
|
|
||||||
if let Some(file) = f.file_name {
|
|
||||||
if let Some(ext) = file.rsplit('.').next() {
|
|
||||||
filename = format!("{}.{}", filename_noext, ext);
|
|
||||||
path = Some(format!("./tmp/{}.{}", filename_noext, ext));
|
|
||||||
} else {
|
|
||||||
path = Some(format!("./tmp/{}", file));
|
|
||||||
}
|
|
||||||
// let path = format!("./tmp/{}", file);
|
|
||||||
log::info!("saving to {}", path.clone().unwrap());
|
|
||||||
match f.file.persist(path.clone().unwrap()) {
|
|
||||||
Ok(f) => {
|
|
||||||
log::info!("{:?}", f);
|
|
||||||
if f.metadata().unwrap().len() > 0 {
|
|
||||||
file_exists = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => log::info!("{:?}: Probably a missing image", e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let multi = if file_exists {
|
async fn store_form(&self) -> Result<()> {
|
||||||
let filebody = fs::read(path.clone().unwrap_or_default());
|
let client = Client::new();
|
||||||
let content_type = ContentType::parse("image/jpg").unwrap();
|
let map = HashMap::from(self);
|
||||||
let attachment = Attachment::new(filename).body(filebody.unwrap(), content_type);
|
let mut json = HashMap::new();
|
||||||
log::info!("{:?}", attachment);
|
json.insert("data", map);
|
||||||
MultiPart::mixed()
|
|
||||||
.singlepart(SinglePart::html(email.to_string()))
|
|
||||||
.singlepart(attachment)
|
|
||||||
} else {
|
|
||||||
MultiPart::alternative_plain_html(String::from("Testing"), email.to_string())
|
|
||||||
};
|
|
||||||
|
|
||||||
log::info!("{:?}", multi);
|
let link = r#"https://staff.tfcconnection.org/apps/tables/#/table/4/row/757"#;
|
||||||
|
let res = client
|
||||||
if let Ok(m) = Message::builder()
|
.post("https://staff.tfcconnection.org/ocs/v2.php/apps/tables/api/2/tables/4/rows")
|
||||||
.from(
|
.basic_auth("chris", Some("2VHeGxeC^Zf9KqFK^G@Pt!zu2q^6@b"))
|
||||||
"TFC ADMIN <no-reply@mail.tfcconnection.org>"
|
.header("OCS-APIRequest", "true")
|
||||||
.parse()
|
.header("Content-Type", "application/json")
|
||||||
.unwrap(),
|
.json(&json)
|
||||||
)
|
.send()
|
||||||
.to("Chris Cochrun <chris@tfcconnection.org>".parse().unwrap())
|
.await?;
|
||||||
// .to("Ethan Rose <ethan@tfcconnection.org>".parse().unwrap())
|
if res.status().is_success() {
|
||||||
.subject(email_subject)
|
let res = res.text().await.unwrap();
|
||||||
.multipart(multi)
|
Ok(())
|
||||||
{
|
} else {
|
||||||
let sender = SmtpTransport::relay("mail.tfcconnection.org")
|
Err(eyre!(
|
||||||
.ok()
|
"Problem in storing data: {:?}",
|
||||||
.unwrap()
|
res.error_for_status()
|
||||||
.credentials(Credentials::new(
|
|
||||||
"no-reply@mail.tfcconnection.org".to_owned(),
|
|
||||||
"r9f36mNZFtiW4f".to_owned(),
|
|
||||||
))
|
))
|
||||||
.authentication(vec![Mechanism::Plain])
|
|
||||||
.build();
|
|
||||||
match sender.send(&m) {
|
|
||||||
Ok(res) => log::info!("{:?}", res),
|
|
||||||
Err(e) => log::info!("{e}"),
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
log::info!("Email incorrect");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match registration.as_str() {
|
fn get_temp_file(&mut self) -> Option<(String, String, Option<String>)> {
|
||||||
|
let first = self.first_name.clone();
|
||||||
|
let last = self.last_name.clone();
|
||||||
|
let filename_noext = format!("{}_{}", first, last);
|
||||||
|
let (file_name, content_type) = if let Some(file) = self.file.as_ref() {
|
||||||
|
let content_type = file.content_type.clone().map(|m| m.to_string());
|
||||||
|
(file.file_name.to_owned(), content_type)
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let filename;
|
||||||
|
let path = if let Some(file_name) = file_name {
|
||||||
|
if let Some(ext) = file_name.rsplit('.').next() {
|
||||||
|
filename = format!("{}.{}", filename_noext, ext);
|
||||||
|
format!("./tmp/{}.{}", filename_noext, ext)
|
||||||
|
} else {
|
||||||
|
filename = String::default();
|
||||||
|
format!("./tmp/{}", file_name)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filename = String::default();
|
||||||
|
String::default()
|
||||||
|
};
|
||||||
|
let file = self.file.take();
|
||||||
|
match file.unwrap().file.persist(path.clone()) {
|
||||||
|
Ok(f) => {
|
||||||
|
if f.metadata().unwrap().len() <= 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
info!(?f, "File saved successfully");
|
||||||
|
Some((filename, path, content_type.clone()))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!("{:?}: Probably a missing image", e);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_email(&mut self) -> Result<Message> {
|
||||||
|
let first = self.first_name.clone();
|
||||||
|
let last = self.last_name.clone();
|
||||||
|
let email_subject = format!("{} {} filled out a health form!", first, last);
|
||||||
|
info!("{first} {last} filled out a health form!");
|
||||||
|
let email = self.build_email();
|
||||||
|
let temp_file = self.get_temp_file();
|
||||||
|
let multi = if let Some((file, path, content_type)) = temp_file {
|
||||||
|
let filebody = fs::read(path);
|
||||||
|
let content_type =
|
||||||
|
ContentType::parse(&content_type.unwrap_or(String::from("image/jpg"))).unwrap();
|
||||||
|
let attachment = Attachment::new(file).body(filebody.unwrap(), content_type);
|
||||||
|
// info!(?attachment);
|
||||||
|
MultiPart::mixed()
|
||||||
|
.singlepart(SinglePart::html(email.into_string()))
|
||||||
|
.singlepart(attachment)
|
||||||
|
} else {
|
||||||
|
MultiPart::alternative_plain_html(String::from("Testing"), email.into_string())
|
||||||
|
};
|
||||||
|
|
||||||
|
Message::builder()
|
||||||
|
.from(
|
||||||
|
"TFC ADMIN <no-reply@mail.tfcconnection.org>"
|
||||||
|
.parse()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.to("Chris Cochrun <chris@tfcconnection.org>".parse().unwrap())
|
||||||
|
.to("Ethan Rose <ethan@tfcconnection.org>".parse().unwrap())
|
||||||
|
.subject(email_subject)
|
||||||
|
.multipart(multi)
|
||||||
|
.wrap_err("Email incorrect")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/api/health-form")]
|
||||||
|
pub async fn health_form(MultipartForm(mut form): MultipartForm<HealthForm>) -> HttpResponse {
|
||||||
|
info!("Starting health form work: {:?}", form);
|
||||||
|
match form.send_email() {
|
||||||
|
Ok(m) => {
|
||||||
|
actix_rt::spawn(send_email(m).map(|r| match r {
|
||||||
|
Ok(_) => info!("Email sent successfully"),
|
||||||
|
Err(e) => error!("There was an erroring sending form to nextcloud: {e}"),
|
||||||
|
}));
|
||||||
|
info!("Successfully sent email health form")
|
||||||
|
}
|
||||||
|
Err(e) => error!("There was an error sending email: {e}"),
|
||||||
|
}
|
||||||
|
|
||||||
|
let map = HashMap::from(&form);
|
||||||
|
actix_rt::spawn(store_form(map).map(|r| match r {
|
||||||
|
Ok(_) => {
|
||||||
|
info!("Successfully stored health form in nextcloud!")
|
||||||
|
}
|
||||||
|
Err(e) => error!("There was an error storing form in nextcloud: {e}"),
|
||||||
|
}));
|
||||||
|
|
||||||
|
let full_name = format!("{} {}", form.first_name.0, form.last_name.0);
|
||||||
|
match form.registration.0.as_str() {
|
||||||
"now" => {
|
"now" => {
|
||||||
log::info!("Sending them to pay for registration now");
|
info!("Sending them to pay for registration now");
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.insert_header(("Access-Control-Expose-Headers", "*"))
|
.insert_header(("Access-Control-Expose-Headers", "*"))
|
||||||
.insert_header((
|
.insert_header((
|
||||||
|
@ -458,7 +385,7 @@ pub async fn health_form(MultipartForm(form): MultipartForm<HealthForm>) -> Http
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
"full" => {
|
"full" => {
|
||||||
log::info!("Sending them to pay for the full registration now");
|
info!("Sending them to pay for the full registration now");
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.insert_header(("Access-Control-Expose-Headers", "*"))
|
.insert_header(("Access-Control-Expose-Headers", "*"))
|
||||||
.insert_header((
|
.insert_header((
|
||||||
|
@ -468,43 +395,62 @@ pub async fn health_form(MultipartForm(form): MultipartForm<HealthForm>) -> Http
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
"later" => {
|
"later" => {
|
||||||
log::info!("{} would like to pay later", full_name);
|
info!("{} would like to pay later", full_name);
|
||||||
let html = markup::new! {
|
let html = html! {
|
||||||
div {
|
div class="mt-8" {
|
||||||
class { "mt-8" }
|
|
||||||
h2 {
|
h2 {
|
||||||
@format!("Thank you, {}!", full_name)
|
"Thank you, " (full_name) "!"
|
||||||
}
|
}
|
||||||
p { "Can't wait to see you at camp!" }
|
|
||||||
p {
|
p {
|
||||||
class { "" }
|
class { "" }
|
||||||
"If you'd like to pay for your registration go to the donate tab in the top right when you are ready and find the camp registration option."
|
"If you'd like to pay for your registration go to the donate tab in the top right when you are ready and find the right registration option."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
HttpResponse::Ok().body(html.to_string())
|
HttpResponse::Ok().body(html.into_string())
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
log::error!("Got registration error.....");
|
log::warn!(
|
||||||
let html = markup::new! {
|
"Got registration error possibly. Here is what the registration was: {}",
|
||||||
div {
|
form.registration.0.as_str()
|
||||||
class { "mt-8" }
|
);
|
||||||
|
let html = html! {
|
||||||
|
div class="mt-8" {
|
||||||
h2 {
|
h2 {
|
||||||
@format!("Thank you, {}!", full_name)
|
"Thank you, " (full_name) "!"
|
||||||
}
|
}
|
||||||
p { "Can't wait to see you at camp!" }
|
|
||||||
p {
|
p {
|
||||||
class { "" }
|
class { "" }
|
||||||
"If you'd like to pay for your registration go to the donate tab in the top right when you are ready and find the camp registration option."
|
"If you filled this out for camp or mission trip you can pay for your registration at the donate tab in the top right when you are ready and find the camp or mission trip registration option."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
HttpResponse::Ok().body(html.to_string())
|
HttpResponse::Ok().body(html.into_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// HttpResponse::Ok().body("hi")
|
// HttpResponse::Ok().body("hi")
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn store_health_form(_form: HealthForm) -> bool {
|
async fn store_form(map: HashMap<i32, String>) -> Result<()> {
|
||||||
todo!()
|
let client = Client::new();
|
||||||
|
let mut json = HashMap::new();
|
||||||
|
json.insert("data", map);
|
||||||
|
|
||||||
|
let res = client
|
||||||
|
.post("https://staff.tfcconnection.org/ocs/v2.php/apps/tables/api/2/tables/4/rows")
|
||||||
|
.basic_auth("chris", Some("2VHeGxeC^Zf9KqFK^G@Pt!zu2q^6@b"))
|
||||||
|
.header("OCS-APIRequest", "true")
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.json(&json)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
if res.status().is_success() {
|
||||||
|
let res = res.text().await.unwrap();
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(eyre!(
|
||||||
|
"Problem in storing data: {:?}",
|
||||||
|
res.error_for_status()
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use actix_multipart::form::{text::Text, MultipartForm};
|
use actix_multipart::form::{text::Text, MultipartForm};
|
||||||
use actix_web::{post, HttpResponse};
|
use actix_web::{post, HttpResponse};
|
||||||
use lettre::{
|
use lettre::{message::MultiPart, Message};
|
||||||
message::MultiPart,
|
use tracing::info;
|
||||||
transport::smtp::authentication::{Credentials, Mechanism},
|
|
||||||
Message, SmtpTransport, Transport,
|
use crate::email::send_email;
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, MultipartForm, Default)]
|
#[derive(Debug, MultipartForm, Default)]
|
||||||
struct LocalForm {
|
struct LocalForm {
|
||||||
|
@ -165,7 +164,7 @@ pub async fn local_form(MultipartForm(form): MultipartForm<LocalForm>) -> HttpRe
|
||||||
.unwrap_or(&Text(String::from("")))
|
.unwrap_or(&Text(String::from("")))
|
||||||
.0
|
.0
|
||||||
.clone();
|
.clone();
|
||||||
log::info!("{first} {last} signed up for the local mission trip!");
|
info!("{first} {last} signed up for the local mission trip!");
|
||||||
let email = markup::new! {
|
let email = markup::new! {
|
||||||
@markup::doctype()
|
@markup::doctype()
|
||||||
html {
|
html {
|
||||||
|
@ -260,7 +259,7 @@ pub async fn local_form(MultipartForm(form): MultipartForm<LocalForm>) -> HttpRe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
log::info!("{:?}", form);
|
info!("{:?}", form);
|
||||||
let multi = MultiPart::alternative_plain_html(String::from("Testing"), email.to_string());
|
let multi = MultiPart::alternative_plain_html(String::from("Testing"), email.to_string());
|
||||||
|
|
||||||
if let Ok(m) = Message::builder()
|
if let Ok(m) = Message::builder()
|
||||||
|
@ -274,21 +273,9 @@ pub async fn local_form(MultipartForm(form): MultipartForm<LocalForm>) -> HttpRe
|
||||||
.subject(email_subject)
|
.subject(email_subject)
|
||||||
.multipart(multi)
|
.multipart(multi)
|
||||||
{
|
{
|
||||||
let sender = SmtpTransport::relay("mail.tfcconnection.org")
|
let _ = send_email(m);
|
||||||
.ok()
|
|
||||||
.unwrap()
|
|
||||||
.credentials(Credentials::new(
|
|
||||||
"no-reply@mail.tfcconnection.org".to_owned(),
|
|
||||||
"r9f36mNZFtiW4f".to_owned(),
|
|
||||||
))
|
|
||||||
.authentication(vec![Mechanism::Plain])
|
|
||||||
.build();
|
|
||||||
match sender.send(&m) {
|
|
||||||
Ok(res) => log::info!("{:?}", res),
|
|
||||||
Err(e) => log::info!("{e}"),
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
log::info!("Email incorrect");
|
info!("Email incorrect");
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse::Ok().body("thankyou")
|
HttpResponse::Ok().body("thankyou")
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
pub mod camp_form;
|
pub mod camp_form;
|
||||||
pub mod church_form;
|
// pub mod errors;
|
||||||
pub mod errors;
|
pub mod contact;
|
||||||
pub mod health_form;
|
pub mod health_form;
|
||||||
pub mod local_trip_form;
|
pub mod local_trip_form;
|
||||||
|
pub mod mt_church_form;
|
||||||
pub mod mt_form;
|
pub mod mt_form;
|
||||||
pub mod parent_form;
|
pub mod mt_parent_form;
|
||||||
pub mod teacher_form;
|
pub mod mt_teacher_form;
|
||||||
|
|
234
src/api/mt_church_form.rs
Normal file
234
src/api/mt_church_form.rs
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
use std::{
|
||||||
|
collections::{BTreeMap, HashMap},
|
||||||
|
fs,
|
||||||
|
};
|
||||||
|
|
||||||
|
use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
|
||||||
|
use actix_web::{post, web, HttpResponse};
|
||||||
|
use color_eyre::{eyre::eyre, Result};
|
||||||
|
use lettre::{
|
||||||
|
message::{header::ContentType, Attachment, MultiPart, SinglePart},
|
||||||
|
Message,
|
||||||
|
};
|
||||||
|
use maud::{html, Markup, PreEscaped, DOCTYPE};
|
||||||
|
use reqwest::Client;
|
||||||
|
use serde_json::json;
|
||||||
|
use sqlx::SqliteConnection;
|
||||||
|
use tracing::{error, info};
|
||||||
|
|
||||||
|
#[derive(Debug, MultipartForm)]
|
||||||
|
struct MtChurchForm {
|
||||||
|
#[multipart(rename = "firstname")]
|
||||||
|
first_name: Text<String>,
|
||||||
|
#[multipart(rename = "lastname")]
|
||||||
|
last_name: Text<String>,
|
||||||
|
#[multipart(rename = "studentfirstname")]
|
||||||
|
student_first_name: Text<String>,
|
||||||
|
#[multipart(rename = "studentlastname")]
|
||||||
|
student_last_name: Text<String>,
|
||||||
|
relationship: Text<String>,
|
||||||
|
#[multipart(rename = "walk-with-jesus")]
|
||||||
|
walk_jesus: Text<String>,
|
||||||
|
commitment: Text<String>,
|
||||||
|
#[multipart(rename = "pos-characteristics")]
|
||||||
|
positive: Text<String>,
|
||||||
|
#[multipart(rename = "neg-characteristics")]
|
||||||
|
negative: Text<String>,
|
||||||
|
teachable: Text<String>,
|
||||||
|
#[multipart(rename = "extra-info")]
|
||||||
|
extra_info: Text<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&MtChurchForm> for HashMap<i32, String> {
|
||||||
|
fn from(form: &MtChurchForm) -> Self {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
map.insert(158, format!("{} {}", form.first_name.0, form.last_name.0));
|
||||||
|
map.insert(
|
||||||
|
159,
|
||||||
|
format!("{} {}", form.student_first_name.0, form.student_last_name.0),
|
||||||
|
);
|
||||||
|
map.insert(160, form.relationship.0.clone());
|
||||||
|
map.insert(163, form.positive.0.clone());
|
||||||
|
map.insert(164, form.negative.0.clone());
|
||||||
|
map.insert(161, form.walk_jesus.0.clone());
|
||||||
|
map.insert(162, form.commitment.0.clone());
|
||||||
|
map.insert(165, form.teachable.0.clone());
|
||||||
|
map.insert(166, form.extra_info.0.clone());
|
||||||
|
map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MtChurchForm {
|
||||||
|
async fn build_email(&self) -> Markup {
|
||||||
|
html! {
|
||||||
|
(DOCTYPE)
|
||||||
|
meta charset="utf-8";
|
||||||
|
html {
|
||||||
|
head {
|
||||||
|
title { (self.first_name.0) " " (self.last_name.0) " filled out a church reference form for " (self.student_first_name.0) " " (self.student_last_name.0) "!" }
|
||||||
|
style {
|
||||||
|
"table { border-collapse: collapse; width: 100% }"
|
||||||
|
"td, th { padding: 8px }"
|
||||||
|
"td { text-align: left; width: 70%; word-wrap: break-word }"
|
||||||
|
"th { text-align: right; border-right: 1px solid #ddd }"
|
||||||
|
"tr { border-bottom: 1px solid #ddd }"
|
||||||
|
"h1 { text-align: center }"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
h1 { "Church reference form for " (self.student_first_name.0) " " (self.student_last_name.0) "!" }
|
||||||
|
hr;
|
||||||
|
table {
|
||||||
|
tr {
|
||||||
|
th { "Name" }
|
||||||
|
td { (self.first_name.0) " " (self.last_name.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Student" }
|
||||||
|
td { (self.student_first_name.0) " " (self.student_last_name.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Relationship with student" }
|
||||||
|
td { (self.relationship.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Positive characteristics" }
|
||||||
|
td { (self.positive.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Negative characteristics" }
|
||||||
|
td { (self.negative.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Walk with Jesus" }
|
||||||
|
td { (self.walk_jesus.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Commitment" }
|
||||||
|
td { (self.commitment.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Teachable heart" }
|
||||||
|
td { (self.teachable.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Other Relevant Info" }
|
||||||
|
td { (self.extra_info.0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn store_form(&self) -> Result<()> {
|
||||||
|
let client = Client::new();
|
||||||
|
let map = HashMap::from(self);
|
||||||
|
let mut json = HashMap::new();
|
||||||
|
json.insert("data", map);
|
||||||
|
|
||||||
|
let link = r#"https://staff.tfcconnection.org/apps/tables/#/table/13/row/757"#;
|
||||||
|
let res = client
|
||||||
|
.post("https://staff.tfcconnection.org/ocs/v2.php/apps/tables/api/2/tables/13/rows")
|
||||||
|
.basic_auth("chris", Some("2VHeGxeC^Zf9KqFK^G@Pt!zu2q^6@b"))
|
||||||
|
.header("OCS-APIRequest", "true")
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.json(&json)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
if res.status().is_success() {
|
||||||
|
let res = res.text().await.unwrap();
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(eyre!(
|
||||||
|
"Problem in storing data: {:?}",
|
||||||
|
res.error_for_status()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_email(&mut self) -> Result<()> {
|
||||||
|
let first = self.student_first_name.clone();
|
||||||
|
let last = self.student_last_name.clone();
|
||||||
|
let email_subject = format!("Church reference form for {} {}!", first, last);
|
||||||
|
info!("{first} {last} church reference form!");
|
||||||
|
let email = self.build_email().await;
|
||||||
|
let email = SinglePart::html(email.into_string());
|
||||||
|
|
||||||
|
if let Ok(m) = Message::builder()
|
||||||
|
.from(
|
||||||
|
"TFC ADMIN <no-reply@mail.tfcconnection.org>"
|
||||||
|
.parse()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.to("Chris Cochrun <chris@tfcconnection.org>".parse().unwrap())
|
||||||
|
.to("Ethan Rose <ethan@tfcconnection.org>".parse().unwrap())
|
||||||
|
.subject(email_subject)
|
||||||
|
.singlepart(email)
|
||||||
|
{
|
||||||
|
crate::email::send_email(m).await
|
||||||
|
} else {
|
||||||
|
Err(eyre!("Email incorrect"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/api/mt-church-form")]
|
||||||
|
pub async fn mt_church_form(MultipartForm(mut form): MultipartForm<MtChurchForm>) -> HttpResponse {
|
||||||
|
match form.store_form().await {
|
||||||
|
Ok(_) => info!("Successfully sent form to nextcloud!"),
|
||||||
|
Err(e) => error!("There was an erroring sending form to nextcloud: {e}"),
|
||||||
|
}
|
||||||
|
match form.send_email().await {
|
||||||
|
Ok(_) => info!("Successfully sent email"),
|
||||||
|
Err(e) => error!("There was an error sending the email: {e}"),
|
||||||
|
}
|
||||||
|
HttpResponse::Ok().body("thankyou")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use actix_web::test;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use sqlx::Connection;
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn form() -> MtChurchForm {
|
||||||
|
MtChurchForm {
|
||||||
|
first_name: Text(String::from("Bilbo")),
|
||||||
|
last_name: Text(String::from("Braggins")),
|
||||||
|
student_first_name: Text(String::from("Frodo")),
|
||||||
|
student_last_name: Text(String::from("Braggins")),
|
||||||
|
relationship: Text(String::from("Uncle")),
|
||||||
|
positive: Text(String::from("Nimble and brave")),
|
||||||
|
negative: Text(String::from("Small")),
|
||||||
|
walk_jesus: Text(String::from("Such a strutter")),
|
||||||
|
commitment: Text(String::from("Super")),
|
||||||
|
teachable: Text(String::from("Very")),
|
||||||
|
extra_info: Text(String::from("Willing to take the ring")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
async fn test_nc_post() {
|
||||||
|
let form = form();
|
||||||
|
assert!(!form.first_name.is_empty());
|
||||||
|
let res = form.store_form().await;
|
||||||
|
match res {
|
||||||
|
Ok(_) => assert!(true, "passed storing test"),
|
||||||
|
Err(e) => assert!(false, "Failed storing test: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
async fn test_email() {
|
||||||
|
let mut form = form();
|
||||||
|
assert!(!form.first_name.is_empty());
|
||||||
|
match form.send_email().await {
|
||||||
|
Ok(_) => assert!(true, "passed emailing test"),
|
||||||
|
Err(e) => assert!(false, "Failed emailing test: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,487 +1,519 @@
|
||||||
use std::fs;
|
use std::{collections::HashMap, fs};
|
||||||
|
|
||||||
use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
|
use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
|
||||||
use actix_web::{post, HttpResponse};
|
use actix_web::{post, HttpResponse};
|
||||||
|
use color_eyre::{
|
||||||
|
eyre::{eyre, Context},
|
||||||
|
Result,
|
||||||
|
};
|
||||||
|
use futures::FutureExt;
|
||||||
use lettre::{
|
use lettre::{
|
||||||
message::{header::ContentType, Attachment, MultiPart, SinglePart},
|
message::{header::ContentType, Attachment, MultiPart, SinglePart},
|
||||||
transport::smtp::authentication::{Credentials, Mechanism},
|
Message,
|
||||||
Message, SmtpTransport, Transport,
|
|
||||||
};
|
};
|
||||||
|
use maud::{html, Markup, DOCTYPE};
|
||||||
|
use reqwest::Client;
|
||||||
|
use tracing::{error, info};
|
||||||
|
|
||||||
#[derive(Debug, MultipartForm, Default)]
|
#[derive(Debug, MultipartForm)]
|
||||||
struct MtForm {
|
struct MtForm {
|
||||||
#[multipart(rename = "firstname")]
|
#[multipart(rename = "firstname")]
|
||||||
first_name: Option<Text<String>>,
|
first_name: Text<String>,
|
||||||
#[multipart(rename = "lastname")]
|
#[multipart(rename = "lastname")]
|
||||||
last_name: Option<Text<String>>,
|
last_name: Text<String>,
|
||||||
#[multipart(rename = "parentfirstname")]
|
#[multipart(rename = "parentfirstname")]
|
||||||
parent_first_name: Option<Text<String>>,
|
parent_first_name: Text<String>,
|
||||||
#[multipart(rename = "parentlastname")]
|
#[multipart(rename = "parentlastname")]
|
||||||
parent_last_name: Option<Text<String>>,
|
parent_last_name: Text<String>,
|
||||||
birthdate: Option<Text<String>>,
|
birthdate: Text<String>,
|
||||||
gender: Option<Text<String>>,
|
gender: Text<String>,
|
||||||
street: Option<Text<String>>,
|
street: Text<String>,
|
||||||
city: Option<Text<String>>,
|
city: Text<String>,
|
||||||
state: Option<Text<String>>,
|
state: Text<String>,
|
||||||
zip: Option<Text<i32>>,
|
zip: Text<i32>,
|
||||||
cellphone: Option<Text<String>>,
|
cellphone: Text<String>,
|
||||||
parentphone: Option<Text<String>>,
|
parentphone: Text<String>,
|
||||||
email: Option<Text<String>>,
|
email: Text<String>,
|
||||||
parentemail: Option<Text<String>>,
|
parentemail: Text<String>,
|
||||||
school: Option<Text<String>>,
|
school: Text<String>,
|
||||||
grade: Option<Text<String>>,
|
grade: Text<String>,
|
||||||
#[multipart(rename = "pastorfirstname")]
|
#[multipart(rename = "pastorfirstname")]
|
||||||
pastor_first_name: Option<Text<String>>,
|
pastor_first_name: Text<String>,
|
||||||
#[multipart(rename = "pastorlastname")]
|
#[multipart(rename = "pastorlastname")]
|
||||||
pastor_last_name: Option<Text<String>>,
|
pastor_last_name: Text<String>,
|
||||||
#[multipart(rename = "churchattendance")]
|
#[multipart(rename = "churchattendance")]
|
||||||
church_attendance: Option<Text<String>>,
|
church_attendance: Text<String>,
|
||||||
#[multipart(rename = "tfcgroup")]
|
#[multipart(rename = "tfcgroup")]
|
||||||
tfc_group: Option<Text<String>>,
|
tfc_group: Text<String>,
|
||||||
shirt: Option<Text<String>>,
|
shirt: Text<String>,
|
||||||
trip: Option<Text<String>>,
|
trip: Text<String>,
|
||||||
#[multipart(rename = "tripnotes")]
|
#[multipart(rename = "tripnotes")]
|
||||||
trip_notes: Option<Text<String>>,
|
trip_notes: Text<String>,
|
||||||
#[multipart(rename = "relationship-with-jesus")]
|
#[multipart(rename = "relationship-with-jesus")]
|
||||||
relationship_with_jesus: Option<Text<String>>,
|
relationship_with_jesus: Text<String>,
|
||||||
#[multipart(rename = "testimony")]
|
#[multipart(rename = "testimony")]
|
||||||
testimony: Option<Text<String>>,
|
testimony: Text<String>,
|
||||||
#[multipart(rename = "involvement-with-group")]
|
#[multipart(rename = "involvement-with-group")]
|
||||||
involvement_with_group: Option<Text<String>>,
|
involvement_with_group: Text<String>,
|
||||||
#[multipart(rename = "reasons-for-trip-choice")]
|
#[multipart(rename = "reasons-for-trip-choice")]
|
||||||
reasons: Option<Text<String>>,
|
reasons: Text<String>,
|
||||||
strengths: Option<Text<String>>,
|
strengths: Text<String>,
|
||||||
weaknesses: Option<Text<String>>,
|
weaknesses: Text<String>,
|
||||||
#[multipart(rename = "previous-trip-info")]
|
#[multipart(rename = "previous-trip-info")]
|
||||||
previous_trip_info: Option<Text<String>>,
|
previous_trip_info: Text<String>,
|
||||||
#[multipart(rename = "attitude-torward-work")]
|
#[multipart(rename = "attitude-torward-work")]
|
||||||
attitude: Option<Text<String>>,
|
attitude: Text<String>,
|
||||||
#[multipart(rename = "relevant-notes")]
|
#[multipart(rename = "relevant-notes")]
|
||||||
relevant_notes: Option<Text<String>>,
|
relevant_notes: Text<String>,
|
||||||
#[multipart(rename = "final-agreement")]
|
#[multipart(rename = "final-agreement")]
|
||||||
final_agreement: Option<Text<String>>,
|
final_agreement: Text<String>,
|
||||||
registration: Option<Text<String>>,
|
registration: Text<String>,
|
||||||
|
#[multipart(rename = "health-form")]
|
||||||
|
health_form: Text<String>,
|
||||||
#[multipart(rename = "image")]
|
#[multipart(rename = "image")]
|
||||||
file: Option<TempFile>,
|
file: Option<TempFile>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/mt-form")]
|
impl From<&MtForm> for HashMap<i32, String> {
|
||||||
pub async fn mt_form(MultipartForm(form): MultipartForm<MtForm>) -> HttpResponse {
|
fn from(form: &MtForm) -> Self {
|
||||||
let first = form
|
let mut map = HashMap::new();
|
||||||
.first_name
|
map.insert(106, format!("{} {}", form.first_name.0, form.last_name.0));
|
||||||
.as_ref()
|
map.insert(
|
||||||
.unwrap_or(&Text(String::from("")))
|
107,
|
||||||
.0
|
format!("{} {}", form.parent_first_name.0, form.parent_last_name.0),
|
||||||
.clone();
|
);
|
||||||
let last = form
|
map.insert(109, form.gender.0.clone());
|
||||||
.last_name
|
map.insert(110, form.birthdate.0.clone());
|
||||||
.as_ref()
|
map.insert(117, form.street.0.clone());
|
||||||
.unwrap_or(&Text(String::from("")))
|
map.insert(118, form.city.0.clone());
|
||||||
.0
|
map.insert(119, form.zip.0.to_string());
|
||||||
.clone();
|
map.insert(120, form.state.0.clone());
|
||||||
let email_subject = format!("{} {} signed up for mission trip!", first, last);
|
map.insert(121, form.cellphone.0.clone());
|
||||||
let filename_noext = format!("{}_{}", first, last);
|
map.insert(122, form.email.0.clone());
|
||||||
let parent = format!(
|
map.insert(123, form.parentphone.0.clone());
|
||||||
"{} {}",
|
map.insert(124, form.parentemail.0.clone());
|
||||||
form.parent_first_name
|
map.insert(125, form.school.0.clone());
|
||||||
.as_ref()
|
map.insert(126, form.grade.0.clone());
|
||||||
.unwrap_or(&Text(String::from("")))
|
map.insert(
|
||||||
.0
|
127,
|
||||||
.clone(),
|
format!("{} {}", form.pastor_first_name.0, form.pastor_last_name.0),
|
||||||
form.parent_last_name
|
);
|
||||||
.as_ref()
|
map.insert(128, form.church_attendance.0.clone());
|
||||||
.unwrap_or(&Text(String::from("")))
|
map.insert(129, form.tfc_group.0.clone());
|
||||||
.0
|
map.insert(130, form.shirt.0.clone());
|
||||||
.clone()
|
map.insert(131, form.trip.0.clone());
|
||||||
);
|
map.insert(132, form.trip_notes.0.clone());
|
||||||
let birthdate = form
|
map.insert(133, form.relationship_with_jesus.0.clone());
|
||||||
.birthdate
|
map.insert(134, form.testimony.0.clone());
|
||||||
.as_ref()
|
map.insert(135, form.involvement_with_group.0.clone());
|
||||||
.unwrap_or(&Text(String::from("")))
|
map.insert(136, form.reasons.0.clone());
|
||||||
.0
|
map.insert(137, form.strengths.0.clone());
|
||||||
.clone();
|
map.insert(138, form.weaknesses.0.clone());
|
||||||
let gender = form
|
map.insert(139, form.previous_trip_info.0.clone());
|
||||||
.gender
|
map.insert(140, form.attitude.0.clone());
|
||||||
.as_ref()
|
map.insert(141, form.relevant_notes.0.clone());
|
||||||
.unwrap_or(&Text(String::from("")))
|
map.insert(144, form.final_agreement.0.clone());
|
||||||
.0
|
map.insert(145, form.registration.0.clone());
|
||||||
.clone();
|
map
|
||||||
let street = form
|
}
|
||||||
.street
|
}
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&Text(String::from("")))
|
impl MtForm {
|
||||||
.0
|
fn build_email(&self) -> Markup {
|
||||||
.clone();
|
html! {
|
||||||
let city = form
|
(DOCTYPE)
|
||||||
.city
|
meta charset="utf-8";
|
||||||
.as_ref()
|
html {
|
||||||
.unwrap_or(&Text(String::from("")))
|
head {
|
||||||
.0
|
title { (self.first_name.0) " " (self.last_name.0) " signed up for mission trip!" }
|
||||||
.clone();
|
style {
|
||||||
let state = form
|
"table { border-collapse: collapse; width: 100% }"
|
||||||
.state
|
"td, th { padding: 8px }"
|
||||||
.as_ref()
|
"td { text-align: left; width: 70%; word-wrap: break-word }"
|
||||||
.unwrap_or(&Text(String::from("")))
|
"th { text-align: right; border-right: 1px solid #ddd }"
|
||||||
.0
|
"tr { border-bottom: 1px solid #ddd }"
|
||||||
.clone();
|
"h1 { text-align: center }"
|
||||||
let zip = form.zip.as_ref().unwrap_or(&Text(0)).0;
|
}
|
||||||
let cellphone = form
|
}
|
||||||
.cellphone
|
body {
|
||||||
.as_ref()
|
h1 { "Mission trip form for " (self.first_name.0) " " (self.last_name.0) "!" }
|
||||||
.unwrap_or(&Text(String::from("")))
|
hr;
|
||||||
.0
|
table {
|
||||||
.clone();
|
tr {
|
||||||
let parentphone = form
|
th { "Name" }
|
||||||
.parentphone
|
td { (self.first_name.0) " " (self.last_name.0) }
|
||||||
.as_ref()
|
}
|
||||||
.unwrap_or(&Text(String::from("")))
|
tr {
|
||||||
.0
|
th { "Parent" }
|
||||||
.clone();
|
td { (self.parent_first_name.0) " " (self.parent_last_name.0) }
|
||||||
let email = form
|
}
|
||||||
.email
|
tr {
|
||||||
.as_ref()
|
th { "Birthdate" }
|
||||||
.unwrap_or(&Text(String::from("")))
|
td { (self.birthdate.0) }
|
||||||
.0
|
}
|
||||||
.clone();
|
tr {
|
||||||
let parentemail = form
|
th { "Gender" }
|
||||||
.parentemail
|
td { (self.gender.0) }
|
||||||
.as_ref()
|
}
|
||||||
.unwrap_or(&Text(String::from("")))
|
tr {
|
||||||
.0
|
th { "Street" }
|
||||||
.clone();
|
td { (self.street.0) }
|
||||||
let school = form
|
}
|
||||||
.school
|
tr {
|
||||||
.as_ref()
|
th { "City" }
|
||||||
.unwrap_or(&Text(String::from("")))
|
td { (self.city.0) }
|
||||||
.0
|
}
|
||||||
.clone();
|
tr {
|
||||||
let grade = form
|
th { "State" }
|
||||||
.grade
|
td { (self.state.0) }
|
||||||
.as_ref()
|
}
|
||||||
.unwrap_or(&Text(String::from("")))
|
tr {
|
||||||
.0
|
th { "Zip" }
|
||||||
.clone();
|
td { (self.zip.0) }
|
||||||
let pastor = format!(
|
}
|
||||||
"{} {}",
|
tr {
|
||||||
form.pastor_first_name
|
th { "Phone" }
|
||||||
.as_ref()
|
td { (self.cellphone.0) }
|
||||||
.unwrap_or(&Text(String::from("")))
|
}
|
||||||
.0
|
tr {
|
||||||
.clone(),
|
th { "Parent Phone" }
|
||||||
form.pastor_last_name
|
td { (self.parentphone.0) }
|
||||||
.as_ref()
|
}
|
||||||
.unwrap_or(&Text(String::from("")))
|
tr {
|
||||||
.0
|
th { "Email" }
|
||||||
.clone()
|
td { (self.email.0) }
|
||||||
);
|
}
|
||||||
let church_attendance = form
|
tr {
|
||||||
.church_attendance
|
th { "Parent Email" }
|
||||||
.as_ref()
|
td { (self.parentemail.0) }
|
||||||
.unwrap_or(&Text(String::from("")))
|
}
|
||||||
.0
|
tr {
|
||||||
.clone();
|
th { "School" }
|
||||||
let tfc_group = form
|
td { (self.school.0) }
|
||||||
.tfc_group
|
}
|
||||||
.as_ref()
|
tr {
|
||||||
.unwrap_or(&Text(String::from("")))
|
th { "Grade" }
|
||||||
.0
|
td { (self.grade.0) }
|
||||||
.clone();
|
}
|
||||||
let shirt = form
|
tr {
|
||||||
.shirt
|
th { "Pastor" }
|
||||||
.as_ref()
|
td { (self.pastor_first_name.0) (self.pastor_last_name.0) }
|
||||||
.unwrap_or(&Text(String::from("")))
|
}
|
||||||
.0
|
tr {
|
||||||
.clone();
|
th { "Church Attendance" }
|
||||||
let trip = form
|
td { (self.church_attendance.0) }
|
||||||
.trip
|
}
|
||||||
.as_ref()
|
tr {
|
||||||
.unwrap_or(&Text(String::from("")))
|
th { "TFC Group" }
|
||||||
.0
|
td { (self.tfc_group.0) }
|
||||||
.clone();
|
}
|
||||||
let trip_notes = form
|
tr {
|
||||||
.trip_notes
|
th { "T-Shirt Size" }
|
||||||
.as_ref()
|
td { (self.shirt.0) }
|
||||||
.unwrap_or(&Text(String::from("")))
|
}
|
||||||
.0
|
tr {
|
||||||
.clone();
|
th { "Trip Choice" }
|
||||||
let relationship = form
|
td { (self.trip.0) }
|
||||||
.relationship_with_jesus
|
}
|
||||||
.as_ref()
|
tr {
|
||||||
.unwrap_or(&Text(String::from("")))
|
th { "Extra Trip Notes" }
|
||||||
.0
|
td { (self.trip_notes.0) }
|
||||||
.clone();
|
}
|
||||||
let testimony = form
|
tr {
|
||||||
.testimony
|
th { "Relationship with Jesus" }
|
||||||
.as_ref()
|
td { (self.relationship_with_jesus.0) }
|
||||||
.unwrap_or(&Text(String::from("")))
|
}
|
||||||
.0
|
tr {
|
||||||
.clone();
|
th { "Testimony" }
|
||||||
let involvement = form
|
td { (self.testimony.0) }
|
||||||
.involvement_with_group
|
}
|
||||||
.as_ref()
|
tr {
|
||||||
.unwrap_or(&Text(String::from("")))
|
th { "Involvement with TFC or Youth Group" }
|
||||||
.0
|
td { (self.involvement_with_group.0) }
|
||||||
.clone();
|
}
|
||||||
let reasons = form
|
tr {
|
||||||
.reasons
|
th { "Reasons for trip choice" }
|
||||||
.as_ref()
|
td { (self.reasons.0) }
|
||||||
.unwrap_or(&Text(String::from("")))
|
}
|
||||||
.0
|
tr {
|
||||||
.clone();
|
th { "Strengths" }
|
||||||
let strengths = form
|
td { (self.strengths.0) }
|
||||||
.strengths
|
}
|
||||||
.as_ref()
|
tr {
|
||||||
.unwrap_or(&Text(String::from("")))
|
th { "Weaknesses" }
|
||||||
.0
|
td { (self.weaknesses.0) }
|
||||||
.clone();
|
}
|
||||||
let weaknesses = form
|
tr {
|
||||||
.weaknesses
|
th { "Previous Trips" }
|
||||||
.as_ref()
|
td { (self.previous_trip_info.0) }
|
||||||
.unwrap_or(&Text(String::from("")))
|
}
|
||||||
.0
|
tr {
|
||||||
.clone();
|
th { "Attitude Torward Work" }
|
||||||
let previous_trip = form
|
td { (self.attitude.0) }
|
||||||
.previous_trip_info
|
}
|
||||||
.as_ref()
|
tr {
|
||||||
.unwrap_or(&Text(String::from("")))
|
th { "Other Relevant Info" }
|
||||||
.0
|
td { (self.relevant_notes.0) }
|
||||||
.clone();
|
}
|
||||||
let attitude = form
|
tr {
|
||||||
.attitude
|
th { "Final Agreement" }
|
||||||
.as_ref()
|
td { (self.final_agreement.0) }
|
||||||
.unwrap_or(&Text(String::from("")))
|
}
|
||||||
.0
|
tr {
|
||||||
.clone();
|
th { "Registration" }
|
||||||
let relevant = form
|
td { (self.registration.0) }
|
||||||
.relevant_notes
|
}
|
||||||
.as_ref()
|
}
|
||||||
.unwrap_or(&Text(String::from("")))
|
}
|
||||||
.0
|
}
|
||||||
.clone();
|
}
|
||||||
let final_agreement = form
|
}
|
||||||
.final_agreement
|
|
||||||
.as_ref()
|
fn get_temp_file(&self) -> Option<(String, String, Option<String>)> {
|
||||||
.unwrap_or(&Text(String::from("")))
|
let first = self.first_name.clone();
|
||||||
.0
|
let last = self.last_name.clone();
|
||||||
.clone();
|
let filename_noext = format!("{}_{}", first, last);
|
||||||
let registration = form
|
let (file_name, content_type) = if let Some(file) = self.file.as_ref() {
|
||||||
.registration
|
let content_type = file.content_type.clone().map(|m| m.to_string());
|
||||||
.as_ref()
|
(file.file_name.to_owned(), content_type)
|
||||||
.unwrap_or(&Text(String::from("")))
|
} else {
|
||||||
.0
|
return None;
|
||||||
.clone();
|
};
|
||||||
log::info!("{first} {last} signed up for mission trip!");
|
let filename;
|
||||||
let email = markup::new! {
|
let path = if let Some(file_name) = file_name {
|
||||||
@markup::doctype()
|
if let Some(ext) = file_name.rsplit('.').next() {
|
||||||
html {
|
filename = format!("{}.{}", filename_noext, ext);
|
||||||
head {
|
format!("./tmp/{}.{}", filename_noext, ext)
|
||||||
title { @format!("{} {} signed up for mission trip!", first, last) }
|
} else {
|
||||||
style {
|
filename = String::default();
|
||||||
"table { border-collapse: collapse; width: 100% }"
|
format!("./tmp/{}", file_name)
|
||||||
"td, th { padding: 8px }"
|
}
|
||||||
"td { text-align: left; width: 70%; word-wrap: break-word }"
|
} else {
|
||||||
"th { text-align: right; border-right: 1px solid #ddd }"
|
filename = String::default();
|
||||||
"tr { border-bottom: 1px solid #ddd }"
|
String::default()
|
||||||
"h1 { text-align: center }"
|
};
|
||||||
}
|
if let Some(file) = &self.file {
|
||||||
}
|
let file = file.file.path();
|
||||||
body {
|
match fs::copy(file, &path) {
|
||||||
h1 { @format!("Mission trip form for {} {}!", first, last) }
|
Ok(f) => {
|
||||||
hr;
|
if f <= 0 {
|
||||||
table {
|
return None;
|
||||||
tr {
|
}
|
||||||
th { "Name" }
|
info!(?f, "File saved successfully");
|
||||||
td { @format!("{} {}", first, last) }
|
Some((filename, path, content_type.clone()))
|
||||||
}
|
}
|
||||||
tr {
|
Err(e) => {
|
||||||
th { "Parent" }
|
error!("{:?}: Probably a missing image", e);
|
||||||
td { @parent }
|
None
|
||||||
}
|
}
|
||||||
tr {
|
}
|
||||||
th { "Birthdate" }
|
} else {
|
||||||
td { @birthdate }
|
error!("Error in tempfile");
|
||||||
}
|
None
|
||||||
tr {
|
}
|
||||||
th { "Gender" }
|
}
|
||||||
td { @gender }
|
|
||||||
}
|
fn prepare_email(&self) -> Result<Message> {
|
||||||
tr {
|
let first = self.first_name.clone();
|
||||||
th { "Street" }
|
let last = self.last_name.clone();
|
||||||
td { @street }
|
let email_subject = format!("{} {} signed up for mission trip!", first, last);
|
||||||
}
|
info!("{first} {last} signed up for mission trip!");
|
||||||
tr {
|
let email = self.build_email();
|
||||||
th { "City" }
|
let temp_file = self.get_temp_file();
|
||||||
td { @city }
|
let multi = if let Some((file, path, content_type)) = temp_file {
|
||||||
}
|
let filebody = fs::read(path);
|
||||||
tr {
|
let content_type =
|
||||||
th { "State" }
|
ContentType::parse(&content_type.unwrap_or(String::from("image/jpg"))).unwrap();
|
||||||
td { @state }
|
let attachment = Attachment::new(file).body(filebody.unwrap(), content_type);
|
||||||
}
|
// info!(?attachment);
|
||||||
tr {
|
MultiPart::mixed()
|
||||||
th { "Zip" }
|
.singlepart(SinglePart::html(email.into_string()))
|
||||||
td { @zip }
|
.singlepart(attachment)
|
||||||
}
|
} else {
|
||||||
tr {
|
MultiPart::alternative_plain_html(String::from("Testing"), email.into_string())
|
||||||
th { "Phone" }
|
};
|
||||||
td { @cellphone }
|
|
||||||
}
|
Message::builder()
|
||||||
tr {
|
.from(
|
||||||
th { "Parent Phone" }
|
"TFC ADMIN <no-reply@mail.tfcconnection.org>"
|
||||||
td { @parentphone }
|
.parse()
|
||||||
}
|
.unwrap(),
|
||||||
tr {
|
)
|
||||||
th { "Email" }
|
.to("Chris Cochrun <chris@tfcconnection.org>".parse().unwrap())
|
||||||
td { @email }
|
.to("Ethan Rose <ethan@tfcconnection.org>".parse().unwrap())
|
||||||
}
|
.subject(email_subject)
|
||||||
tr {
|
.multipart(multi)
|
||||||
th { "Parent Email" }
|
.wrap_err("problemss")
|
||||||
td { @parentemail }
|
}
|
||||||
}
|
}
|
||||||
tr {
|
|
||||||
th { "School" }
|
#[post("/api/mt-form")]
|
||||||
td { @school }
|
pub async fn mt_form(MultipartForm(form): MultipartForm<MtForm>) -> HttpResponse {
|
||||||
}
|
let name = format!("{} {}", form.first_name.0, form.last_name.0);
|
||||||
tr {
|
let map = HashMap::from(&form);
|
||||||
th { "Grade" }
|
let store = store_form(map);
|
||||||
td { @grade }
|
actix_rt::spawn(store.map(|s| match s {
|
||||||
}
|
Ok(_) => info!("Successfully sent form to nextcloud!"),
|
||||||
tr {
|
Err(e) => error!("There was an erroring sending form to nextcloud: {e}"),
|
||||||
th { "Pastor" }
|
}));
|
||||||
td { @pastor }
|
let email = form.prepare_email();
|
||||||
}
|
match email {
|
||||||
tr {
|
Ok(m) => {
|
||||||
th { "Church Attendance" }
|
let sent = crate::email::send_email(m);
|
||||||
td { @church_attendance }
|
actix_rt::spawn(sent.map(|s| match s {
|
||||||
}
|
Ok(_) => info!("Successfully sent form to email!"),
|
||||||
tr {
|
Err(e) => error!("There was an erroring sending form to email: {e}"),
|
||||||
th { "TFC Group" }
|
}));
|
||||||
td { @tfc_group }
|
}
|
||||||
}
|
Err(e) => error!("error sending email {e}"),
|
||||||
tr {
|
};
|
||||||
th { "T-Shirt Size" }
|
match form.registration.0.as_str() {
|
||||||
td { @shirt }
|
"now" => {
|
||||||
}
|
if form.health_form.0.as_str() == "yes" {
|
||||||
tr {
|
HttpResponse::Ok()
|
||||||
th { "Trip Choice" }
|
.insert_header(("Access-Control-Expose-Headers", "*"))
|
||||||
td { @trip }
|
.insert_header(("HX-Redirect", "/health-form?registration=now"))
|
||||||
}
|
.finish()
|
||||||
tr {
|
} else {
|
||||||
th { "Extra Trip Notes" }
|
HttpResponse::Ok()
|
||||||
td { @trip_notes }
|
.insert_header(("Access-Control-Expose-Headers", "*"))
|
||||||
}
|
.insert_header((
|
||||||
tr {
|
"HX-Redirect",
|
||||||
th { "Relationship with Jesus" }
|
"https://secure.myvanco.com/L-Z772/campaign/C-13DM3",
|
||||||
td { @relationship }
|
))
|
||||||
}
|
.finish()
|
||||||
tr {
|
}
|
||||||
th { "Testimony" }
|
}
|
||||||
td { @testimony }
|
"later" => {
|
||||||
}
|
if form.health_form.0.as_str() == "yes" {
|
||||||
tr {
|
HttpResponse::Ok()
|
||||||
th { "Involvement with TFC or Youth Group" }
|
.insert_header(("Access-Control-Expose-Headers", "*"))
|
||||||
td { @involvement }
|
.insert_header(("HX-Redirect", "/health-form?registration=later"))
|
||||||
}
|
.finish()
|
||||||
tr {
|
} else {
|
||||||
th { "Reasons for trip choice" }
|
HttpResponse::Ok().body(
|
||||||
td { @reasons }
|
html! {
|
||||||
}
|
h2 { "Thank you! {}" (name)}
|
||||||
tr {
|
p { "You can go to the health form "
|
||||||
th { "Strengths" }
|
a href="/health-form" { "here" }
|
||||||
td { @strengths }
|
" or you can pay for mission trip "
|
||||||
}
|
a href="https://secure.myvanco.com/L-Z772/campaign/C-13DM3" { "here" }
|
||||||
tr {
|
}
|
||||||
th { "Weaknesses" }
|
}
|
||||||
td { @weaknesses }
|
.into_string(),
|
||||||
}
|
)
|
||||||
tr {
|
}
|
||||||
th { "Previous Trips" }
|
}
|
||||||
td { @previous_trip }
|
_ => {
|
||||||
}
|
error!("There wasn't an option for the registration passed in.");
|
||||||
tr {
|
HttpResponse::Ok().body(
|
||||||
th { "Attitude Torward Work" }
|
html! {
|
||||||
td { @attitude }
|
h2 { "Thank you! {}" (name)}
|
||||||
}
|
p { "You can go to the health form "
|
||||||
tr {
|
a href="/health-form" { "here" }
|
||||||
th { "Other Relevant Info" }
|
" or you can pay for mission trip "
|
||||||
td { @relevant }
|
a href="https://secure.myvanco.com/L-Z772/campaign/C-13DM3" { "here" }
|
||||||
}
|
}
|
||||||
tr {
|
}
|
||||||
th { "Final Agreement" }
|
.into_string(),
|
||||||
td { @final_agreement }
|
)
|
||||||
}
|
}
|
||||||
tr {
|
}
|
||||||
th { "Registration" }
|
}
|
||||||
td { @registration }
|
|
||||||
}
|
async fn store_form(map: HashMap<i32, String>) -> Result<()> {
|
||||||
}
|
let client = Client::new();
|
||||||
}
|
// let map = HashMap::from(self);
|
||||||
}
|
let mut json = HashMap::new();
|
||||||
};
|
json.insert("data", map);
|
||||||
let mut path: Option<String> = Some(String::from(""));
|
|
||||||
let mut file_exists = false;
|
let res = client
|
||||||
let mut filename = String::from("");
|
.post("https://staff.tfcconnection.org/ocs/v2.php/apps/tables/api/2/tables/9/rows")
|
||||||
log::info!("{:?}", file_exists);
|
.basic_auth("chris", Some("2VHeGxeC^Zf9KqFK^G@Pt!zu2q^6@b"))
|
||||||
if let Some(f) = form.file {
|
.header("OCS-APIRequest", "true")
|
||||||
if let Some(file) = f.file_name {
|
.header("Content-Type", "application/json")
|
||||||
if let Some(ext) = file.rsplit('.').next() {
|
.json(&json)
|
||||||
filename = format!("{}.{}", filename_noext, ext);
|
.send()
|
||||||
path = Some(format!("./tmp/{}.{}", filename_noext, ext));
|
.await?;
|
||||||
} else {
|
if res.status().is_success() {
|
||||||
path = Some(format!("./tmp/{}", file));
|
// let res = res.text().await.unwrap();
|
||||||
}
|
Ok(())
|
||||||
// let path = format!("./tmp/{}", file);
|
} else {
|
||||||
log::info!("saving to {}", path.clone().unwrap());
|
Err(eyre!(
|
||||||
match f.file.persist(path.clone().unwrap()) {
|
"Problem in storing data: {:?}",
|
||||||
Ok(f) => {
|
res.error_for_status()
|
||||||
log::info!("{:?}", f);
|
))
|
||||||
if f.metadata().unwrap().len() > 0 {
|
}
|
||||||
file_exists = true;
|
}
|
||||||
}
|
|
||||||
}
|
#[cfg(test)]
|
||||||
Err(e) => log::info!("{:?}: Probably a missing image", e),
|
mod test {
|
||||||
}
|
use actix_web::test;
|
||||||
}
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn form() -> MtForm {
|
||||||
|
MtForm {
|
||||||
|
first_name: Text(String::from("Frodo")),
|
||||||
|
last_name: Text(String::from("Braggins")),
|
||||||
|
parent_first_name: Text(String::from("Bilbo")),
|
||||||
|
parent_last_name: Text(String::from("Braggins")),
|
||||||
|
birthdate: Text(String::from("1845-09-12")),
|
||||||
|
gender: Text(String::from("male")),
|
||||||
|
street: Text(String::from("1234 Bag End")),
|
||||||
|
city: Text(String::from("The Shire")),
|
||||||
|
state: Text(String::from("Hobbiton")),
|
||||||
|
zip: Text(88888),
|
||||||
|
cellphone: Text(String::from("7868889797")),
|
||||||
|
parentphone: Text(String::from("1234567898")),
|
||||||
|
email: Text(String::from("frodo@hobbits.com")),
|
||||||
|
parentemail: Text(String::from("bilbo@hobbits.com")),
|
||||||
|
school: Text(String::from("Shire High")),
|
||||||
|
grade: Text(String::from("junior")),
|
||||||
|
pastor_first_name: Text(String::from("Gandalf")),
|
||||||
|
pastor_last_name: Text(String::from("The White")),
|
||||||
|
church_attendance: Text(String::from("often")),
|
||||||
|
tfc_group: Text(String::from("Northern Valley")),
|
||||||
|
shirt: Text(String::from("medium")),
|
||||||
|
trip: Text(String::from("Mordor")),
|
||||||
|
trip_notes: Text(String::from("If it must happen, I'll do it.")),
|
||||||
|
relationship_with_jesus: Text(String::from("Cool beans")),
|
||||||
|
testimony: Text(String::from("Nephew of Bilbo Braggins")),
|
||||||
|
involvement_with_group: Text(String::from("Good friends with Gandalf")),
|
||||||
|
reasons: Text(String::from("Want an adventure")),
|
||||||
|
strengths: Text(String::from("Willing, brave, small, and curious")),
|
||||||
|
weaknesses: Text(String::from("Not strong, or good with weapons")),
|
||||||
|
previous_trip_info: Text(String::from("The edge of Hob Hill")),
|
||||||
|
attitude: Text(String::from("Willing")),
|
||||||
|
relevant_notes: Text(String::from("Willing to take the ring")),
|
||||||
|
final_agreement: Text(String::from("yes")),
|
||||||
|
registration: Text(String::from("later")),
|
||||||
|
health_form: Text(String::from("yes")),
|
||||||
|
file: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
async fn test_nc_post() {
|
||||||
|
let form = form();
|
||||||
|
assert!(!form.first_name.is_empty());
|
||||||
|
let map = HashMap::from(&form);
|
||||||
|
let res = store_form(map).await;
|
||||||
|
match res {
|
||||||
|
Ok(_) => assert!(true, "passed storing test"),
|
||||||
|
Err(e) => assert!(false, "Failed storing test: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
async fn test_email() {
|
||||||
|
let form = form();
|
||||||
|
assert!(!form.first_name.is_empty());
|
||||||
|
match form.prepare_email() {
|
||||||
|
Ok(m) => {
|
||||||
|
assert!(crate::email::send_email(m).await.is_ok())
|
||||||
|
}
|
||||||
|
Err(e) => assert!(false, "Failed emailing test: {e}"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let multi = if file_exists {
|
|
||||||
let filebody = fs::read(path.clone().unwrap_or_default());
|
|
||||||
let content_type = ContentType::parse("image/jpg").unwrap();
|
|
||||||
let attachment = Attachment::new(filename).body(filebody.unwrap(), content_type);
|
|
||||||
log::info!("{:?}", attachment);
|
|
||||||
MultiPart::mixed()
|
|
||||||
.singlepart(SinglePart::html(email.to_string()))
|
|
||||||
.singlepart(attachment)
|
|
||||||
} else {
|
|
||||||
MultiPart::alternative_plain_html(String::from("Testing"), email.to_string())
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Ok(m) = Message::builder()
|
|
||||||
.from(
|
|
||||||
"TFC ADMIN <no-reply@mail.tfcconnection.org>"
|
|
||||||
.parse()
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
.to("Chris Cochrun <chris@tfcconnection.org>".parse().unwrap())
|
|
||||||
.to("Ethan Rose <ethan@tfcconnection.org>".parse().unwrap())
|
|
||||||
.subject(email_subject)
|
|
||||||
.multipart(multi)
|
|
||||||
{
|
|
||||||
let sender = SmtpTransport::relay("mail.tfcconnection.org")
|
|
||||||
.ok()
|
|
||||||
.unwrap()
|
|
||||||
.credentials(Credentials::new(
|
|
||||||
"no-reply@mail.tfcconnection.org".to_owned(),
|
|
||||||
"r9f36mNZFtiW4f".to_owned(),
|
|
||||||
))
|
|
||||||
.authentication(vec![Mechanism::Plain])
|
|
||||||
.build();
|
|
||||||
match sender.send(&m) {
|
|
||||||
Ok(res) => log::info!("{:?}", res),
|
|
||||||
Err(e) => log::info!("{e}"),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log::info!("Email incorrect");
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpResponse::Ok().body("thankyou")
|
|
||||||
}
|
}
|
||||||
|
|
241
src/api/mt_parent_form.rs
Normal file
241
src/api/mt_parent_form.rs
Normal file
|
@ -0,0 +1,241 @@
|
||||||
|
use std::{
|
||||||
|
collections::{BTreeMap, HashMap},
|
||||||
|
fs,
|
||||||
|
};
|
||||||
|
|
||||||
|
use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
|
||||||
|
use actix_web::{post, web, HttpResponse};
|
||||||
|
use color_eyre::{eyre::eyre, Result};
|
||||||
|
use lettre::{
|
||||||
|
message::{header::ContentType, Attachment, MultiPart, SinglePart},
|
||||||
|
Message,
|
||||||
|
};
|
||||||
|
use maud::{html, Markup, PreEscaped, DOCTYPE};
|
||||||
|
use reqwest::Client;
|
||||||
|
use serde_json::json;
|
||||||
|
use sqlx::SqliteConnection;
|
||||||
|
use tracing::{error, info};
|
||||||
|
|
||||||
|
#[derive(Debug, MultipartForm)]
|
||||||
|
struct MtParentForm {
|
||||||
|
#[multipart(rename = "firstname")]
|
||||||
|
first_name: Text<String>,
|
||||||
|
#[multipart(rename = "lastname")]
|
||||||
|
last_name: Text<String>,
|
||||||
|
email: Text<String>,
|
||||||
|
#[multipart(rename = "studentfirstname")]
|
||||||
|
student_first_name: Text<String>,
|
||||||
|
#[multipart(rename = "studentlastname")]
|
||||||
|
student_last_name: Text<String>,
|
||||||
|
authority: Text<String>,
|
||||||
|
positive: Text<String>,
|
||||||
|
negative: Text<String>,
|
||||||
|
#[multipart(rename = "family-relation")]
|
||||||
|
family: Text<String>,
|
||||||
|
#[multipart(rename = "previous-trip-info")]
|
||||||
|
previous_trip: Text<String>,
|
||||||
|
#[multipart(rename = "trip-feelings")]
|
||||||
|
feelings: Text<String>,
|
||||||
|
#[multipart(rename = "extra-info")]
|
||||||
|
extra_info: Text<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&MtParentForm> for HashMap<i32, String> {
|
||||||
|
fn from(form: &MtParentForm) -> Self {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
map.insert(167, format!("{} {}", form.first_name.0, form.last_name.0));
|
||||||
|
map.insert(
|
||||||
|
168,
|
||||||
|
format!("{} {}", form.student_first_name.0, form.student_last_name.0),
|
||||||
|
);
|
||||||
|
map.insert(169, form.authority.0.clone());
|
||||||
|
map.insert(170, form.positive.0.clone());
|
||||||
|
map.insert(171, form.negative.0.clone());
|
||||||
|
map.insert(172, form.family.0.clone());
|
||||||
|
map.insert(173, form.previous_trip.0.clone());
|
||||||
|
map.insert(174, form.feelings.0.clone());
|
||||||
|
map.insert(175, form.extra_info.0.clone());
|
||||||
|
map.insert(176, form.email.0.clone());
|
||||||
|
map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MtParentForm {
|
||||||
|
async fn build_email(&self) -> Markup {
|
||||||
|
html! {
|
||||||
|
(DOCTYPE)
|
||||||
|
meta charset="utf-8";
|
||||||
|
html {
|
||||||
|
head {
|
||||||
|
title { (self.first_name.0) " " (self.last_name.0) " filled out a parent form for " (self.student_first_name.0) " " (self.student_last_name.0) "!" }
|
||||||
|
style {
|
||||||
|
"table { border-collapse: collapse; width: 100% }"
|
||||||
|
"td, th { padding: 8px }"
|
||||||
|
"td { text-align: left; width: 70%; word-wrap: break-word }"
|
||||||
|
"th { text-align: right; border-right: 1px solid #ddd }"
|
||||||
|
"tr { border-bottom: 1px solid #ddd }"
|
||||||
|
"h1 { text-align: center }"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
h1 { "Parent reference form for " (self.student_first_name.0) " " (self.student_last_name.0) "!" }
|
||||||
|
hr;
|
||||||
|
table {
|
||||||
|
tr {
|
||||||
|
th { "Name" }
|
||||||
|
td { (self.first_name.0) " " (self.last_name.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Student" }
|
||||||
|
td { (self.student_first_name.0) " " (self.student_last_name.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Email" }
|
||||||
|
td { (self.email.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Authority" }
|
||||||
|
td { (self.authority.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Positive characteristics" }
|
||||||
|
td { (self.positive.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Negative characteristics" }
|
||||||
|
td { (self.negative.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Family Relations" }
|
||||||
|
td { (self.family.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Previous Trip" }
|
||||||
|
td { (self.previous_trip.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Trip Feelings" }
|
||||||
|
td { (self.feelings.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Other Relevant Info" }
|
||||||
|
td { (self.extra_info.0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn store_form(&self) -> Result<()> {
|
||||||
|
let client = Client::new();
|
||||||
|
let map = HashMap::from(self);
|
||||||
|
let mut json = HashMap::new();
|
||||||
|
json.insert("data", map);
|
||||||
|
|
||||||
|
let link = r#"https://staff.tfcconnection.org/apps/tables/#/table/14/row/757"#;
|
||||||
|
let res = client
|
||||||
|
.post("https://staff.tfcconnection.org/ocs/v2.php/apps/tables/api/2/tables/14/rows")
|
||||||
|
.basic_auth("chris", Some("2VHeGxeC^Zf9KqFK^G@Pt!zu2q^6@b"))
|
||||||
|
.header("OCS-APIRequest", "true")
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.json(&json)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
if res.status().is_success() {
|
||||||
|
let res = res.text().await.unwrap();
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(eyre!(
|
||||||
|
"Problem in storing data: {:?}",
|
||||||
|
res.error_for_status()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_email(&self) -> Result<()> {
|
||||||
|
let first = self.student_first_name.clone();
|
||||||
|
let last = self.student_last_name.clone();
|
||||||
|
let email_subject = format!("Parent reference form for {} {}!", first, last);
|
||||||
|
info!("{first} {last} parent reference form!");
|
||||||
|
let email = self.build_email().await;
|
||||||
|
let email = SinglePart::html(email.into_string());
|
||||||
|
|
||||||
|
if let Ok(m) = Message::builder()
|
||||||
|
.from(
|
||||||
|
"TFC ADMIN <no-reply@mail.tfcconnection.org>"
|
||||||
|
.parse()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.to("Chris Cochrun <chris@tfcconnection.org>".parse().unwrap())
|
||||||
|
.to("Ethan Rose <ethan@tfcconnection.org>".parse().unwrap())
|
||||||
|
.subject(email_subject)
|
||||||
|
.singlepart(email)
|
||||||
|
{
|
||||||
|
crate::email::send_email(m).await
|
||||||
|
} else {
|
||||||
|
Err(eyre!("Email incorrect"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/api/mt-parent-form")]
|
||||||
|
pub async fn mt_parent_form(MultipartForm(form): MultipartForm<MtParentForm>) -> HttpResponse {
|
||||||
|
match form.store_form().await {
|
||||||
|
Ok(_) => info!("Successfully sent form to nextcloud!"),
|
||||||
|
Err(e) => error!("There was an erroring sending form to nextcloud: {e}"),
|
||||||
|
}
|
||||||
|
match form.send_email().await {
|
||||||
|
Ok(_) => info!("Successfully sent email"),
|
||||||
|
Err(e) => error!("There was an error sending the email: {e}"),
|
||||||
|
}
|
||||||
|
HttpResponse::Ok().body("thankyou")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use actix_web::test;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use sqlx::Connection;
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn form() -> MtParentForm {
|
||||||
|
MtParentForm {
|
||||||
|
first_name: Text(String::from("Bilbo")),
|
||||||
|
last_name: Text(String::from("Braggins")),
|
||||||
|
student_first_name: Text(String::from("Frodo")),
|
||||||
|
student_last_name: Text(String::from("Braggins")),
|
||||||
|
email: Text(String::from("biblo@hobbits.us")),
|
||||||
|
authority: Text(String::from("Uncle")),
|
||||||
|
positive: Text(String::from("Nimble and brave")),
|
||||||
|
negative: Text(String::from("Small")),
|
||||||
|
family: Text(String::from("Such a strutter")),
|
||||||
|
previous_trip: Text(String::from("Super")),
|
||||||
|
feelings: Text(String::from("Very")),
|
||||||
|
extra_info: Text(String::from("Willing to take the ring")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
async fn test_nc_post() {
|
||||||
|
let form = form();
|
||||||
|
assert!(!form.first_name.is_empty());
|
||||||
|
let res = form.store_form().await;
|
||||||
|
match res {
|
||||||
|
Ok(_) => assert!(true, "passed storing test"),
|
||||||
|
Err(e) => assert!(false, "Failed storing test: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
async fn test_email() {
|
||||||
|
let mut form = form();
|
||||||
|
assert!(!form.first_name.is_empty());
|
||||||
|
match form.send_email().await {
|
||||||
|
Ok(_) => assert!(true, "passed emailing test"),
|
||||||
|
Err(e) => assert!(false, "Failed emailing test: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
234
src/api/mt_teacher_form.rs
Normal file
234
src/api/mt_teacher_form.rs
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
use std::{
|
||||||
|
collections::{BTreeMap, HashMap},
|
||||||
|
fs,
|
||||||
|
};
|
||||||
|
|
||||||
|
use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
|
||||||
|
use actix_web::{post, web, HttpResponse};
|
||||||
|
use color_eyre::{eyre::eyre, Result};
|
||||||
|
use lettre::{
|
||||||
|
message::{header::ContentType, Attachment, MultiPart, SinglePart},
|
||||||
|
Message,
|
||||||
|
};
|
||||||
|
use maud::{html, Markup, PreEscaped, DOCTYPE};
|
||||||
|
use reqwest::Client;
|
||||||
|
use serde_json::json;
|
||||||
|
use sqlx::SqliteConnection;
|
||||||
|
use tracing::{error, info};
|
||||||
|
|
||||||
|
#[derive(Debug, MultipartForm)]
|
||||||
|
struct MtTeacherForm {
|
||||||
|
#[multipart(rename = "firstname")]
|
||||||
|
first_name: Text<String>,
|
||||||
|
#[multipart(rename = "lastname")]
|
||||||
|
last_name: Text<String>,
|
||||||
|
#[multipart(rename = "studentfirstname")]
|
||||||
|
student_first_name: Text<String>,
|
||||||
|
#[multipart(rename = "studentlastname")]
|
||||||
|
student_last_name: Text<String>,
|
||||||
|
relationship: Text<String>,
|
||||||
|
positive: Text<String>,
|
||||||
|
negative: Text<String>,
|
||||||
|
attitudes: Text<String>,
|
||||||
|
#[multipart(rename = "team-challenges")]
|
||||||
|
challenges: Text<String>,
|
||||||
|
behavior: Text<String>,
|
||||||
|
#[multipart(rename = "extra-info")]
|
||||||
|
extra_info: Text<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&MtTeacherForm> for HashMap<i32, String> {
|
||||||
|
fn from(form: &MtTeacherForm) -> Self {
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
map.insert(150, format!("{} {}", form.first_name.0, form.last_name.0));
|
||||||
|
map.insert(
|
||||||
|
151,
|
||||||
|
format!("{} {}", form.student_first_name.0, form.student_last_name.0),
|
||||||
|
);
|
||||||
|
map.insert(152, form.relationship.0.clone());
|
||||||
|
map.insert(153, form.positive.0.clone());
|
||||||
|
map.insert(154, form.negative.0.clone());
|
||||||
|
map.insert(155, form.attitudes.0.clone());
|
||||||
|
map.insert(156, form.challenges.0.clone());
|
||||||
|
map.insert(157, form.extra_info.0.clone());
|
||||||
|
map.insert(177, form.behavior.0.clone());
|
||||||
|
map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MtTeacherForm {
|
||||||
|
async fn build_email(&self) -> Markup {
|
||||||
|
html! {
|
||||||
|
(DOCTYPE)
|
||||||
|
meta charset="utf-8";
|
||||||
|
html {
|
||||||
|
head {
|
||||||
|
title { (self.first_name.0) " " (self.last_name.0) " filled out a teacher reference form for " (self.student_first_name.0) " " (self.student_last_name.0) "!" }
|
||||||
|
style {
|
||||||
|
"table { border-collapse: collapse; width: 100% }"
|
||||||
|
"td, th { padding: 8px }"
|
||||||
|
"td { text-align: left; width: 70%; word-wrap: break-word }"
|
||||||
|
"th { text-align: right; border-right: 1px solid #ddd }"
|
||||||
|
"tr { border-bottom: 1px solid #ddd }"
|
||||||
|
"h1 { text-align: center }"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
h1 { "Teacher reference form for " (self.student_first_name.0) " " (self.student_last_name.0) "!" }
|
||||||
|
hr;
|
||||||
|
table {
|
||||||
|
tr {
|
||||||
|
th { "Name" }
|
||||||
|
td { (self.first_name.0) " " (self.last_name.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Student" }
|
||||||
|
td { (self.student_first_name.0) " " (self.student_last_name.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Relationship with student" }
|
||||||
|
td { (self.relationship.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Positive characteristics" }
|
||||||
|
td { (self.positive.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Negative characteristics" }
|
||||||
|
td { (self.negative.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Attitudes" }
|
||||||
|
td { (self.attitudes.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Teamwork" }
|
||||||
|
td { (self.challenges.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Behavior in school" }
|
||||||
|
td { (self.behavior.0) }
|
||||||
|
}
|
||||||
|
tr {
|
||||||
|
th { "Other Relevant Info" }
|
||||||
|
td { (self.extra_info.0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn store_form(&self) -> Result<()> {
|
||||||
|
let client = Client::new();
|
||||||
|
let map = HashMap::from(self);
|
||||||
|
let mut json = HashMap::new();
|
||||||
|
json.insert("data", map);
|
||||||
|
|
||||||
|
let link = r#"https://staff.tfcconnection.org/apps/tables/#/table/12/row/757"#;
|
||||||
|
let res = client
|
||||||
|
.post("https://staff.tfcconnection.org/ocs/v2.php/apps/tables/api/2/tables/12/rows")
|
||||||
|
.basic_auth("chris", Some("2VHeGxeC^Zf9KqFK^G@Pt!zu2q^6@b"))
|
||||||
|
.header("OCS-APIRequest", "true")
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.json(&json)
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
if res.status().is_success() {
|
||||||
|
let res = res.text().await.unwrap();
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(eyre!(
|
||||||
|
"Problem in storing data: {:?}",
|
||||||
|
res.error_for_status()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_email(&mut self) -> Result<()> {
|
||||||
|
let first = self.student_first_name.clone();
|
||||||
|
let last = self.student_last_name.clone();
|
||||||
|
let email_subject = format!("Teacher reference form for {} {}!", first, last);
|
||||||
|
info!("{first} {last} teacher reference form!");
|
||||||
|
let email = self.build_email().await;
|
||||||
|
let email = SinglePart::html(email.into_string());
|
||||||
|
|
||||||
|
if let Ok(m) = Message::builder()
|
||||||
|
.from(
|
||||||
|
"TFC ADMIN <no-reply@mail.tfcconnection.org>"
|
||||||
|
.parse()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.to("Chris Cochrun <chris@tfcconnection.org>".parse().unwrap())
|
||||||
|
.to("Ethan Rose <ethan@tfcconnection.org>".parse().unwrap())
|
||||||
|
.subject(email_subject)
|
||||||
|
.singlepart(email)
|
||||||
|
{
|
||||||
|
crate::email::send_email(m).await
|
||||||
|
} else {
|
||||||
|
Err(eyre!("Email incorrect"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[post("/api/mt-teacher-form")]
|
||||||
|
pub async fn mt_teacher_form(
|
||||||
|
MultipartForm(mut form): MultipartForm<MtTeacherForm>,
|
||||||
|
) -> HttpResponse {
|
||||||
|
match form.store_form().await {
|
||||||
|
Ok(_) => info!("Successfully sent form to nextcloud!"),
|
||||||
|
Err(e) => error!("There was an erroring sending form to nextcloud: {e}"),
|
||||||
|
}
|
||||||
|
match form.send_email().await {
|
||||||
|
Ok(_) => info!("Successfully sent email"),
|
||||||
|
Err(e) => error!("There was an error sending the email: {e}"),
|
||||||
|
}
|
||||||
|
HttpResponse::Ok().body("thankyou")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use actix_web::test;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use sqlx::Connection;
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
fn form() -> MtTeacherForm {
|
||||||
|
MtTeacherForm {
|
||||||
|
first_name: Text(String::from("Bilbo")),
|
||||||
|
last_name: Text(String::from("Braggins")),
|
||||||
|
student_first_name: Text(String::from("Frodo")),
|
||||||
|
student_last_name: Text(String::from("Braggins")),
|
||||||
|
relationship: Text(String::from("Uncle")),
|
||||||
|
positive: Text(String::from("Nimble and brave")),
|
||||||
|
negative: Text(String::from("Small")),
|
||||||
|
attitudes: Text(String::from("Lighthearted")),
|
||||||
|
challenges: Text(String::from("Willing")),
|
||||||
|
behavior: Text(String::from("Good")),
|
||||||
|
extra_info: Text(String::from("Willing to take the ring")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
async fn test_nc_post() {
|
||||||
|
let form = form();
|
||||||
|
assert!(!form.first_name.is_empty());
|
||||||
|
let res = form.store_form().await;
|
||||||
|
match res {
|
||||||
|
Ok(_) => assert!(true, "passed storing test"),
|
||||||
|
Err(e) => assert!(false, "Failed storing test: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
async fn test_email() {
|
||||||
|
let mut form = form();
|
||||||
|
assert!(!form.first_name.is_empty());
|
||||||
|
match form.send_email().await {
|
||||||
|
Ok(_) => assert!(true, "passed emailing test"),
|
||||||
|
Err(e) => assert!(false, "Failed emailing test: {e}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/email.rs
Normal file
30
src/email.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
use color_eyre::Result;
|
||||||
|
use lettre::{
|
||||||
|
transport::smtp::authentication::{Credentials, Mechanism},
|
||||||
|
Message, SmtpTransport, Transport,
|
||||||
|
};
|
||||||
|
use tracing::{error, info};
|
||||||
|
|
||||||
|
pub async fn send_email(message: Message) -> Result<()> {
|
||||||
|
let sender = SmtpTransport::relay("mail.tfcconnection.org")
|
||||||
|
.ok()
|
||||||
|
.unwrap()
|
||||||
|
.credentials(Credentials::new(
|
||||||
|
"no-reply@mail.tfcconnection.org".to_owned(),
|
||||||
|
"r9f36mNZFtiW4f".to_owned(),
|
||||||
|
))
|
||||||
|
.authentication(vec![Mechanism::Plain])
|
||||||
|
.build();
|
||||||
|
|
||||||
|
match sender.send(&message) {
|
||||||
|
Ok(res) => {
|
||||||
|
let res: String = res.message().collect();
|
||||||
|
info!(
|
||||||
|
"Successfully sent email to server with this response: {:?}",
|
||||||
|
res
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Err(e) => error!("There was an error sending the email: {e}"),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
98
src/main.rs
98
src/main.rs
|
@ -1,37 +1,105 @@
|
||||||
mod api;
|
mod api;
|
||||||
|
pub mod email;
|
||||||
|
|
||||||
|
use actix_files::Files;
|
||||||
use actix_multipart::form::tempfile::TempFileConfig;
|
use actix_multipart::form::tempfile::TempFileConfig;
|
||||||
use actix_web::{middleware, App, HttpServer};
|
use actix_web::body::MessageBody;
|
||||||
|
use actix_web::dev::{ServiceRequest, ServiceResponse};
|
||||||
|
use actix_web::{web, App, Error, HttpServer};
|
||||||
use api::camp_form::camp_form;
|
use api::camp_form::camp_form;
|
||||||
use api::church_form::church_form;
|
use api::contact::{self, contact_form};
|
||||||
use api::health_form::health_form;
|
use api::health_form::health_form;
|
||||||
use api::local_trip_form::local_form;
|
use api::local_trip_form::local_form;
|
||||||
use api::mt_form::mt_form;
|
use api::mt_form::mt_form;
|
||||||
use api::parent_form::parent_form;
|
use api::{
|
||||||
use api::teacher_form::teacher_form;
|
mt_church_form::mt_church_form, mt_parent_form::mt_parent_form,
|
||||||
|
mt_teacher_form::mt_teacher_form,
|
||||||
|
};
|
||||||
|
use color_eyre::eyre::Context;
|
||||||
|
use color_eyre::Result;
|
||||||
|
use sqlx::{Connection, SqliteConnection};
|
||||||
|
use tracing::level_filters::LevelFilter;
|
||||||
|
use tracing::{info, Span};
|
||||||
|
use tracing_actix_web::{RootSpanBuilder, TracingLogger};
|
||||||
|
use tracing_appender::rolling::{RollingFileAppender, Rotation};
|
||||||
|
use tracing_subscriber::{layer::SubscriberExt, EnvFilter, Layer};
|
||||||
|
|
||||||
|
pub struct DomainRootSpanBuilder;
|
||||||
|
|
||||||
|
impl RootSpanBuilder for DomainRootSpanBuilder {
|
||||||
|
fn on_request_start(request: &ServiceRequest) -> Span {
|
||||||
|
let method = request.method();
|
||||||
|
let info = request.connection_info();
|
||||||
|
let ip = info.realip_remote_addr().expect("hi");
|
||||||
|
let location = request.path();
|
||||||
|
info!(?method, ip, location);
|
||||||
|
tracing_actix_web::root_span!(request)
|
||||||
|
|
||||||
|
// let client_id: &str = todo!("Somehow extract it from the authorization header");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_request_end<B: MessageBody>(_span: Span, _outcome: &Result<ServiceResponse<B>, Error>) {}
|
||||||
|
}
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
let timer =
|
||||||
|
tracing_subscriber::fmt::time::ChronoLocal::new("%Y-%m-%d_%I:%M:%S%.6f %P".to_owned());
|
||||||
|
let logfile = RollingFileAppender::builder()
|
||||||
|
.rotation(Rotation::DAILY)
|
||||||
|
.filename_prefix("api")
|
||||||
|
.filename_suffix("log")
|
||||||
|
.build("/tmp/tfcsite")
|
||||||
|
.expect("Shouldn't");
|
||||||
|
|
||||||
log::info!("creating temporary upload directory");
|
let filter = EnvFilter::builder()
|
||||||
std::fs::create_dir_all("./tmp")?;
|
.with_default_directive(LevelFilter::WARN.into())
|
||||||
|
.parse_lossy("tfcapi=debug");
|
||||||
|
let logfile_layer = tracing_subscriber::fmt::layer()
|
||||||
|
.with_writer(logfile)
|
||||||
|
.with_line_number(true)
|
||||||
|
.with_level(true)
|
||||||
|
.with_target(true)
|
||||||
|
.with_ansi(false)
|
||||||
|
.with_timer(timer.clone());
|
||||||
|
let stdout_layer = tracing_subscriber::fmt::layer()
|
||||||
|
.pretty()
|
||||||
|
.with_line_number(true)
|
||||||
|
.with_target(true)
|
||||||
|
.with_timer(timer)
|
||||||
|
.with_filter(filter);
|
||||||
|
let filter = EnvFilter::builder()
|
||||||
|
.with_default_directive(LevelFilter::WARN.into())
|
||||||
|
.parse_lossy("tfcapi=debug");
|
||||||
|
let subscriber = tracing_subscriber::registry()
|
||||||
|
.with(logfile_layer.with_filter(filter).and_then(stdout_layer));
|
||||||
|
let _ = tracing::subscriber::set_global_default(subscriber).wrap_err("Tracing broked");
|
||||||
|
|
||||||
log::info!("starting HTTP server at http://localhost:4242");
|
std::fs::create_dir_all("/tmp/tfcsite")?;
|
||||||
|
|
||||||
HttpServer::new(|| {
|
info!("starting HTTP server at http://localhost:4242");
|
||||||
|
|
||||||
|
let conn = SqliteConnection::connect("sqlite://./data.db")
|
||||||
|
.await
|
||||||
|
.expect("Couldn't connect sqlite db");
|
||||||
|
let data = web::Data::new(conn);
|
||||||
|
|
||||||
|
HttpServer::new(move || {
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(middleware::Logger::default())
|
.app_data(data.clone())
|
||||||
.app_data(TempFileConfig::default().directory("./tmp"))
|
.wrap(TracingLogger::<DomainRootSpanBuilder>::new())
|
||||||
|
.app_data(TempFileConfig::default().directory("/tmp/tfcsite"))
|
||||||
.service(mt_form)
|
.service(mt_form)
|
||||||
.service(health_form)
|
.service(health_form)
|
||||||
.service(parent_form)
|
.service(mt_parent_form)
|
||||||
.service(teacher_form)
|
.service(mt_teacher_form)
|
||||||
.service(church_form)
|
.service(mt_church_form)
|
||||||
.service(local_form)
|
.service(local_form)
|
||||||
.service(camp_form)
|
.service(camp_form)
|
||||||
|
.service(contact_form)
|
||||||
|
.service(Files::new("/", "./public").index_file("index.html"))
|
||||||
})
|
})
|
||||||
.bind(("127.0.0.1", 4242))?
|
.bind(("localhost", 4242))?
|
||||||
.workers(2)
|
.workers(2)
|
||||||
.run()
|
.run()
|
||||||
.await
|
.await
|
||||||
|
|
2
src/testing.json
Normal file
2
src/testing.json
Normal file
File diff suppressed because one or more lines are too long
|
@ -998,6 +998,10 @@
|
||||||
--tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 8px 10px -6px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
--tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 8px 10px -6px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
||||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||||
}
|
}
|
||||||
|
.ring {
|
||||||
|
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
||||||
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||||
|
}
|
||||||
.ring-1 {
|
.ring-1 {
|
||||||
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
||||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||||
|
@ -1016,6 +1020,9 @@
|
||||||
--tw-blur: blur(8px);
|
--tw-blur: blur(8px);
|
||||||
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
||||||
}
|
}
|
||||||
|
.filter {
|
||||||
|
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
||||||
|
}
|
||||||
.transition {
|
.transition {
|
||||||
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter;
|
transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter;
|
||||||
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
|
||||||
|
|
|
@ -4,35 +4,35 @@
|
||||||
|
|
||||||
<div class="m-4 w-full grid grid-cols-1 gap-12 lg:grid-cols-4 md:grid-cols-2 sm:grid-cols-1">
|
<div class="m-4 w-full grid grid-cols-1 gap-12 lg:grid-cols-4 md:grid-cols-2 sm:grid-cols-1">
|
||||||
<div class="p-2">
|
<div class="p-2">
|
||||||
<div class="text-xl font-extrabold">Lost and Seeking</div>
|
<div class="text-2xl font-extrabold">Lost and Seeking</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li>The World</li>
|
<li class="py-2">The World</li>
|
||||||
<li>Outreach</li>
|
<li class="py-2">Outreach</li>
|
||||||
<li>Relationship Building</li>
|
<li class="py-2">Relationship Building</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-2">
|
<div class="p-2">
|
||||||
<div class="text-xl font-extrabold">Come and Grow</div>
|
<div class="text-2xl font-extrabold">Come and Grow</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Growing Believers</li>
|
<li class="py-2">Growing Believers</li>
|
||||||
<li>Building Faith</li>
|
<li class="py-2">Building Faith</li>
|
||||||
<li>Jesus Started Here</li>
|
<li class="py-2">Jesus Started Here</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-2">
|
<div class="p-2">
|
||||||
<div class="text-xl font-extrabold">Come and Follow</div>
|
<div class="text-2xl font-extrabold">Come and Follow</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Workers</li>
|
<li class="py-2">Workers</li>
|
||||||
<li>Equipping</li>
|
<li class="py-2">Equipping</li>
|
||||||
<li>Jesus Invited Some</li>
|
<li class="py-2">Jesus Invited Some</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="p-2">
|
<div class="p-2">
|
||||||
<div class="text-xl font-extrabold">Come and Go</div>
|
<div class="text-2xl font-extrabold">Come and Go</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Leadership</li>
|
<li class="py-2">Leadership</li>
|
||||||
<li>Multiplication</li>
|
<li class=py-2>Multiplication</li>
|
||||||
<li>Jesus Sent Some</li>
|
<li class=py-2>Jesus Sent Some</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue