Skip to content

Commit

Permalink
validators, form_process rewrite, form_config
Browse files Browse the repository at this point in the history
  • Loading branch information
shorekeeper committed Jun 4, 2023
1 parent 47d7fcb commit 1619570
Show file tree
Hide file tree
Showing 10 changed files with 310 additions and 61 deletions.
8 changes: 5 additions & 3 deletions .env
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
SERVER_IP=127.0.0.1:8080
SMTP_USER=your_smtp_user
# !! LOCALHOST: 127.0.0.1:8080
# !! IP SHOULD BE DECLARED IN FORMAT IP:PORT
SERVER_IP=your_ip
SMTP_PASS=your_smtp_pass
SMTP_HOST=your_smtp_host
DATABASE_URL=postgres://user:password@host/database
DATABASE_URL=postgres://user:password@host/database
SMTP_USER=your_smtp_user
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ futures = "0.3.28"
futures-util = "0.3.28"
lettre = "0.10.4"
lettre_email = "0.9.4"
regex = "1.8.3"
serde_json = "1.0.96"
tera = "1.11.0"

Expand Down
18 changes: 10 additions & 8 deletions src/check_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,28 @@ use crate::log::info;
use crate::time::current_time;

const DEFAULT_CONFIG: &str =
r#"SERVER_IP=your_ip
// !! LOCALHOST: 127.0.0.1:8080
// !! IP SHOULD BE DECLARED IN FORMAT IP:PORT
r#"# !! LOCALHOST: 127.0.0.1:8080
# !! IP SHOULD BE DECLARED IN FORMAT IP:PORT
SERVER_IP=your_ip
SMTP_USER=your_smtp_user
SMTP_PASS=your_smtp_pass
SMTP_HOST=your_smtp_host
DATABASE_URL=postgres://user:password@host/database"#;

pub fn check_config() {
pub fn check_config() -> Result<(), std::io::Error> {
let now = current_time();
let env_path = Path::new(".env");
match env_path.exists() {
false => {
// create .env file with default values if it does not exist
fs::write(env_path, DEFAULT_CONFIG).expect("Failed to create .env file");
fs::write(env_path, DEFAULT_CONFIG)?;
log_info!(&now, "Created .env file. Please configure it before running the program again.");
thread::sleep(Duration::from_secs(10));
std::process::exit(0);
}
true => {
// check for missing values and add them to the file if necessary
let mut config = fs::read_to_string(env_path).expect("Failed to read .env file");
let mut config = fs::read_to_string(env_path)?;
let mut updated = false;
for line in DEFAULT_CONFIG.lines() {
let key = line.split('=').next().unwrap();
Expand All @@ -44,7 +44,7 @@ pub fn check_config() {
}
match updated {
true => {
fs::write(env_path, config).expect("Failed to update .env file");
fs::write(env_path, config)?;
log_info!(&now, "\x1B[1m\x1b[32mUpdated .env file with missing values. Please configure them before running the program again.\x1B[0m");
thread::sleep(Duration::from_secs(10));
std::process::exit(0);
Expand All @@ -53,4 +53,6 @@ pub fn check_config() {
}
}
}
}

Ok(())
}
22 changes: 22 additions & 0 deletions src/email_validator/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use regex::Regex;

pub trait EmailValidator {
fn is_valid(&self, email: &str) -> bool;
}

pub struct EmailRegexValidator {
regex: Regex,
}

impl EmailRegexValidator {
pub fn new() -> Self {
let regex = Regex::new(r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").unwrap();
Self { regex }
}
}

impl EmailValidator for EmailRegexValidator {
fn is_valid(&self, email: &str) -> bool {
self.regex.is_match(email)
}
}
113 changes: 113 additions & 0 deletions src/form_config/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use crate::email_validator::{EmailRegexValidator, EmailValidator};
use crate::input_validator::NonEmptyInputValidator;
use std::env;
use tera::Context;

pub trait FormConfig {
fn set_email(&mut self, email: String);
fn set_name(&mut self, name: String);
fn set_message_body(&mut self, message_body: String);
fn smtp_user(&self) -> String;
fn smtp_pass(&self) -> String;
fn smtp_host(&self) -> String;
fn input_validator(&self) -> &NonEmptyInputValidator;
fn message_printed(&mut self) -> &mut bool;
fn email(&self) -> String;
fn name(&self) -> String;
fn message_body(&self) -> String;
fn context(&mut self) -> &mut Context;
fn email_validator(&self) -> &dyn EmailValidator;
}

pub struct FormConfigImpl {
smtp_user: String,
smtp_pass: String,
smtp_host: String,
email_validator: EmailRegexValidator,
input_validator: NonEmptyInputValidator,
message_printed: bool,
email: String,
name: String,
message_body: String,
context: Context,
}

impl FormConfigImpl {
pub fn new() -> Self {
let smtp_user = env::var("SMTP_USER").expect("SMTP_USER must be set");
let smtp_pass = env::var("SMTP_PASS").expect("SMTP_PASS must be set");
let smtp_host = env::var("SMTP_HOST").expect("SMTP_HOST must be set");
let email_validator = EmailRegexValidator::new();
let input_validator = NonEmptyInputValidator;
let mut context = Context::new();
context.insert("name", "User");
context.insert("context", "Rust Form");

Self {
smtp_user,
smtp_pass,
smtp_host,
email_validator,
input_validator,
message_printed: false,
email: String::new(),
name: String::new(),
message_body: String::new(),
context,
}
}
}

impl FormConfig for FormConfigImpl {
fn smtp_user(&self) -> String {
self.smtp_user.clone()
}

fn smtp_pass(&self) -> String {
self.smtp_pass.clone()
}

fn smtp_host(&self) -> String {
self.smtp_host.clone()
}

fn email_validator(&self) -> &dyn EmailValidator {
&self.email_validator
}

fn input_validator(&self) -> &NonEmptyInputValidator {
&self.input_validator
}

fn message_printed(&mut self) -> &mut bool {
&mut self.message_printed
}

fn email(&self) -> String {
self.email.clone()
}

fn name(&self) -> String {
self.name.clone()
}

fn message_body(&self) -> String {
self.message_body.clone()
}

fn context(&mut self) -> &mut Context {
&mut self.context
}

fn set_email(&mut self, email: String) {
self.email = email;
}

fn set_name(&mut self, name: String) {
self.name = name;
}

fn set_message_body(&mut self, message_body: String) {
self.message_body = message_body;
}
}
90 changes: 43 additions & 47 deletions src/form_process.rs
Original file line number Diff line number Diff line change
@@ -1,81 +1,77 @@
use crate::input_validator::InputValidator;
use crate::form_config::{FormConfig, FormConfigImpl};
use crate::{log_info, log_warn, log_error};
use crate::log::{info, error, warn};
use crate::time::current_time;

use actix_web::{web, HttpResponse};
use lettre::{Message, SmtpTransport, Transport};
use lettre::transport::smtp::authentication::Credentials;
use tera::{Context, Tera};
use std::env;
use tera::{Tera};

#[allow(non_snake_case)]
pub async fn process_form(form: web::Form<std::collections::HashMap<String, String>>) -> HttpResponse {

let mut context = Context::new(); // create a new Tera context
context.insert("name", "User");
context.insert("context", "Rust Form");

// defining SMTP server credentials as static variables
let SMTP_USER = env::var("SMTP_USER").expect("SMTP_USER must be set"); // get the SMTP_USER from the .env file
let SMTP_PASS = env::var("SMTP_PASS").expect("SMTP_PASS must be set"); // get the SMTP_PASS from the .env file
let SMTP_HOST = env::var("SMTP_HOST").expect("SMTP_HOST must be set"); // get the SMTP_HOST from the .env file | WITHOUT SSL:// OR TLS://!!!
let now = current_time();
let mut config = FormConfigImpl::new();

let mut email = String::new();
let mut name = String::new();
let mut message_body = String::new();

for (key, value) in form.into_inner() { // iterate over the form data
match value.is_empty() {
true => {
context.insert("error", &format!("{} cannot be empty", key));
match (name.is_empty(), email.is_empty(), message_body.is_empty()) {
for (key, value) in form.into_inner() {
match config.input_validator().is_valid(&value) {
false => {
config.context().insert("error", &format!("{} cannot be empty", key));
match (config.name().is_empty(), config.email().is_empty(), config.message_body().is_empty()) {
(true, true, true) => {
context.insert("error", "smtp is not magic, type smth");
println!("User is bruh");
//if !*config.message_printed() {
config.context().insert("error", "smtp is not magic, type smth");
log_error!(&now, "User didn't entered anything for: {}", key);
// *config.message_printed() = true;
//}
},
_ => println!("User didn't entered {}", key),
_ => log_warn!(&now, "User didn't entered {}", key),
}
continue;
}
false => {
context.insert(key.as_str(), &value);
println!("User entered {} for {}", value, key);
true => {
config.context().insert(key.as_str(), &value);
log_info!(&now, "User entered {} for {}", value, key);
match key.as_str() {
"email" => email = value,
"name" => name = value,
"message" => message_body = value,
"email" => config.set_email(value),
"name" => config.set_name(value),
"message" => config.set_message_body(value),
_ => (),
}
}
}
}
// creating let with SMTP credentials
let credentials = Credentials::new(SMTP_USER.to_string(), SMTP_PASS.to_string());


// creating let for an SMTP transport
// i was trying to make relay(SMTP_SERVER) but it looks like that relay doesn't like this idk why
let mailer = SmtpTransport::relay(&SMTP_HOST)
let credentials = Credentials::new(config.smtp_user(), config.smtp_pass());
let mailer = SmtpTransport::relay(&config.smtp_host())
.unwrap()
.credentials(credentials)
.build();

match (email.is_empty() || !email.contains('@'), SMTP_USER.is_empty() || !SMTP_USER.contains('@')) { // validate the email address and SMTP username
(true, _) => context.insert("error", "Invalid email address"),
(_, true) => context.insert("error", "Invalid SMTP username"),
(false, false) => {
let message = Message::builder() // create an email message
.from(SMTP_USER.parse().unwrap())
.to(email.parse().unwrap())
match (config.email_validator().is_valid(&config.email()), config.email_validator().is_valid(&config.smtp_user())) {
(false, _) => config.context().insert("error", "Invalid email address"),
(_, false) => config.context().insert("error", "Invalid SMTP username"),
(true, true) => {
let message = Message::builder()
.from(config.smtp_user().parse().unwrap())
.to(config.email().parse().unwrap())
.subject("Form Submission")
.body(format!(
"Thank you for your submission, {}!\n\nYour message:\n{}",
name, message_body
config.name(), config.message_body()
))
.unwrap();

// send the email message
match mailer.send(&message) {
Ok(_) => println!("Email sended: {}", email),
Err(e) => eprintln!("Error sending email: {:?}", e),
Ok(_) => log_info!(&now, "Email sended: {}", config.email()),
Err(e) => log_error!(&now, "Error sending email: {:?}", e),
}
}
}
let body = Tera::one_off(include_str!("templates/form.tera"), &context, false).unwrap(); // render the template with the context
HttpResponse::Ok().body(body) // return the rendered template as the response body


let body = Tera::one_off(include_str!("templates/form.tera"), &config.context(), false).unwrap();
HttpResponse::Ok().body(body)
}
Loading

0 comments on commit 1619570

Please sign in to comment.