0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-30 16:41:20 -05:00

♻️ Clean email related namespaces

- Remove legacy and outdated mjml directory
- Rename namespace to a proper name
- Add more specs
This commit is contained in:
Andrey Antukh 2023-02-06 12:20:51 +01:00
parent 64f2d874fe
commit bd2745d1fe
32 changed files with 64 additions and 323 deletions

View file

@ -1,66 +0,0 @@
<mjml>
<mj-head>
<mj-font name="Source Sans Pro" href="https://fonts.googleapis.com/css?family=Source%20Sans%20Pro" />
<mj-attributes>
<mj-text font-family="Source Sans Pro, sans-serif" font-size="16px" color="#000000" line-height="150%" />
<mj-button background-color="#31EFB8" color="#1F1F1F" font-family="Source Sans Pro, sans-serif" font-size="16px" />
</mj-attributes>
</mj-head>
<mj-body background-color="#E5E5E5">
<mj-section padding="0">
<mj-column>
<mj-image src="{{ public-uri }}/images/email/uxbox-title.png"
width="97px" height="32px" align="left" padding="16px" />
</mj-column>
</mj-section>
<mj-section background-color="#FFFFFF">
<mj-column>
<mj-text font-size="24px" font-weight="600">Hello {{name}}!</mj-text>
<mj-text>We received a request to change your current email to {{ pending-email }}.</mj-text>
<mj-text>Click to the link below to confirm the change:</mj-text>
<mj-button href="{{ public-uri }}/#/auth/verify-token?token={{token}}">
Confirm email change
</mj-button>
<mj-text>
If you received this email by mistake, please consider changing your password
for security reasons.
</mj-text>
<mj-text>Enjoy!</mj-text>
<mj-text>The Penpot team.</mj-text>
</mj-column>
</mj-section>
<mj-section padding="24px 0 0 0">
<mj-column width="425px">
<mj-text align="center" font-size="14px" color="#64666A">
Penpot is the first Open Source design and prototyping platform meant for cross-domain teams.
</mj-text>
</mj-column>
</mj-section>
<mj-section padding="0">
<mj-column>
<mj-social icon-size="24px" mode="horizontal">
<mj-social-element src="{{ public-uri }}/images/email/logo-uxbox.png" href="https://penpot.app/" padding="0 8px" />
<mj-social-element src="{{ public-uri }}/images/email/logo-twitter.png" href="https://twitter.com/penpotapp" padding="0 8px" />
<mj-social-element src="{{ public-uri }}/images/email/logo-github.png" href="https://github.com/penpot/" padding="0 8px" />
<mj-social-element src="{{ public-uri }}/images/email/logo-instagram.png" href="https://www.instagram.com/penpot.app/" padding="0 8px" />
<mj-social-element src="{{ public-uri }}/images/email/logo-taiga.png" href="https://tree.taiga.io/project/penpot" padding="0 8px" />
</mj-social>
</mj-column>
</mj-section>
<mj-section padding="0 0 24px 0">
<mj-column>
<mj-text align="center" font-size="14px" color="#64666A" line-height="150%">
Penpot © 2020 | Made with &lt;3 and Open Source
</mj-text>
</mj-column>
</mj-section>
</mg-body>
</mjml>

View file

@ -1,59 +0,0 @@
<mjml>
<mj-head>
<mj-font name="Source Sans Pro" href="https://fonts.googleapis.com/css?family=Source%20Sans%20Pro" />
<mj-attributes>
<mj-text font-family="Source Sans Pro, sans-serif" font-size="16px" color="#000000" line-height="150%" />
<mj-button background-color="#31EFB8" color="#1F1F1F" font-family="Source Sans Pro, sans-serif" font-size="16px" />
</mj-attributes>
</mj-head>
<mj-body background-color="#E5E5E5">
<mj-section padding="0">
<mj-column>
<mj-image src="{{ public-uri }}/images/email/uxbox-title.png"
width="97px" height="32px" align="left" padding="16px" />
</mj-column>
</mj-section>
<mj-section background-color="#FFFFFF">
<mj-column>
<mj-text font-size="24px" font-weight="600">Hello!</mj-text>
<mj-text>
{{invited-by}} has invited you to join the team “{{ team }}”.
</mj-text>
<mj-button href="{{ public-uri }}/#/auth/verify-token?token={{token}}">
Accept invite
</mj-button>
<mj-text>Enjoy!</mj-text>
<mj-text>The Penpot team.</mj-text>
</mj-column>
</mj-section>
<mj-section padding="24px 0 0 0">
<mj-column width="425px">
<mj-text align="center" font-size="14px" color="#64666A">
Penpot is the first Open Source design and prototyping platform meant for cross-domain teams.
</mj-text>
</mj-column>
</mj-section>
<mj-section padding="0">
<mj-column>
<mj-social icon-size="24px" mode="horizontal">
<mj-social-element src="{{ public-uri }}/images/email/logo-uxbox.png" href="https://penpot.app/" padding="0 8px" />
<mj-social-element src="{{ public-uri }}/images/email/logo-twitter.png" href="https://twitter.com/penpotapp" padding="0 8px" />
<mj-social-element src="{{ public-uri }}/images/email/logo-github.png" href="https://github.com/penpot/" padding="0 8px" />
<mj-social-element src="{{ public-uri }}/images/email/logo-instagram.png" href="https://www.instagram.com/penpot.app/" padding="0 8px" />
<mj-social-element src="{{ public-uri }}/images/email/logo-taiga.png" href="https://tree.taiga.io/project/penpot" padding="0 8px" />
</mj-social>
</mj-column>
</mj-section>
<mj-section padding="0 0 24px 0">
<mj-column>
<mj-text align="center" font-size="14px" color="#64666A" line-height="150%">
Penpot © 2020 | Made with &lt;3 and Open Source
</mj-text>
</mj-column>
</mj-section>
</mg-body>
</mjml>

View file

@ -1,68 +0,0 @@
<mjml>
<mj-head>
<mj-font name="Source Sans Pro" href="https://fonts.googleapis.com/css?family=Source%20Sans%20Pro" />
<mj-attributes>
<mj-text font-family="Source Sans Pro, sans-serif" font-size="16px" color="#000000" line-height="150%" />
<mj-button background-color="#31EFB8" color="#1F1F1F" font-family="Source Sans Pro, sans-serif" font-size="16px" />
</mj-attributes>
</mj-head>
<mj-body background-color="#E5E5E5">
<mj-section padding="0">
<mj-column>
<mj-image src="{{ public-uri }}/images/email/uxbox-title.png"
width="97px" height="32px" align="left" padding="16px" />
</mj-column>
</mj-section>
<mj-section background-color="#FFFFFF">
<mj-column>
<mj-text font-size="24px" font-weight="600">Hello {{name}}!</mj-text>
<mj-text>
We have received a request to reset your password. Click the link
below to choose a new one:
</mj-text>
<mj-button href="{{ public-uri }}/#/auth/recovery?token={{token}}">
Reset password
</mj-button>
<mj-text>
If you received this email by mistake, you can safely ignore
it. Your password won't be changed.
</mj-text>
<mj-text>Enjoy!</mj-text>
<mj-text>The Penpot team.</mj-text>
</mj-column>
</mj-section>
<mj-section padding="24px 0 0 0">
<mj-column width="425px">
<mj-text align="center" font-size="14px" color="#64666A">
Penpot is the first Open Source design and prototyping platform meant for cross-domain teams.
</mj-text>
</mj-column>
</mj-section>
<mj-section padding="0">
<mj-column>
<mj-social icon-size="24px" mode="horizontal">
<mj-social-element src="{{ public-uri }}/images/email/logo-uxbox.png" href="https://penpot.app/" padding="0 8px" />
<mj-social-element src="{{ public-uri }}/images/email/logo-twitter.png" href="https://twitter.com/penpotapp" padding="0 8px" />
<mj-social-element src="{{ public-uri }}/images/email/logo-github.png" href="https://github.com/penpot/" padding="0 8px" />
<mj-social-element src="{{ public-uri }}/images/email/logo-instagram.png" href="https://www.instagram.com/penpot.app/" padding="0 8px" />
<mj-social-element src="{{ public-uri }}/images/email/logo-taiga.png" href="https://tree.taiga.io/project/penpot" padding="0 8px" />
</mj-social>
</mj-column>
</mj-section>
<mj-section padding="0 0 24px 0">
<mj-column>
<mj-text align="center" font-size="14px" color="#64666A" line-height="150%">
Penpot © 2020 | Made with &lt;3 and Open Source
</mj-text>
</mj-column>
</mj-section>
</mg-body>
</mjml>

View file

@ -1,65 +0,0 @@
<mjml>
<mj-head>
<mj-font name="Source Sans Pro" href="https://fonts.googleapis.com/css?family=Source%20Sans%20Pro" />
<mj-attributes>
<mj-text font-family="Source Sans Pro, sans-serif" font-size="16px" color="#000000" line-height="150%" />
<mj-button background-color="#31EFB8" color="#1F1F1F" font-family="Source Sans Pro, sans-serif" font-size="16px" />
</mj-attributes>
</mj-head>
<mj-body background-color="#E5E5E5">
<mj-section padding="0">
<mj-column>
<mj-image src="{{ public-uri }}/images/email/uxbox-title.png"
width="97px" height="32px" align="left" padding="16px" />
</mj-column>
</mj-section>
<mj-section background-color="#FFFFFF">
<mj-column>
<mj-text font-size="24px" font-weight="600">Hello {{name}}!</mj-text>
<mj-text>
Thanks for signing up for your Penpot account! Please verify your
email using the link below and get started building mockups and
prototypes today!
</mj-text>
<mj-button href="{{ public-uri }}/#/auth/verify-token?token={{token}}">
Verify email
</mj-button>
<mj-text>Enjoy!</mj-text>
<mj-text>The Penpot team.</mj-text>
</mj-column>
</mj-section>
<mj-section padding="24px 0 0 0">
<mj-column width="425px">
<mj-text align="center" font-size="14px" color="#64666A">
Penpot is the first Open Source design and prototyping platform meant for cross-domain teams.
</mj-text>
</mj-column>
</mj-section>
<mj-section padding="0">
<mj-column>
<mj-social icon-size="24px" mode="horizontal">
<mj-social-element src="{{ public-uri }}/images/email/logo-uxbox.png" href="https://penpot.app/" padding="0 8px" />
<mj-social-element src="{{ public-uri }}/images/email/logo-twitter.png" href="https://twitter.com/penpotapp" padding="0 8px" />
<mj-social-element src="{{ public-uri }}/images/email/logo-github.png" href="https://github.com/penpot/" padding="0 8px" />
<mj-social-element src="{{ public-uri }}/images/email/logo-instagram.png" href="https://www.instagram.com/penpot.app/" padding="0 8px" />
<mj-social-element src="{{ public-uri }}/images/email/logo-taiga.png" href="https://tree.taiga.io/project/penpot" padding="0 8px" />
</mj-social>
</mj-column>
</mj-section>
<mj-section padding="0 0 24px 0">
<mj-column>
<mj-text align="center" font-size="14px" color="#64666A" line-height="150%">
Penpot © 2020 | Made with &lt;3 and Open Source
</mj-text>
</mj-column>
</mj-section>
</mg-body>
</mjml>

View file

@ -4,7 +4,7 @@
;;
;; Copyright (c) KALEIDOS INC
(ns app.emails
(ns app.email
"Main api for send emails."
(:require
[app.common.exceptions :as ex]
@ -14,7 +14,7 @@
[app.config :as cf]
[app.db :as db]
[app.db.sql :as sql]
[app.emails.invite-to-team :as-alias emails.invite-to-team]
[app.email.invite-to-team :as-alias email.invite-to-team]
[app.metrics :as mtx]
[app.util.template :as tmpl]
[app.worker :as wrk]
@ -71,7 +71,7 @@
(.addFrom ^MimeMessage mmsg from)))))
(defn- assign-reply-to
[mmsg {:keys [default-reply-to] :as cfg} {:keys [reply-to] :as params}]
[mmsg {:keys [::default-reply-to] :as cfg} {:keys [reply-to] :as params}]
(let [reply-to (or reply-to default-reply-to)]
(when reply-to
(let [reply-to (parse-address reply-to)]
@ -127,9 +127,8 @@
mmsg))
(defn- opts->props
[{:keys [username tls host port timeout default-from]
:or {timeout 30000}
:as opts}]
[{:keys [::username ::tls ::host ::port ::timeout ::default-from]
:or {timeout 30000}}]
(reduce-kv
(fn [^Properties props k v]
(if (nil? v)
@ -150,8 +149,8 @@
"mail.smtp.connectiontimeout" timeout}))
(defn- create-smtp-session
[opts]
(let [props (opts->props opts)]
[cfg]
(let [props (opts->props cfg)]
(Session/getInstance props)))
(defn- create-smtp-message
@ -171,7 +170,7 @@
;; TEMPLATE EMAIL IMPL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:private email-path "app/emails/%(id)s/%(lang)s.%(type)s")
(def ^:private email-path "app/email/%(id)s/%(lang)s.%(type)s")
(defn- render-email-template-part
[type id context]
@ -283,14 +282,14 @@
(s/def ::default-from ::cf/smtp-default-from)
(s/def ::smtp-config
(s/keys :opt-un [::username
::password
::tls
::ssl
::host
::port
::default-from
::default-reply-to]))
(s/keys :opt [::username
::password
::tls
::ssl
::host
::port
::default-from
::default-reply-to]))
(declare send-to-logger!)
@ -306,8 +305,8 @@
(let [session (create-smtp-session cfg)]
(with-open [transport (.getTransport session (if (:ssl cfg) "smtps" "smtp"))]
(.connect ^Transport transport
^String (:username cfg)
^String (:password cfg))
^String (::username cfg)
^String (::password cfg))
(let [^MimeMessage message (create-smtp-message cfg session params)]
(.sendMessage ^Transport transport
@ -319,10 +318,10 @@
(send-to-logger! cfg params))))
(defmethod ig/pre-init-spec ::handler [_]
(s/keys :req-un [::sendmail ::mtx/metrics]))
(s/keys :req [::sendmail ::mtx/metrics]))
(defmethod ig/init-key ::handler
[_ {:keys [sendmail]}]
[_ {:keys [::sendmail]}]
(fn [{:keys [props] :as task}]
(sendmail props)))
@ -380,14 +379,14 @@
"Password change confirmation email"
(template-factory ::change-email))
(s/def ::emails.invite-to-team/invited-by ::us/string)
(s/def ::emails.invite-to-team/team ::us/string)
(s/def ::emails.invite-to-team/token ::us/string)
(s/def ::email.invite-to-team/invited-by ::us/string)
(s/def ::email.invite-to-team/team ::us/string)
(s/def ::email.invite-to-team/token ::us/string)
(s/def ::invite-to-team
(s/keys :req-un [::emails.invite-to-team/invited-by
::emails.invite-to-team/token
::emails.invite-to-team/team]))
(s/keys :req-un [::email.invite-to-team/invited-by
::email.invite-to-team/token
::email.invite-to-team/team]))
(def invite-to-team
"Teams member invitation email."

View file

@ -12,6 +12,7 @@
[app.common.logging :as l]
[app.config :as cf]
[app.db :as-alias db]
[app.email :as-alias email]
[app.http.access-token :as-alias actoken]
[app.http.assets :as-alias http.assets]
[app.http.awsns :as http.awsns]
@ -375,20 +376,19 @@
:run-webhook
(ig/ref ::webhooks/run-webhook-handler)}}
::email/sendmail
{::email/host (cf/get :smtp-host)
::email/port (cf/get :smtp-port)
::email/ssl (cf/get :smtp-ssl)
::email/tls (cf/get :smtp-tls)
::email/username (cf/get :smtp-username)
::email/password (cf/get :smtp-password)
::email/default-reply-to (cf/get :smtp-default-reply-to)
::email/default-from (cf/get :smtp-default-from)}
:app.emails/sendmail
{:host (cf/get :smtp-host)
:port (cf/get :smtp-port)
:ssl (cf/get :smtp-ssl)
:tls (cf/get :smtp-tls)
:username (cf/get :smtp-username)
:password (cf/get :smtp-password)
:default-reply-to (cf/get :smtp-default-reply-to)
:default-from (cf/get :smtp-default-from)}
:app.emails/handler
{:sendmail (ig/ref :app.emails/sendmail)
:metrics (ig/ref ::mtx/metrics)}
::email/handler
{::email/sendmail (ig/ref ::email/sendmail)
::mtx/metrics (ig/ref ::mtx/metrics)}
:app.tasks.tasks-gc/handler
{:pool (ig/ref ::db/pool)

View file

@ -13,7 +13,7 @@
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
[app.emails :as eml]
[app.email :as eml]
[app.http.session :as session]
[app.loggers.audit :as audit]
[app.main :as-alias main]

View file

@ -452,6 +452,7 @@
`::embed-assets?`: instead of including the libraries, embed in the
same file library all assets used from external libraries."
[{:keys [::include-libraries? ::embed-assets?] :as options}]
(us/assert! ::write-export-options options)
(us/verify!
:expr (not (and include-libraries? embed-assets?))

View file

@ -11,7 +11,7 @@
[app.common.spec :as us]
[app.config :as cf]
[app.db :as db]
[app.emails :as eml]
[app.email :as eml]
[app.rpc :as-alias rpc]
[app.rpc.commands.profile :as profile]
[app.rpc.doc :as-alias doc]

View file

@ -13,7 +13,7 @@
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
[app.emails :as eml]
[app.email :as eml]
[app.http.session :as session]
[app.loggers.audit :as audit]
[app.main :as-alias main]

View file

@ -13,7 +13,7 @@
[app.common.uuid :as uuid]
[app.config :as cf]
[app.db :as db]
[app.emails :as eml]
[app.email :as eml]
[app.loggers.audit :as audit]
[app.main :as-alias main]
[app.media :as media]

View file

@ -58,7 +58,7 @@
:expr (string? destination)
:hint "destination should be provided")
(let [handler (:app.emails/sendmail system)]
(let [handler (:app.email/sendmail system)]
(handler {:body "test email"
:subject "test email"
:to [destination]})))

View file

@ -7,7 +7,7 @@
(ns backend-tests.bounce-handling-test
(:require
[app.db :as db]
[app.emails :as emails]
[app.email :as email]
[app.http.awsns :as awsns]
[app.tokens :as tokens]
[app.util.time :as dt]
@ -261,11 +261,11 @@
(th/create-complaint-for pool {:type :bounce :id (:id profile)})
(th/create-complaint-for pool {:type :bounce :id (:id profile)})
(t/is (true? (emails/allow-send-emails? pool profile)))
(t/is (true? (email/allow-send-emails? pool profile)))
(t/is (= 4 (:call-count @mock)))
(th/create-complaint-for pool {:type :bounce :id (:id profile)})
(t/is (false? (emails/allow-send-emails? pool profile))))))
(t/is (false? (email/allow-send-emails? pool profile))))))
(t/deftest test-allow-send-messages-predicate-with-complaints
@ -281,32 +281,32 @@
(th/create-complaint-for pool {:type :bounce :id (:id profile)})
(th/create-complaint-for pool {:type :complaint :id (:id profile)})
(t/is (true? (emails/allow-send-emails? pool profile)))
(t/is (true? (email/allow-send-emails? pool profile)))
(t/is (= 4 (:call-count @mock)))
(th/create-complaint-for pool {:type :complaint :id (:id profile)})
(t/is (false? (emails/allow-send-emails? pool profile))))))
(t/is (false? (email/allow-send-emails? pool profile))))))
(t/deftest test-has-complaint-reports-predicate
(let [profile (th/create-profile* 1)
pool (:app.db/pool th/*system*)]
(t/is (false? (emails/has-complaint-reports? pool (:email profile))))
(t/is (false? (email/has-complaint-reports? pool (:email profile))))
(th/create-global-complaint-for pool {:type :bounce :email (:email profile)})
(t/is (false? (emails/has-complaint-reports? pool (:email profile))))
(t/is (false? (email/has-complaint-reports? pool (:email profile))))
(th/create-global-complaint-for pool {:type :complaint :email (:email profile)})
(t/is (true? (emails/has-complaint-reports? pool (:email profile))))))
(t/is (true? (email/has-complaint-reports? pool (:email profile))))))
(t/deftest test-has-bounce-reports-predicate
(let [profile (th/create-profile* 1)
pool (:app.db/pool th/*system*)]
(t/is (false? (emails/has-bounce-reports? pool (:email profile))))
(t/is (false? (email/has-bounce-reports? pool (:email profile))))
(th/create-global-complaint-for pool {:type :complaint :email (:email profile)})
(t/is (false? (emails/has-bounce-reports? pool (:email profile))))
(t/is (false? (email/has-bounce-reports? pool (:email profile))))
(th/create-global-complaint-for pool {:type :bounce :email (:email profile)})
(t/is (true? (emails/has-bounce-reports? pool (:email profile))))))
(t/is (true? (email/has-bounce-reports? pool (:email profile))))))

View file

@ -8,7 +8,7 @@
(:require
[backend-tests.helpers :as th]
[app.db :as db]
[app.emails :as emails]
[app.email :as emails]
[clojure.test :as t]
[promesa.core :as p]))

View file

@ -231,7 +231,7 @@
(t/deftest prepare-register-and-register-profile-2
(with-redefs [app.rpc.commands.auth/register-retry-threshold (dt/duration 500)]
(with-mocks [mock {:target 'app.emails/send! :return nil}]
(with-mocks [mock {:target 'app.email/send! :return nil}]
(let [current-token (atom nil)]
;; PREPARE REGISTER
@ -409,7 +409,7 @@
(t/is (= :email-as-password (:code edata))))))
(t/deftest email-change-request
(with-mocks [mock {:target 'app.emails/send! :return nil}]
(with-mocks [mock {:target 'app.email/send! :return nil}]
(let [profile (th/create-profile* 1)
pool (:app.db/pool th/*system*)
data {::th/type :request-email-change
@ -443,7 +443,7 @@
(t/deftest email-change-request-without-smtp
(with-mocks [mock {:target 'app.emails/send! :return nil}]
(with-mocks [mock {:target 'app.email/send! :return nil}]
(with-redefs [app.config/flags #{}]
(let [profile (th/create-profile* 1)
pool (:app.db/pool th/*system*)
@ -459,7 +459,7 @@
(t/deftest request-profile-recovery
(with-mocks [mock {:target 'app.emails/send! :return nil}]
(with-mocks [mock {:target 'app.email/send! :return nil}]
(let [profile1 (th/create-profile* 1)
profile2 (th/create-profile* 2 {:is-active true})
pool (:app.db/pool th/*system*)

View file

@ -22,7 +22,7 @@
(t/use-fixtures :each th/database-reset)
(t/deftest create-team-invitations
(with-mocks [mock {:target 'app.emails/send! :return nil}]
(with-mocks [mock {:target 'app.email/send! :return nil}]
(let [profile1 (th/create-profile* 1 {:is-active true})
profile2 (th/create-profile* 2 {:is-active true})
profile3 (th/create-profile* 3 {:is-active true :is-muted true})
@ -105,7 +105,7 @@
(t/deftest invitation-tokens
(with-mocks [mock {:target 'app.emails/send! :return nil}]
(with-mocks [mock {:target 'app.email/send! :return nil}]
(let [profile1 (th/create-profile* 1 {:is-active true})
profile2 (th/create-profile* 2 {:is-active true})
@ -251,7 +251,7 @@
)))
(t/deftest create-team-invitations-with-email-verification-disabled
(with-mocks [mock {:target 'app.emails/send! :return nil}]
(with-mocks [mock {:target 'app.email/send! :return nil}]
(let [profile1 (th/create-profile* 1 {:is-active true})
profile2 (th/create-profile* 2 {:is-active true})
profile3 (th/create-profile* 3 {:is-active true :is-muted true})

View file

@ -8,7 +8,6 @@
(:require
[backend-tests.helpers :as th]
[app.db :as db]
[app.emails :as emails]
[app.util.time :as dt]
[clojure.pprint :refer [pprint]]
[clojure.test :as t]