Skip to content
Snippets Groups Projects
Commit 4b38e84c authored by Cam Saul's avatar Cam Saul
Browse files

Use SMTP for sending email

parent 2e2855d4
No related branches found
No related tags found
No related merge requests found
......@@ -26,6 +26,7 @@
[clj-time "0.9.0"] ; library for dealing with date/time
[colorize "0.1.1" :exclusions [org.clojure/clojure]] ; string output with ANSI color codes (for logging)
[com.cemerick/friend "0.2.1"] ; auth library
[com.draines/postal "1.11.3"] ; SMTP library
[com.h2database/h2 "1.4.186"] ; embedded SQL database
[com.mattbertolini/liquibase-slf4j "1.2.1"]
[com.novemberain/monger "2.1.0"] ; MongoDB Driver
......
(ns metabase.email
(:require [clojure.data.json :as json]
[clojure.tools.logging :as log]
[clj-http.lite.client :as client]
[medley.core :as medley]
(:require [clojure.tools.logging :as log]
[postal.core :as postal]
[metabase.models.setting :refer [defsetting]]
[metabase.util :as u]))
(declare api-post-messages-send
format-recipients)
;; ## CONFIG
(defsetting mandrill-api-key "API key for Mandrill.")
(defsetting email-from-address "Email address used as the sender of system notifications." "notifications@metabase.com")
(defsetting email-from-name "Name used as the sender of the system notifications." "Metabase")
(defsetting email-smtp-host "SMTP host." "smtp.mandrillapp.com")
(defsetting email-smtp-username "SMTP username.")
(defsetting email-smtp-password "SMTP password.")
(defsetting email-smtp-port "SMTP port." "587")
;; ## PUBLIC INTERFACE
(defn send-message [subject recipients message-type message & {:as kwargs}]
;; TODO - wouldn't this be *nicer* if all args were kwargs?
(defn send-message
"Send an email to one or more RECIPIENTS.
RECIPIENTS is a sequence of email addresses; MESSAGE-TYPE must be either `:text` or `:html`.
(email/send-message
:subject \"[Metabase] Password Reset Request\"
:recipients [\"cam@metabase.com\"]
:message-type :text
:message \"How are you today?\")
Upon success, this returns the MESSAGE that was just sent."
[& {:keys [subject recipients message-type message]}]
{:pre [(string? subject)
(map? recipients)
(sequential? recipients)
(every? u/is-email? recipients)
(contains? #{:text :html} message-type)
(string? message)]}
(medley/mapply api-post-messages-send (merge {:subject subject
:to (format-recipients recipients)
message-type message}
kwargs)))
;; ## IMPLEMENTATION
(def ^:private api-prefix
"URL prefix for API calls to the Mandrill API."
"https://mandrillapp.com/api/1.0/")
(defn- api-post
"Make a `POST` call to the Mandrill API.
(api-post \"messages/send\" :body { ... })"
[endpoint & {:keys [body] :as request-map
:or {body {}}}]
{:pre [(string? endpoint)]}
(if-not (mandrill-api-key)
(log/warn "Cannot send email: no Mandrill API key!")
(let [defaults {:content-type :json
:accept :json}
body (-> body
(assoc :key (mandrill-api-key))
json/write-str)]
(client/post (str api-prefix endpoint ".json")
(merge defaults request-map {:body body})))))
(defn- api-post-messages-send
"Make a `POST messages/send` call to the Mandrill API."
[& {:as kwargs}]
(let [defaults {:from_email (email-from-address)
:from_name (email-from-name)}]
(= (:status (api-post "messages/send"
:body {:message (merge defaults kwargs)}))
200)))
(defn- format-recipients
"Format a map of email -> name in the format expected by the Mandrill API.
(format-recipients {\"cam@metabase.com\" \"Cam Saul\"})
-> {:email \"cam@metabase.com\"
:name \"Cam Saul\"
:type :to}"
[email->name]
(map (fn [[email name]]
{:pre [(u/is-email? email)
(string? name)]}
{:email email
:name name
:type :to})
email->name))
(try
;; Check to make sure all valid settings are set!
(when-not (email-smtp-username)
(throw (Exception. "SMTP username is not set.")))
(when-not (email-smtp-password)
(throw (Exception. "SMTP password is not set.")))
;; Now send the email
(let [{error :error error-message :message} (postal/send-message {:host (email-smtp-host)
:user (email-smtp-username)
:pass (email-smtp-password)
:port (Integer/parseInt (email-smtp-port))}
{:from (email-from-address)
:to recipients
:subject subject
:body (condp = message-type
:text message
:html [{:type "text/html; charset=utf-8"
:content message}])})]
(when-not (= error :SUCCESS)
(throw (Exception. (format "Emails failed to send: error: %s; message: %s" error error-message))))
message)
(catch Throwable e
(log/warn "Failed to send email: " (.getMessage e)))))
......@@ -21,11 +21,10 @@
[:p "Your account is setup and ready to go, you just need to set a password so you can login. Follow the link below to reset your account password."]
[:p [:a {:href password-reset-url} password-reset-url]]]])]
(email/send-message
"Your new Metabase account is all set up"
{email email}
:html message-body)
;; return the message body we sent
message-body))
:subject "Your new Metabase account is all set up"
:recipients [email]
:message-type :html
:message message-body)))
(defn send-password-reset-email
"Format and Send an email informing the user how to reset their password."
......@@ -40,8 +39,7 @@
"It can be safely ignored if you did not request a password reset. Click the link below to reset your password.")]
[:p [:a {:href password-reset-url} password-reset-url]]]])]
(email/send-message
"[Metabase] Password Reset Request"
{email email}
:html message-body)
;; return the message body we sent
message-body))
:subject "[Metabase] Password Reset Request"
:recipients [email]
:message-type :html
:message message-body)))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment