diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..718f731 --- /dev/null +++ b/.env.example @@ -0,0 +1,29 @@ +# Phoenix / App +PHX_HOST=mixer.example.com +PHX_SERVER=true +PORT=4000 +SECRET_KEY_BASE=REPLACE_WITH_64_CHAR_SECRET # generate with: mix phx.gen.secret + +# Database +DATABASE_URL=ecto://USER:PASSWORD@HOST/DATABASE +ECTO_IPV6=false +POOL_SIZE=10 + +# Clustering (leave blank if not using DNS-based clustering) +DNS_CLUSTER_QUERY= + +# Auth +TOKEN_SIGNING_SECRET=REPLACE_WITH_SECRET + +# S3 / Object Storage +S3_ACCESS_KEY_ID=your-access-key-id +S3_SECRET_ACCESS_KEY=your-secret-access-key +S3_HOST=s3.amazonaws.com +S3_BUCKET=your-bucket-name +S3_ASSET_HOST=https://your-bucket.s3.amazonaws.com +S3_SCHEME=https:// +S3_PORT=80 +S3_VIRTUAL_HOST=false + +# Email (Brevo) +BREVO_API_KEY=your-brevo-api-key diff --git a/.gitignore b/.gitignore index 8cfb4ae..9958181 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# .env +.env + # The directory Mix will write compiled artifacts to. /_build/ diff --git a/config/prod.exs b/config/prod.exs index 6c169b2..e2e9acf 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -21,7 +21,7 @@ config :mixer, MixerWeb.Endpoint, ] # Configure Swoosh API Client -config :swoosh, api_client: Swoosh.ApiClient.Req +config :swoosh, api_client: Swoosh.ApiClient.Hackney # Disable Swoosh Local Memory Storage config :swoosh, local: false diff --git a/config/runtime.exs b/config/runtime.exs index 64f13a8..05cd20d 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -52,7 +52,7 @@ if config_env() == :prod do You can generate one by calling: mix phx.gen.secret """ - host = System.get_env("PHX_HOST") || "example.com" + host = System.get_env("PHX_HOST") || "mixer.jimweaver.com" config :mixer, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY") @@ -97,6 +97,12 @@ if config_env() == :prod do System.get_env("S3_ASSET_HOST") || raise("Missing environment variable `S3_ASSET_HOST`!") + config :mixer, Mixer.Mailer, + adapter: Swoosh.Adapters.Brevo, + api_key: + System.get_env("BREVO_API_KEY") || + raise("Missing environment variable `BREVO_API_KEY`!") + # ## SSL Support # # To get SSL working, you will need to add the `https` key diff --git a/lib/mixer/accounts/user/senders/send_magic_link_email.ex b/lib/mixer/accounts/user/senders/send_magic_link_email.ex index bb2c090..1eec038 100644 --- a/lib/mixer/accounts/user/senders/send_magic_link_email.ex +++ b/lib/mixer/accounts/user/senders/send_magic_link_email.ex @@ -21,8 +21,7 @@ defmodule Mixer.Accounts.User.Senders.SendMagicLinkEmail do end new() - # TODO: Replace with your email - |> from({"noreply", "noreply@example.com"}) + |> from({"noreply", "noreply@jimweaver.com"}) |> to(to_string(email)) |> subject("Your login link") |> html_body(body(token: token, email: email)) @@ -31,10 +30,79 @@ defmodule Mixer.Accounts.User.Senders.SendMagicLinkEmail do defp body(params) do # NOTE: You may have to change this to match your magic link acceptance URL. + link = url(~p"/magic_link/#{params[:token]}") + email_template("Your magic link", "Hello, #{params[:email]}!", """ +

+ Use the button below to sign in to Mixer. This link is valid for a short time and can only be used once. +

+

+ If you didn't request this, you can safely ignore this email. +

+ """, link, "Sign In to Mixer") + end + defp email_template(title, greeting, content, button_url, button_label) do """ -

Hello, #{params[:email]}! Click this link to sign in:

-

#{url(~p"/magic_link/#{params[:token]}")}

+ + + + + + #{title} + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+
Mixer
+
Your social feed
+
 
+

#{greeting}

+ #{content} + + + + + +
+ #{button_label} +
+
+

+ This is an automated message — replies to this address are not monitored. +

+

+ You received this because you have an account on Mixer. +

+
+
+ + """ end end diff --git a/lib/mixer/accounts/user/senders/send_new_user_confirmation_email.ex b/lib/mixer/accounts/user/senders/send_new_user_confirmation_email.ex index e56903c..128e367 100644 --- a/lib/mixer/accounts/user/senders/send_new_user_confirmation_email.ex +++ b/lib/mixer/accounts/user/senders/send_new_user_confirmation_email.ex @@ -13,8 +13,7 @@ defmodule Mixer.Accounts.User.Senders.SendNewUserConfirmationEmail do @impl true def send(user, token, _) do new() - # TODO: Replace with your email - |> from({"noreply", "noreply@example.com"}) + |> from({"noreply", "noreply@jimweaver.com"}) |> to(to_string(user.email)) |> subject("Confirm your email address") |> html_body(body(token: token)) @@ -22,11 +21,79 @@ defmodule Mixer.Accounts.User.Senders.SendNewUserConfirmationEmail do end defp body(params) do - url = url(~p"/confirm_new_user/#{params[:token]}") + link = url(~p"/confirm_new_user/#{params[:token]}") + email_template("Confirm your email", "Welcome to Mixer!", """ +

+ Thanks for signing up. Just one more step — confirm your email address to activate your account. +

+

+ If you didn't create an account on Mixer, you can safely ignore this email. +

+ """, link, "Confirm Email Address") + end + defp email_template(title, greeting, content, button_url, button_label) do """ -

Click this link to confirm your email:

-

#{url}

+ + + + + + #{title} + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+
Mixer
+
Your social feed
+
 
+

#{greeting}

+ #{content} + + + + + +
+ #{button_label} +
+
+

+ This is an automated message — replies to this address are not monitored. +

+

+ You received this because you signed up for Mixer. +

+
+
+ + """ end end diff --git a/lib/mixer/accounts/user/senders/send_password_reset_email.ex b/lib/mixer/accounts/user/senders/send_password_reset_email.ex index cb43eac..3d94c56 100644 --- a/lib/mixer/accounts/user/senders/send_password_reset_email.ex +++ b/lib/mixer/accounts/user/senders/send_password_reset_email.ex @@ -13,8 +13,7 @@ defmodule Mixer.Accounts.User.Senders.SendPasswordResetEmail do @impl true def send(user, token, _) do new() - # TODO: Replace with your email - |> from({"noreply", "noreply@example.com"}) + |> from({"noreply", "noreply@jimweaver.com"}) |> to(to_string(user.email)) |> subject("Reset your password") |> html_body(body(token: token)) @@ -22,11 +21,79 @@ defmodule Mixer.Accounts.User.Senders.SendPasswordResetEmail do end defp body(params) do - url = url(~p"/password-reset/#{params[:token]}") + link = url(~p"/password-reset/#{params[:token]}") + email_template("Reset your password", "Password reset request", """ +

+ We received a request to reset the password for your Mixer account. Click the button below to choose a new one. +

+

+ If you didn't request a password reset, you can safely ignore this email — your password will not change. +

+ """, link, "Reset My Password") + end + defp email_template(title, greeting, content, button_url, button_label) do """ -

Click this link to reset your password:

-

#{url}

+ + + + + + #{title} + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+
Mixer
+
Your social feed
+
 
+

#{greeting}

+ #{content} + + + + + +
+ #{button_label} +
+
+

+ This is an automated message — replies to this address are not monitored. +

+

+ You received this because a password reset was requested for your Mixer account. +

+
+
+ + """ end end