From 2b24fc04b85f52e36325abc01404df486f3da158 Mon Sep 17 00:00:00 2001 From: Bowrna Date: Fri, 27 Dec 2024 23:55:04 +0530 Subject: [PATCH] blocklist subscribers based on bounces --- cmd/bounce.go | 35 +++++++++++++++++++++++++++++++++++ cmd/handlers.go | 2 ++ frontend/src/api/index.js | 10 ++++++++++ internal/core/bounces.go | 9 +++++++++ models/queries.go | 11 ++++++----- queries.sql | 10 ++++++++++ 6 files changed, 72 insertions(+), 5 deletions(-) diff --git a/cmd/bounce.go b/cmd/bounce.go index 4b23db126..778143f14 100644 --- a/cmd/bounce.go +++ b/cmd/bounce.go @@ -111,6 +111,41 @@ func handleDeleteBounces(c echo.Context) error { return c.JSON(http.StatusOK, okResp{true}) } +func handleBlocklistSubscriberBounces(c echo.Context) error { + var ( + app = c.Get("app").(*App) + pID = c.Param("id") + all, _ = strconv.ParseBool(c.QueryParam("all")) + IDs = []int{} + ) + // Is it an /:id call? + if pID != "" { + id, _ := strconv.Atoi(pID) + if id < 1 { + return echo.NewHTTPError(http.StatusBadRequest, app.i18n.T("globals.messages.invalidID")) + } + IDs = append(IDs, id) + } else if !all { + // Multiple IDs. + i, err := parseStringIDs(c.Request().URL.Query()["id"]) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, + app.i18n.Ts("globals.messages.invalidID", "error", err.Error())) + } + + if len(i) == 0 { + return echo.NewHTTPError(http.StatusBadRequest, + app.i18n.Ts("globals.messages.invalidID")) + } + IDs = i + } + + if err := app.core.BlocklistSubscriberBounces(IDs); err != nil { + return err + } + return c.JSON(http.StatusOK, okResp{true}) +} + // handleBounceWebhook renders the HTML preview of a template. func handleBounceWebhook(c echo.Context) error { var ( diff --git a/cmd/handlers.go b/cmd/handlers.go index 0c38adb2a..7e30e303a 100644 --- a/cmd/handlers.go +++ b/cmd/handlers.go @@ -140,6 +140,8 @@ func initHTTPHandlers(e *echo.Echo, app *App) { api.GET("/api/bounces/:id", pm(handleGetBounces, "bounces:get")) api.DELETE("/api/bounces", pm(handleDeleteBounces, "bounces:manage")) api.DELETE("/api/bounces/:id", pm(handleDeleteBounces, "bounces:manage")) + api.PUT("/api/bounces/blocklist", pm(handleBlocklistSubscriberBounces, "bounces:manage")) + api.PUT("/api/bounces/:id/blocklist", pm(handleBlocklistSubscriberBounces, "bounces:manage")) // Subscriber operations based on arbitrary SQL queries. // These aren't very REST-like. diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js index 5f4f11a65..c3d267738 100644 --- a/frontend/src/api/index.js +++ b/frontend/src/api/index.js @@ -179,6 +179,16 @@ export const deleteSubscriberBounces = async (id) => http.delete( { loading: models.bounces }, ); +export const blocklistSubscriberBounce = async (id) => http.put( + `/api/bounces/${id}/blocklist`, + { loading: models.bounces }, +); + +export const blocklistSubscriberBounces = async (params) => http.put( + '/api/bounces/blocklist', + { params, loading: models.bounces }, +); + export const deleteBounce = async (id) => http.delete( `/api/bounces/${id}`, { loading: models.bounces }, diff --git a/internal/core/bounces.go b/internal/core/bounces.go index 0e0800205..49e45a1ea 100644 --- a/internal/core/bounces.go +++ b/internal/core/bounces.go @@ -100,3 +100,12 @@ func (c *Core) DeleteBounces(ids []int) error { } return nil } + +func (c *Core) BlocklistSubscriberBounces(ids []int) error { + if _, err := c.q.BlocklistSubscribersByBounces.Exec(pq.Array(ids)); err != nil { + c.log.Printf("error blocklisting subscribers by bounces: %v", err) + return echo.NewHTTPError(http.StatusInternalServerError, + c.i18n.Ts("globals.messages.errorUpdating", "name", "{globals.terms.bounce}", "error", pqErrMsg(err))) + } + return nil +} diff --git a/models/queries.go b/models/queries.go index 5aa6f5768..fc0b74829 100644 --- a/models/queries.go +++ b/models/queries.go @@ -104,11 +104,12 @@ type Queries struct { UpdateSettings *sqlx.Stmt `query:"update-settings"` // GetStats *sqlx.Stmt `query:"get-stats"` - RecordBounce *sqlx.Stmt `query:"record-bounce"` - QueryBounces string `query:"query-bounces"` - DeleteBounces *sqlx.Stmt `query:"delete-bounces"` - DeleteBouncesBySubscriber *sqlx.Stmt `query:"delete-bounces-by-subscriber"` - GetDBInfo string `query:"get-db-info"` + RecordBounce *sqlx.Stmt `query:"record-bounce"` + QueryBounces string `query:"query-bounces"` + DeleteBounces *sqlx.Stmt `query:"delete-bounces"` + DeleteBouncesBySubscriber *sqlx.Stmt `query:"delete-bounces-by-subscriber"` + BlocklistSubscribersByBounces *sqlx.Stmt `query:"blocklist-subscribers-by-bounces"` + GetDBInfo string `query:"get-db-info"` CreateUser *sqlx.Stmt `query:"create-user"` UpdateUser *sqlx.Stmt `query:"update-user"` diff --git a/queries.sql b/queries.sql index c5b755dde..ad688aba5 100644 --- a/queries.sql +++ b/queries.sql @@ -1040,6 +1040,16 @@ WITH sub AS ( ) DELETE FROM bounces WHERE subscriber_id = (SELECT id FROM sub); +--name: blocklist-subscribers-by-bounces +WITH subscriber_ids_to_blocklist AS ( + SELECT DISTINCT b.subscriber_id + FROM bounces b + WHERE b.id = ANY($1) +) +UPDATE subscribers +SET status = 'blocklisted' +WHERE id IN (SELECT subscriber_id FROM subscriber_ids_to_blocklist) +RETURNING id, email, status; -- name: get-db-info SELECT JSON_BUILD_OBJECT('version', (SELECT VERSION()),