Skip to content

Commit

Permalink
Add Postmark bounce webhook support (refactor #1385) (#1485)
Browse files Browse the repository at this point in the history
Co-authored-by: Thomas Siebers <[email protected]>
  • Loading branch information
knadh and tosie committed Aug 31, 2023
1 parent e5ac111 commit 2b95c88
Show file tree
Hide file tree
Showing 37 changed files with 283 additions and 28 deletions.
13 changes: 13 additions & 0 deletions cmd/bounce.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,19 @@ func handleBounceWebhook(c echo.Context) error {
}
bounces = append(bounces, bs...)

// Postmark.
case service == "postmark" && app.constants.BouncePostmarkEnabled:
bs, err := app.bounce.Postmark.ProcessBounce(rawReq, c)
if err != nil {
app.log.Printf("error processing postmark notification: %v", err)
if _, ok := err.(*echo.HTTPError); ok {
return err
}

return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("globals.messages.invalidData"))
}
bounces = append(bounces, bs...)

default:
return echo.NewHTTPError(http.StatusBadRequest, app.i18n.Ts("bounces.unknownService"))
}
Expand Down
13 changes: 12 additions & 1 deletion cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ type constants struct {
BounceWebhooksEnabled bool
BounceSESEnabled bool
BounceSendgridEnabled bool
BouncePostmarkEnabled bool
}

type notifTpls struct {
Expand Down Expand Up @@ -400,6 +401,8 @@ func initConstants() *constants {
c.BounceWebhooksEnabled = ko.Bool("bounce.webhooks_enabled")
c.BounceSESEnabled = ko.Bool("bounce.ses_enabled")
c.BounceSendgridEnabled = ko.Bool("bounce.sendgrid_enabled")
c.BouncePostmarkEnabled = ko.Bool("bounce.postmark.enabled")

return &c
}

Expand Down Expand Up @@ -668,7 +671,15 @@ func initBounceManager(app *App) *bounce.Manager {
SESEnabled: ko.Bool("bounce.ses_enabled"),
SendgridEnabled: ko.Bool("bounce.sendgrid_enabled"),
SendgridKey: ko.String("bounce.sendgrid_key"),

Postmark: struct {
Enabled bool
Username string
Password string
}{
ko.Bool("bounce.postmark.enabled"),
ko.String("bounce.postmark.username"),
ko.String("bounce.postmark.password"),
},
RecordBounceCB: app.core.RecordBounce,
}

Expand Down
4 changes: 4 additions & 0 deletions cmd/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func handleGetSettings(c echo.Context) error {
s.UploadS3AwsSecretAccessKey = strings.Repeat(pwdMask, utf8.RuneCountInString(s.UploadS3AwsSecretAccessKey))
s.SendgridKey = strings.Repeat(pwdMask, utf8.RuneCountInString(s.SendgridKey))
s.SecurityCaptchaSecret = strings.Repeat(pwdMask, utf8.RuneCountInString(s.SecurityCaptchaSecret))
s.BouncePostmark.Password = strings.Repeat(pwdMask, utf8.RuneCountInString(s.BouncePostmark.Password))

return c.JSON(http.StatusOK, okResp{s})
}
Expand Down Expand Up @@ -183,6 +184,9 @@ func handleUpdateSettings(c echo.Context) error {
if set.SendgridKey == "" {
set.SendgridKey = cur.SendgridKey
}
if set.BouncePostmark.Password == "" {
set.BouncePostmark.Password = cur.BouncePostmark.Password
}
if set.SecurityCaptchaSecret == "" {
set.SecurityCaptchaSecret = cur.SecurityCaptchaSecret
}
Expand Down
1 change: 1 addition & 0 deletions cmd/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ var migList = []migFunc{
{"v2.3.0", migrations.V2_3_0},
{"v2.4.0", migrations.V2_4_0},
{"v2.5.0", migrations.V2_5_0},
{"v2.6.0", migrations.V2_6_0},
}

// upgrade upgrades the database to the current version by running SQL migration files
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/content/bounces.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ Some mail servers may also return the bounce to the `Reply-To` address, which ca
The bounce webhook API can be used to record bounce events with custom scripting. This could be by reading a mailbox, a database, or mail server logs.

| Method | Endpoint | Description |
|--------|------------------|------------------------|
| ------ | ---------------- | ---------------------- |
| `POST` | /webhooks/bounce | Record a bounce event. |


| Name | Data type | Required/Optional | Description |
|-------------------|-----------|-------------------|--------------------------------------------------------------------------------------|
| ----------------- | --------- | ----------------- | ------------------------------------------------------------------------------------ |
| `subscriber_uuid` | String | Optional | The UUID of the subscriber. Either this or `email` is required. |
| `email` | String | Optional | The e-mail of the subscriber. Either this or `subscriber_uuid` is required. |
| `campaign_uuid` | String | Optional | UUID of the campaign for which the bounce happened. |
Expand All @@ -46,7 +46,7 @@ listmonk supports receiving bounce webhook events from the following SMTP provid
|-----------------------------|------------------|-----------|
| `https://listmonk.yoursite.com/webhooks/service/ses` | Amazon (AWS) SES | You can use these [Mautic steps](https://docs.mautic.org/en/channels/emails/bounce-management#amazon-webhook) as a general guide, but use your listmonk's endpoint instead. <ul> <li>When creating the *topic* select "standard" instead of the preselected "FIFO". You can put a name and leave everything else at default.</li> <li>When creating a *subscription* choose HTTPS for "Protocol", and leave *"Enable raw message delivery"* UNCHECKED.</li> <li>On the _"SES -> verified identities"_ page, make sure to check **"[include original headers](https://github.com/knadh/listmonk/issues/720#issuecomment-1046877192)"**.</li> <li>The Mautic screenshot suggests you should turn off _email feedback forwarding_, but that's completely optional depending on whether you want want email notifications.</li></ul> |
| `https://listmonk.yoursite.com/webhooks/service/sendgrid` | Sendgrid / Twilio Signed event webhook | [More info](https://docs.sendgrid.com/for-developers/tracking-events/getting-started-event-webhook-security-features) |

| `https://listmonk.yoursite.com/webhooks/service/postmark` | Postmark webhook | [More info](https://postmarkapp.com/developer/webhooks/webhooks-overview)


## Verification
Expand Down
14 changes: 14 additions & 0 deletions docs/swagger/collections.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2703,6 +2703,8 @@ components:
type: string
settings.bounces.enableSendgrid:
type: string
settings.bounces.enablePostmark:
type: string
settings.bounces.enableWebhooks:
type: string
settings.bounces.enabled:
Expand All @@ -2721,6 +2723,12 @@ components:
type: string
settings.bounces.sendgridKey:
type: string
settings.bounces.postmarkUsername:
type: string
settings.bounces.postmarkUsernameHelp:
type: string
settings.bounces.postmarkPassword:
type: string
settings.bounces.type:
type: string
settings.bounces.username:
Expand Down Expand Up @@ -3398,6 +3406,12 @@ components:
type: boolean
bounce.sendgrid_key:
type: string
bounce.postmark_enabled:
type: boolean
bounce.postmark_username:
type: string
bounce.postmark_password:
type: string
bounce.mailboxes:
type: array
items:
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/views/Settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ export default Vue.extend({
hasDummy = 'captcha';
}
if (this.isDummy(form['bounce.postmark'].password)) {
form['bounce.postmark'].password = '';
} else if (this.hasDummy(form['bounce.postmark'].password)) {
hasDummy = 'postmark';
}
for (let i = 0; i < form.messengers.length; i += 1) {
// If it's the dummy UI password placeholder, ignore it.
if (this.isDummy(form.messengers[i].password)) {
Expand Down
27 changes: 27 additions & 0 deletions frontend/src/views/settings/bounces.vue
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,33 @@
</b-field>
</div>
</div>
<div class="columns">
<div class="column is-3">
<b-field :label="$t('settings.bounces.enablePostmark')">
<b-switch v-model="data['bounce.postmark'].enabled"
name="postmark_enabled" :native-value="true"
data-cy="btn-enable-bounce-postmark" />
</b-field>
</div>
<div class="column">
<b-field :label="$t('settings.bounces.postmarkUsername')"
:message="$t('settings.bounces.postmarkUsernameHelp')">
<b-input v-model="data['bounce.postmark'].username" type="text"
:disabled="!data['bounce.postmark'].enabled"
name="postmark_username"
data-cy="btn-enable-bounce-postmark" />
</b-field>
</div>
<div class="column">
<b-field :label="$t('settings.bounces.postmarkPassword')"
:message="$t('globals.messages.passwordChange')">
<b-input v-model="data['bounce.postmark'].password" type="password"
:disabled="!data['bounce.postmark'].enabled"
name="postmark_password"
data-cy="btn-enable-bounce-postmark" />
</b-field>
</div>
</div>
</div>
</div>

Expand Down
3 changes: 2 additions & 1 deletion i18n/ca.json
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@
"settings.bounces.enableSES": "Activa SES",
"settings.bounces.enableSendgrid": "Activa SendGrid",
"settings.bounces.enableWebhooks": "Activa els webhooks pels rebots",
"settings.bounces.enablePostmark": "Activa Postmark",
"settings.bounces.enabled": "Activat",
"settings.bounces.folder": "Carpeta",
"settings.bounces.folderHelp": "Nom de la carpeta IMAP a escanejar. Ex: Safata d'entrada.",
Expand Down Expand Up @@ -577,4 +578,4 @@
"templates.subject": "Assumpte",
"users.login": "Inicia sessió",
"users.logout": "Tanca sessió"
}
}
3 changes: 2 additions & 1 deletion i18n/cs-cz.json
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@
"settings.bounces.enableSES": "Povolit SES",
"settings.bounces.enableSendgrid": "Povolit SendGrid",
"settings.bounces.enableWebhooks": "Povolit webhooky v případě nedoručitelnosti",
"settings.bounces.enablePostmark": "Povolit Postmark",
"settings.bounces.enabled": "Povoleno",
"settings.bounces.folder": "Složka",
"settings.bounces.folderHelp": "Název složky IMAP ke skenování. Např.: Došlá pošta.",
Expand Down Expand Up @@ -577,4 +578,4 @@
"templates.subject": "Předmět",
"users.login": "Přihlásit",
"users.logout": "Odhlásit"
}
}
3 changes: 2 additions & 1 deletion i18n/cy.json
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@
"settings.bounces.enableSES": "Galluogi SES",
"settings.bounces.enableSendgrid": "Galluogi SendGrid",
"settings.bounces.enableWebhooks": "Galluogi bachau gwe sydd wedi sboncio'n ôl",
"settings.bounces.enablePostmark": "Galluogi Postmark",
"settings.bounces.enabled": "Wedi galluogi",
"settings.bounces.folder": "Ffolder",
"settings.bounces.folderHelp": "Enw'r ffolder IMAP i'w sganio. ee: blwch derbyn.",
Expand Down Expand Up @@ -577,4 +578,4 @@
"templates.subject": "Pwnc",
"users.login": "Mewngofnodi",
"users.logout": "Allgofnodi"
}
}
3 changes: 2 additions & 1 deletion i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@
"settings.bounces.enableSES": "SES aktivieren",
"settings.bounces.enableSendgrid": "SendGrid aktivieren",
"settings.bounces.enableWebhooks": "Bounce-Webhooks aktivieren",
"settings.bounces.enablePostmark": "Postmark aktivieren",
"settings.bounces.enabled": "Aktiviert",
"settings.bounces.folder": "Ordner",
"settings.bounces.folderHelp": "Name des zu scannenden IMAP-Ordners. z.B.: Inbox.",
Expand Down Expand Up @@ -577,4 +578,4 @@
"templates.subject": "Betreff",
"users.login": "Anmelden",
"users.logout": "Abmelden"
}
}
6 changes: 5 additions & 1 deletion i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@
"settings.bounces.enableSES": "Enable SES",
"settings.bounces.enableSendgrid": "Enable SendGrid",
"settings.bounces.enableWebhooks": "Enable bounce webhooks",
"settings.bounces.enablePostmark": "Enable Postmark",
"settings.bounces.enabled": "Enabled",
"settings.bounces.folder": "Folder",
"settings.bounces.folderHelp": "Name of the IMAP folder to scan. Eg: Inbox.",
Expand All @@ -372,6 +373,9 @@
"settings.bounces.scanInterval": "Scan interval",
"settings.bounces.scanIntervalHelp": "Interval at which the bounce mailbox should be scanned for bounces (s for second, m for minute).",
"settings.bounces.sendgridKey": "SendGrid Key",
"settings.bounces.postmarkUsername": "Postmark Username",
"settings.bounces.postmarkUsernameHelp": "Postmark allows you to enable basic authorization for webhooks. Make sure to enter the same credentials here and in your Postmark webhook settings.",
"settings.bounces.postmarkPassword": "Postmark Password",
"settings.bounces.type": "Type",
"settings.bounces.username": "Username",
"settings.confirmRestart": "Ensure running campaigns are paused. Restart?",
Expand Down Expand Up @@ -573,4 +577,4 @@
"templates.subject": "Subject",
"users.login": "Login",
"users.logout": "Logout"
}
}
3 changes: 2 additions & 1 deletion i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@
"settings.bounces.enableSES": "Activar SES",
"settings.bounces.enableSendgrid": "Activar SendGrid",
"settings.bounces.enableWebhooks": "Activar webhooks de rebotes",
"settings.bounces.enablePostmark": "Activar Postmark",
"settings.bounces.enabled": "Activado",
"settings.bounces.folder": "Carpeta",
"settings.bounces.folderHelp": "Nombre de la carpeta IMAP a escanear, por ejemplo: Entrada.",
Expand Down Expand Up @@ -578,4 +579,4 @@
"templates.subject": "Asunto",
"users.login": "Ingresar",
"users.logout": "Salir"
}
}
3 changes: 2 additions & 1 deletion i18n/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@
"settings.bounces.enableMailbox": "Ota käyttöön bounce-postilaatikko",
"settings.bounces.enableSES": "Ota käyttöön SES",
"settings.bounces.enableSendgrid": "Ota käyttöön SendGrid",
"settings.bounces.enablePostmark": "Ota käyttöön Postmark",
"settings.bounces.enableWebhooks": "Ota käyttöön palautusten webhookit",
"settings.bounces.enabled": "Käytössä",
"settings.bounces.folder": "Kansio",
Expand Down Expand Up @@ -578,4 +579,4 @@
"templates.subject": "Aihe",
"users.login": "Kirjaudu sisään",
"users.logout": "Kirjaudu ulos"
}
}
3 changes: 2 additions & 1 deletion i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@
"settings.bounces.enableSES": "Activer SES",
"settings.bounces.enableSendgrid": "Activer SendGrid",
"settings.bounces.enableWebhooks": "Activez les 'webhooks' de rebond",
"settings.bounces.enablePostmark": "Activer Postmark",
"settings.bounces.enabled": "Activer",
"settings.bounces.folder": "Dossier",
"settings.bounces.folderHelp": "Nom du dossier IMAP à scanner. Exple : InBox.",
Expand Down Expand Up @@ -578,4 +579,4 @@
"templates.subject": "Objet",
"users.login": "Connecter",
"users.logout": "Déconnecter"
}
}
3 changes: 2 additions & 1 deletion i18n/hu.json
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@
"settings.bounces.enableSES": "SES",
"settings.bounces.enableSendgrid": "SendGrid",
"settings.bounces.enableWebhooks": "Visszapattanó webhook",
"settings.bounces.enablePostmark": "Postmark",
"settings.bounces.enabled": "Engedélyezve",
"settings.bounces.folder": "Mappa",
"settings.bounces.folderHelp": "A vizsgálandó IMAP mappa neve. Például: 'Inbox'",
Expand Down Expand Up @@ -577,4 +578,4 @@
"templates.subject": "Tárgy",
"users.login": "Belépés",
"users.logout": "Kijelentkezés"
}
}
3 changes: 2 additions & 1 deletion i18n/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@
"settings.bounces.enableSES": "Attiva SES",
"settings.bounces.enableSendgrid": "Attiva SendGrid",
"settings.bounces.enableWebhooks": "Attiva bounce webhooks",
"settings.bounces.enablePostmark": "Attiva Postmark",
"settings.bounces.enabled": "Attivato",
"settings.bounces.folder": "Cartella",
"settings.bounces.folderHelp": "Nome della cartella IMAP da analizzare. Ad esempio: Posta in arrivo.",
Expand Down Expand Up @@ -578,4 +579,4 @@
"templates.subject": "Oggeto",
"users.login": "Accesso",
"users.logout": "Esci"
}
}
3 changes: 2 additions & 1 deletion i18n/jp.json
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@
"settings.bounces.enableSES": "SESを有効にする",
"settings.bounces.enableSendgrid": "SendGridを有効にする",
"settings.bounces.enableWebhooks": "バウンスウェブフックを有効にする",
"settings.bounces.enablePostmark": "Postmarkを有効にする",
"settings.bounces.enabled": "有効",
"settings.bounces.folder": "フォルダ",
"settings.bounces.folderHelp": "スキャンするIMAPフォルダの名前。 例: Inbox.",
Expand Down Expand Up @@ -578,4 +579,4 @@
"templates.subject": "件名",
"users.login": "ログイン",
"users.logout": "ログアウト"
}
}
3 changes: 2 additions & 1 deletion i18n/ml.json
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@
"settings.bounces.enableSES": "SES പ്രവർത്തനക്ഷമമാക്കുക",
"settings.bounces.enableSendgrid": "SendGrid പ്രവർത്തനക്ഷമമാക്കുക",
"settings.bounces.enableWebhooks": "ബൗൺസ് വെബ്‌ഹുക്കുകൾ പ്രവർത്തനക്ഷമമാക്കുക",
"settings.bounces.enablePostmark": "Postmark പ്രവർത്തനക്ഷമമാക്കുക",
"settings.bounces.enabled": "പ്രവർത്തനക്ഷമമാക്കി",
"settings.bounces.folder": "ഫോൾഡർ",
"settings.bounces.folderHelp": "സ്കാൻ ചെയ്യാനുള്ള IMAP ഫോൾഡറിന്റെ പേര്. ഉദാ: ഇൻബോക്സ്.",
Expand Down Expand Up @@ -577,4 +578,4 @@
"templates.subject": "വിഷയം",
"users.login": "പ്രവേശിക്കുക",
"users.logout": "പുറത്തുകടക്കുക"
}
}
3 changes: 2 additions & 1 deletion i18n/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@
"settings.bounces.enableSES": "SES inschakelen",
"settings.bounces.enableSendgrid": "SendGrid inschakelen",
"settings.bounces.enableWebhooks": "Bounce webhooks inschakelen",
"settings.bounces.enablePostmark": "Postmark inschakelen",
"settings.bounces.enabled": "Ingeschakeld",
"settings.bounces.folder": "Map",
"settings.bounces.folderHelp": "Naam van de IMAP map om te scannen. Bv.: Inbox.",
Expand Down Expand Up @@ -577,4 +578,4 @@
"templates.subject": "Onderwerp",
"users.login": "Inloggen",
"users.logout": "Uitloggen"
}
}
Loading

0 comments on commit 2b95c88

Please sign in to comment.