123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- package main
- import (
- "context"
- "database/sql"
- "fmt"
- "time"
- "github.com/jmoiron/sqlx"
- "github.com/lib/pq"
- )
- // Queries contains all prepared SQL queries.
- type Queries struct {
- GetDashboardCharts *sqlx.Stmt `query:"get-dashboard-charts"`
- GetDashboardCounts *sqlx.Stmt `query:"get-dashboard-counts"`
- InsertSubscriber *sqlx.Stmt `query:"insert-subscriber"`
- UpsertSubscriber *sqlx.Stmt `query:"upsert-subscriber"`
- UpsertBlocklistSubscriber *sqlx.Stmt `query:"upsert-blocklist-subscriber"`
- GetSubscriber *sqlx.Stmt `query:"get-subscriber"`
- GetSubscribersByEmails *sqlx.Stmt `query:"get-subscribers-by-emails"`
- GetSubscriberLists *sqlx.Stmt `query:"get-subscriber-lists"`
- GetSubscriberListsLazy *sqlx.Stmt `query:"get-subscriber-lists-lazy"`
- SubscriberExists *sqlx.Stmt `query:"subscriber-exists"`
- UpdateSubscriber *sqlx.Stmt `query:"update-subscriber"`
- BlocklistSubscribers *sqlx.Stmt `query:"blocklist-subscribers"`
- AddSubscribersToLists *sqlx.Stmt `query:"add-subscribers-to-lists"`
- DeleteSubscriptions *sqlx.Stmt `query:"delete-subscriptions"`
- ConfirmSubscriptionOptin *sqlx.Stmt `query:"confirm-subscription-optin"`
- UnsubscribeSubscribersFromLists *sqlx.Stmt `query:"unsubscribe-subscribers-from-lists"`
- DeleteSubscribers *sqlx.Stmt `query:"delete-subscribers"`
- Unsubscribe *sqlx.Stmt `query:"unsubscribe"`
- ExportSubscriberData *sqlx.Stmt `query:"export-subscriber-data"`
- // Non-prepared arbitrary subscriber queries.
- QuerySubscribers string `query:"query-subscribers"`
- QuerySubscribersCount string `query:"query-subscribers-count"`
- QuerySubscribersForExport string `query:"query-subscribers-for-export"`
- QuerySubscribersTpl string `query:"query-subscribers-template"`
- DeleteSubscribersByQuery string `query:"delete-subscribers-by-query"`
- AddSubscribersToListsByQuery string `query:"add-subscribers-to-lists-by-query"`
- BlocklistSubscribersByQuery string `query:"blocklist-subscribers-by-query"`
- DeleteSubscriptionsByQuery string `query:"delete-subscriptions-by-query"`
- UnsubscribeSubscribersFromListsByQuery string `query:"unsubscribe-subscribers-from-lists-by-query"`
- CreateList *sqlx.Stmt `query:"create-list"`
- QueryLists string `query:"query-lists"`
- GetLists *sqlx.Stmt `query:"get-lists"`
- GetListsByOptin *sqlx.Stmt `query:"get-lists-by-optin"`
- UpdateList *sqlx.Stmt `query:"update-list"`
- UpdateListsDate *sqlx.Stmt `query:"update-lists-date"`
- DeleteLists *sqlx.Stmt `query:"delete-lists"`
- CreateCampaign *sqlx.Stmt `query:"create-campaign"`
- QueryCampaigns string `query:"query-campaigns"`
- GetCampaign *sqlx.Stmt `query:"get-campaign"`
- GetCampaignForPreview *sqlx.Stmt `query:"get-campaign-for-preview"`
- GetCampaignStats *sqlx.Stmt `query:"get-campaign-stats"`
- GetCampaignStatus *sqlx.Stmt `query:"get-campaign-status"`
- GetCampaignViewCounts *sqlx.Stmt `query:"get-campaign-view-counts"`
- GetCampaignClickCounts *sqlx.Stmt `query:"get-campaign-click-counts"`
- GetCampaignBounceCounts *sqlx.Stmt `query:"get-campaign-bounce-counts"`
- GetCampaignLinkCounts *sqlx.Stmt `query:"get-campaign-link-counts"`
- NextCampaigns *sqlx.Stmt `query:"next-campaigns"`
- NextCampaignSubscribers *sqlx.Stmt `query:"next-campaign-subscribers"`
- GetOneCampaignSubscriber *sqlx.Stmt `query:"get-one-campaign-subscriber"`
- UpdateCampaign *sqlx.Stmt `query:"update-campaign"`
- UpdateCampaignStatus *sqlx.Stmt `query:"update-campaign-status"`
- UpdateCampaignCounts *sqlx.Stmt `query:"update-campaign-counts"`
- RegisterCampaignView *sqlx.Stmt `query:"register-campaign-view"`
- DeleteCampaign *sqlx.Stmt `query:"delete-campaign"`
- InsertMedia *sqlx.Stmt `query:"insert-media"`
- GetMedia *sqlx.Stmt `query:"get-media"`
- DeleteMedia *sqlx.Stmt `query:"delete-media"`
- CreateTemplate *sqlx.Stmt `query:"create-template"`
- GetTemplates *sqlx.Stmt `query:"get-templates"`
- UpdateTemplate *sqlx.Stmt `query:"update-template"`
- SetDefaultTemplate *sqlx.Stmt `query:"set-default-template"`
- DeleteTemplate *sqlx.Stmt `query:"delete-template"`
- CreateLink *sqlx.Stmt `query:"create-link"`
- RegisterLinkClick *sqlx.Stmt `query:"register-link-click"`
- GetSettings *sqlx.Stmt `query:"get-settings"`
- 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"`
- }
- // dbConf contains database config required for connecting to a DB.
- type dbConf struct {
- Host string `koanf:"host"`
- Port int `koanf:"port"`
- User string `koanf:"user"`
- Password string `koanf:"password"`
- DBName string `koanf:"database"`
- SSLMode string `koanf:"ssl_mode"`
- MaxOpen int `koanf:"max_open"`
- MaxIdle int `koanf:"max_idle"`
- MaxLifetime time.Duration `koanf:"max_lifetime"`
- }
- // connectDB initializes a database connection.
- func connectDB(c dbConf) (*sqlx.DB, error) {
- db, err := sqlx.Connect("postgres",
- fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s",
- c.Host, c.Port, c.User, c.Password, c.DBName, c.SSLMode))
- if err != nil {
- return nil, err
- }
- db.SetMaxOpenConns(c.MaxOpen)
- db.SetMaxIdleConns(c.MaxIdle)
- db.SetConnMaxLifetime(c.MaxLifetime)
- return db, nil
- }
- // compileSubscriberQueryTpl takes a arbitrary WHERE expressions
- // to filter subscribers from the subscribers table and prepares a query
- // out of it using the raw `query-subscribers-template` query template.
- // While doing this, a readonly transaction is created and the query is
- // dry run on it to ensure that it is indeed readonly.
- func (q *Queries) compileSubscriberQueryTpl(exp string, db *sqlx.DB) (string, error) {
- tx, err := db.BeginTxx(context.Background(), &sql.TxOptions{ReadOnly: true})
- if err != nil {
- return "", err
- }
- defer tx.Rollback()
- // Perform the dry run.
- if exp != "" {
- exp = " AND " + exp
- }
- stmt := fmt.Sprintf(q.QuerySubscribersTpl, exp)
- if _, err := tx.Exec(stmt, true, pq.Int64Array{}); err != nil {
- return "", err
- }
- return stmt, nil
- }
- // compileSubscriberQueryTpl takes a arbitrary WHERE expressions and a subscriber
- // query template that depends on the filter (eg: delete by query, blocklist by query etc.)
- // combines and executes them.
- func (q *Queries) execSubscriberQueryTpl(exp, tpl string, listIDs []int64, db *sqlx.DB, args ...interface{}) error {
- // Perform a dry run.
- filterExp, err := q.compileSubscriberQueryTpl(exp, db)
- if err != nil {
- return err
- }
- if len(listIDs) == 0 {
- listIDs = pq.Int64Array{}
- }
- // First argument is the boolean indicating if the query is a dry run.
- a := append([]interface{}{false, pq.Int64Array(listIDs)}, args...)
- if _, err := db.Exec(fmt.Sprintf(tpl, filterExp), a...); err != nil {
- return err
- }
- return nil
- }
|