use std::{collections::HashMap, fs}; use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm}; use actix_web::{post, HttpResponse}; use lettre::{ message::{header::ContentType, Attachment, MultiPart, SinglePart}, transport::smtp::authentication::{Credentials, Mechanism}, Message, SmtpTransport, Transport, }; use maud::html; use reqwest::{Client, Error}; #[derive(Debug, MultipartForm, Default)] struct HealthForm { #[multipart(rename = "first-name")] first_name: Option>, #[multipart(rename = "last-name")] last_name: Option>, #[multipart(rename = "parent-first-name")] parent_first_name: Option>, #[multipart(rename = "parent-last-name")] parent_last_name: Option>, #[multipart(rename = "birth-date")] birthdate: Option>, street: Option>, city: Option>, state: Option>, zip: Option>, #[multipart(rename = "cell-phone")] parent_cellphone: Option>, #[multipart(rename = "home-phone")] homephone: Option>, #[multipart(rename = "additional-emergency-contact")] contact: Option>, #[multipart(rename = "addtional-emergency-contact-phone")] contact_phone: Option>, #[multipart(rename = "doctor-name")] doctorname: Option>, #[multipart(rename = "doctor-city")] doctorcity: Option>, #[multipart(rename = "doctor-phone")] doctorphone: Option>, #[multipart(rename = "medical-coverage")] medical: Option>, #[multipart(rename = "insurance-name")] insurance: Option>, #[multipart(rename = "policy-number")] policy_number: Option>, allergies: Option>, #[multipart(rename = "allergies-other")] allergies_other: Option>, #[multipart(rename = "specific-allergies")] specific_allergies: Option>, #[multipart(rename = "allergic-treatment")] treatment: Option>, conditions: Option>, #[multipart(rename = "tetanus-shot")] tetanus: Option>, #[multipart(rename = "swimming-ability")] swimming: Option>, #[multipart(rename = "medication-schedule")] medication: Option>, #[multipart(rename = "other-notes")] notes: Option>, agreement: Option>, #[multipart(rename = "image")] file: Option, registration: Option>, } #[post("/health-form")] pub async fn health_form(MultipartForm(form): MultipartForm) -> HttpResponse { let first = form .first_name .as_ref() .unwrap_or(&Text(String::from(""))) .0 .clone(); let last = form .last_name .as_ref() .unwrap_or(&Text(String::from(""))) .0 .clone(); let full_name = format!("{} {}", first, last); let registration = form .registration .as_ref() .unwrap_or(&Text(String::from(""))) .0 .clone(); let email_subject = format!("{} {} filled out a health form!", first, last); let filename_noext = format!("{}_{}", first, last); let parent = format!( "{} {}", form.parent_first_name .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 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(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 = html! { html { head { title { (first) " " (last) " filled out a health 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 { "Health form for " (first) " " (last) "!" } hr; table { tr { th { "Name" } td { (first) " " (last) } } tr { th { "Parent" } td { (parent) } } tr { th { "Birthdate" } td { (birthdate) } } tr { th { "Street" } td { (street) } } tr { th { "City" } td { (city) } } tr { th { "State" } td { (state) } } tr { th { "Zip" } td { (zip) } } tr { th { "Phone" } td { (parent_cellphone) } } tr { th { "Home Phone" } td { (homephone) } } tr { th { "Additional Emergency Contact" } td { (contact) } } tr { th { "Contact Phone" } td { (contact_phone) } } tr { th { "Doctor" } td { (doctorname) } } tr { th { "Doctor City" } td { (doctorcity) } } tr { th { "Doctor Phone" } td { (doctorphone) } } tr { th { "Medical Coverage" } td { (medical) } } tr { th { "Insurance Provider" } td { (insurance) } } tr { th { "Policy Number" } td { (policy_number) } } tr { th { "Allergies" } td { (allergies) } } tr { th { "Other Allergies" } td { (allergies_other) } } tr { th { "Specific Allergies" } td { (specific_allergies) } } tr { th { "Treatments" } td { (treatment) } } tr { th { "Physical or mental conditions" } td { (conditions) } } tr { th { "Last tetanus shot" } td { (tetanus) } } tr { th { "Swimming Ability" } td { (swimming) } } tr { th { "Medication Schedule" } td { (medication) } } tr { th { "Other Relevant Info" } td { (notes) } } tr { th { "Final Agreement" } td { (agreement) } } } } } }; let mut path: Option = Some(String::from("")); let mut file_exists = false; let mut filename = String::from(""); log::info!("Does the file exist? {:?}", file_exists); match store_health_form(&form).await { Ok(_) => log::info!("Successfully posted the health form in nextcloud tables"), Err(e) => log::error!("Error posting health form to nextcloud tables: {:?}", e), } 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!( "Hey! We got a file! {:?}", f.metadata().expect("Something bad happened mate") ); if f.metadata().unwrap().len() > 0 { file_exists = true; } } Err(e) => log::info!("{:?}: Probably a missing image", 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!("The attachment is: {:?}", attachment.headers()); MultiPart::mixed() .singlepart(SinglePart::html(email.into_string())) .singlepart(attachment) } else { MultiPart::alternative_plain_html(String::from("Testing"), email.into_string()) }; // log::info!("{:?}", multi); if let Ok(m) = Message::builder() .from( "TFC ADMIN " .parse() .unwrap(), ) .to("Chris Cochrun ".parse().unwrap()) .to("Ethan Rose ".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"); } match registration.as_str() { "now" => { log::info!("Sending them to pay for registration now"); HttpResponse::Ok() .insert_header(("Access-Control-Expose-Headers", "*")) .insert_header(( "HX-Redirect", "https://secure.myvanco.com/L-Z772/campaign/C-13JPJ", )) .finish() } "full" => { log::info!("Sending them to pay for the full registration now"); HttpResponse::Ok() .insert_header(("Access-Control-Expose-Headers", "*")) .insert_header(( "HX-Redirect", "https://secure.myvanco.com/L-Z772/campaign/C-13JQE", )) .finish() } "later" => { log::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." } } }; HttpResponse::Ok().body(html.into_string()) } _ => { 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("hi") } async fn store_health_form(form: &HealthForm) -> Result<(), Error> { let request = Client::new(); let mut map = HashMap::new(); map.insert( 37, format!( "{} {}", form.first_name .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), form.last_name .as_ref() .unwrap_or(&Text(String::new())) .0 .clone() ), ); map.insert( 38, format!( "{} {}", form.parent_first_name .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), form.parent_last_name .as_ref() .unwrap_or(&Text(String::new())) .0 .clone() ), ); map.insert( 39, form.birthdate .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 40, form.street .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 41, form.city.as_ref().unwrap_or(&Text(String::new())).0.clone(), ); map.insert( 42, form.state .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 43, form.zip.as_ref().unwrap_or(&Text(String::new())).0.clone(), ); map.insert( 44, form.parent_cellphone .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 45, form.homephone .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 46, format!( "{} {}", form.contact .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), form.contact_phone .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ), ); map.insert( 47, form.doctorname .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 48, form.doctorcity .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 49, form.doctorphone .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 50, form.medical .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 51, form.insurance .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 52, form.policy_number .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 54, form.agreement .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 55, format!( "{}{}", form.allergies .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), form.allergies_other .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ), ); map.insert( 56, form.specific_allergies .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 57, form.treatment .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 58, form.conditions .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 59, form.tetanus .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 62, form.swimming .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 60, form.medication .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); map.insert( 61, form.notes .as_ref() .unwrap_or(&Text(String::new())) .0 .clone(), ); let mut json = HashMap::new(); json.insert("data", map); request .post("https://staff.tfcconnection.org/apps/tables/api/1/tables/4/rows") .basic_auth("chris", Some("2VHeGxeC^Zf9KqFK^G@Pt!zu2q^6@b")) .json(&json) .send() .await?; Ok(()) }