make thing do

This commit is contained in:
Ryan Heywood 2024-09-04 15:13:05 -04:00
parent 2856ebbd18
commit 31d701ac72
Signed by: ryan
GPG Key ID: 8E401478A3FBEF72
4 changed files with 1381 additions and 25 deletions

3
.dockerignore Normal file
View File

@ -0,0 +1,3 @@
Dockerfile
.dockerignore
target

1244
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

55
Dockerfile Normal file
View File

@ -0,0 +1,55 @@
FROM stagex/busybox:sx2024.05.0@sha256:8cb9360041cd17e8df33c5cbc6c223875045c0c249254367ed7e0eb445720757 AS busybox
FROM stagex/binutils:sx2024.05.0@sha256:823ad20a58696435f4afd61aadbe7d9e18afde676a94b59a932126fc16ba0761 AS binutils
FROM stagex/musl:sx2024.05.0@sha256:f888fcf45fabaaae3d0268bcec902ceb94edba7bf8d09ef6966ebb20e00b7127 AS musl
FROM stagex/rust:sx2024.05.0@sha256:b982614e41a163f0b4222a7472030e30d466a8a605a1ff41e9731e9921e90f0e AS rust
FROM stagex/gcc:sx2024.05.0@sha256:ce77c0d1576d3c9d7905edd438fc58d2c51c0340a996c09a93c4602534dc2e26 AS gcc
FROM stagex/llvm:sx2024.05.0@sha256:c43abe585e2867d2ef2fad0902f2a264a3c4a429f95dddd00320de59dcf66210 AS llvm
FROM stagex/libunwind:sx2024.05.0@sha256:18d3b25f523d83aec9db229528d83068a5e289cc6dd27c85ab6ed0f0a55bc9a9 AS libunwind
FROM stagex/openssl:sx2024.05.0@sha256:9bd55ed05263a538e6a23c0262edc356c998a24674f3b8ad008a4b117a4cdf3b AS openssl
FROM stagex/zlib:sx2024.05.0@sha256:d0d6eef463a410191e086448c710441109ae72693cb074fe2b795ee033aa6c9d AS zlib
FROM stagex/ca-certificates:sx2024.05.0@sha256:76b232139c838fad3cdc693f839384141c2a5bf6e7f390294a133be1392a9b7a AS ca-certificates
FROM stagex/pkgconf:sx2024.05.0@sha256:2f5128c181a137c259d5304b77d213ca0d9fc85452b034027b47c2431044c5a6 AS pkgconf
FROM scratch AS base
FROM base AS fetch
COPY --from=busybox . /
COPY --from=musl . /
COPY --from=binutils . /
COPY --from=rust . /
COPY --from=gcc . /
COPY --from=llvm . /
COPY --from=libunwind . /
COPY --from=openssl . /
COPY --from=zlib . /
# NOTE: Necessary for `cargo fetch`, but CA trust is not relied upon
COPY --from=ca-certificates . /
COPY --from=pkgconf . /
COPY . /web-form
WORKDIR /web-form
RUN cargo fetch
FROM fetch AS build
ENV RUSTFLAGS='-C codegen-units=1'
RUN --network=none cargo build --frozen --release
FROM build AS install
COPY --from=gcc . /rootfs
COPY --from=musl . /rootfs
COPY --from=libunwind . /rootfs
COPY --from=openssl . /rootfs
RUN --network=none cp /web-form/target/release/website-form /rootfs/usr/bin/website-form
FROM stagex/filesystem:sx2024.05.0@sha256:c504b17edae1bea8c139d058b20bebd383a0be1b4f57565d92cb578012f9c0f8 AS package
COPY --from=install /rootfs /
ENTRYPOINT ["/usr/bin/website-form"]

View File

@ -2,7 +2,7 @@ use axum::{
http::header::SET_COOKIE, http::header::SET_COOKIE,
response::{AppendHeaders, IntoResponse, Redirect}, response::{AppendHeaders, IntoResponse, Redirect},
routing::post, routing::post,
Form, Router, Extension, Form, Router,
}; };
use lettre::message::Mailbox; use lettre::message::Mailbox;
@ -11,59 +11,113 @@ use lettre::{AsyncTransport, Message, Tokio1Executor};
use serde::Deserialize; use serde::Deserialize;
use std::error::Error; use std::error::Error;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::Arc;
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all = "kebab-case")]
struct FormData { struct FormData {
name: String, name: Option<String>,
email: String, email: String,
message: String, company_name: Option<String>,
service: String,
help: String,
hear_about: String,
other_source: Option<String>,
}
struct State {
username: String,
password: String,
} }
#[tokio::main] #[tokio::main]
async fn main() { async fn main() -> Result<(), Box<dyn std::error::Error>> {
let app = Router::new().route("/submit-email", post(handle_form)); let username = std::env::var("WEBMAIL_USERNAME")?;
let password = std::env::var("WEBMAIL_PASSWORD")?;
let state = State { username, password };
let app = Router::new()
.route("/submit-email", post(handle_form))
.layer(Extension(Arc::new(state)));
let addr = SocketAddr::from(([0, 0, 0, 0], 4200)); let addr = SocketAddr::from(([0, 0, 0, 0], 4200));
println!("Listening on {}", addr); println!("Listening on {}", addr);
let listener = tokio::net::TcpListener::bind("0.0.0.0:4200").await.unwrap(); let listener = tokio::net::TcpListener::bind("0.0.0.0:4200").await?;
axum::serve(listener, app).await.unwrap(); axum::serve(listener, app).await?;
Ok(())
} }
async fn handle_form(Form(form_data): Form<FormData>) -> impl IntoResponse { async fn handle_form(
match send_email(form_data).await { state: Extension<Arc<State>>,
Form(form_data): Form<FormData>,
) -> impl IntoResponse {
match send_email(form_data, state.as_ref()).await {
Ok(_) => ( Ok(_) => (
AppendHeaders([(SET_COOKIE, "is=ok")]), AppendHeaders([(SET_COOKIE, "is=ok")]),
Redirect::to("/contact.html"), Redirect::to("/contact.html"),
), ),
Err(_) => ( Err(e) => {
eprintln!("Encountered error sending mail: {e:?}");
(
AppendHeaders([(SET_COOKIE, "is=err")]), AppendHeaders([(SET_COOKIE, "is=err")]),
Redirect::to("/contact.html"), Redirect::to("/contact.html"),
), )
}
} }
} }
async fn send_email(form_data: FormData) -> Result<(), Box<dyn Error>> { async fn send_email(form_data: FormData, state: &State) -> Result<(), Box<dyn Error>> {
let username = &state.username;
let password = &state.password;
let email = form_data.email.parse::<Mailbox>()?;
let content = [
(
"Name?",
form_data.name.unwrap_or("Not provided".to_string()),
),
("Email", form_data.email),
(
"Company Name?",
form_data.company_name.unwrap_or("Not provided".to_string()),
),
("Service", form_data.service),
("How can we help?", form_data.help),
("How did you hear about us?", form_data.hear_about),
(
"Other",
form_data
.other_source
.unwrap_or("Not provided/relevant".to_string()),
),
];
let body = content.map(|(a, b)| format!("{a}: {b}")).join("\n");
println!("Body: {body}");
let email = Message::builder() let email = Message::builder()
.from("webmail@distrust.co".parse()?) .from(username.parse()?)
.reply_to(form_data.email.parse::<Mailbox>()?) .reply_to(email)
.to("sales@distrust.co".parse()?) .to("ryan@distrust.co".parse()?)
.cc("anton@distrust.co".parse()?)
.subject("New Website Form Inquiry") .subject("New Website Form Inquiry")
.body(format!( .body(body)?;
"Name: {}/nEmail: {}/nMessage: {}",
form_data.name, form_data.email, form_data.message
))?;
let credentials = lettre::transport::smtp::authentication::Credentials::new( let credentials = lettre::transport::smtp::authentication::Credentials::new(
std::env::var("WEBMAIL_USERNAME")?, username.clone(),
std::env::var("WEBMAIL_PASSWORD")?, password.clone(),
); );
let mailer = AsyncSmtpTransport::<Tokio1Executor>::relay("smtp.distrust.co")? let mailer = AsyncSmtpTransport::<Tokio1Executor>::relay("smtp.migadu.com")?
.credentials(credentials) .credentials(credentials)
.build(); .build();
mailer.send(email).await?; let response = mailer.send(email).await?;
dbg!(&response);
Ok(()) Ok(())
} }