Skip to content
Permalink
Browse files
Admin email campaign
  • Loading branch information
Johnny Lin committed Jun 11, 2020
1 parent c74318c commit 91c2ff460a93683e29f40f6a366818a20bb1b0ce
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 1 deletion.
@@ -1,5 +1,7 @@
module.exports = {
AdminUser: require("./models/admin-user-model.js"),
CampaignEmail: require("./models/campaign-email-model.js"),
Campaign: require("./models/campaign-model.js"),
Certificate: require("./models/certificate-model.js"),
ClientFile: require("./models/client-file-model.js"),
Partner: require("./models/partner-model.js"),
@@ -0,0 +1,72 @@
const ConfirmedError = require("../error.js");
const Logger = require("../logger.js");

// Utilities
const Database = require("../utilities/database.js");

class CampaignEmail {

constructor(row) {
if (!row) {
throw new ConfirmedError(400, 999, "Error creating tracker: Null row.");
}
this.id = row.id;
this.campaignId = row.campaign_id;
this.emailEncrypted = row.email_encrypted;
this.unsubscribeCode = row.unsubscribe_code;
this.sent = row.sent;
this.failed = row.failed;
}

static addLockdownEmailsToCampaign(campaign) {
return Database.query(
`INSERT INTO campaign_emails (campaign_id, email_encrypted, unsubscribe_code)
SELECT $1, email_encrypted, newsletter_unsubscribe_code
FROM users
WHERE email_confirmed = true AND do_not_email = false AND lockdown = true AND newsletter_subscribed = true
ON CONFLICT
DO NOTHING`,
[campaign.id])
.catch( error => {
throw new ConfirmedError(400, 99, "Error adding lockdown emails to campaign", error);
});
}

static getUnsentEmailsAndMarkAsSent(campaignId, maxNum) {
return Database.query(
`UPDATE campaign_emails
SET sent = true
WHERE id IN
(SELECT id
FROM campaign_emails
WHERE campaign_id = $1 AND sent = false
LIMIT $2)
RETURNING *`,
[campaignId, maxNum])
.catch( error => {
throw new ConfirmedError(400, 99, "Error getting unsent emails by id", error);
})
.then( result => {
var campaignEmails = [];
result.rows.forEach(campaignEmail => {
campaignEmails.push(new CampaignEmail(campaignEmail));
})
return campaignEmails;
})
}

static setFailed(id) {
return Database.query(
`UPDATE campaign_emails
SET failed = true
WHERE id = $1
RETURNING *`,
[id])
.catch( error => {
Logger.error("Error setting failed for campaign email: ", JSON.stringify(error));
})
}

}

module.exports = CampaignEmail;
@@ -0,0 +1,106 @@
const ConfirmedError = require("../error.js");
const Logger = require("../logger.js");

// Utilities
const Database = require("../utilities/database.js");

class Campaign {

constructor(row) {
if (!row) {
throw new ConfirmedError(400, 999, "Error creating campaign: Null row.");
}
this.id = row.id;
this.name = row.name;
this.fromAddress = row.from_address;
this.subject = row.subject;
this.html = row.html;
this.plaintext = row.plaintext;
this.createDate = new Date(row.create_date);
this.lastSentDate = row.last_sent_date ? new Date(row.last_sent_date) : null
}

static getById(id) {
return Database.query(
`SELECT *
FROM campaigns
WHERE id = $1`,
[id]
)
.catch( error => {
throw new ConfirmedError(400, 99, "Error getting campaign by id", error);
})
.then( result => {
if (result.rows.length === 0) {
throw new ConfirmedError(400, 99, "No such campaign.");
}
return new Campaign(result.rows[0]);
});
}

static getStats(id) {
return Database.query(
`SELECT count(*) AS total,
count(CASE WHEN sent THEN 1 END) as sent,
count(CASE WHEN failed THEN 1 END) as failed
FROM campaign_emails
WHERE campaign_id = $1`,
[id]
)
.catch( error => {
throw new ConfirmedError(400, 99, "Error getting campaign detail", error);
})
.then( result => {
if (result.rows.length === 0) {
throw new ConfirmedError(400, 99, "No such campaign.");
}
return {
total: result.rows[0].total,
sent: result.rows[0].sent,
failed: result.rows[0].failed
}
});
}

static getAll() {
return Database.query(
`SELECT * FROM campaigns`
)
.catch( error => {
throw new ConfirmedError(400, 99, "Error getting campaigns", error);
})
.then( result => {
var campaigns = [];
result.rows.forEach(campaign => {
campaigns.push(new Campaign(campaign));
})
return campaigns;
})
}

static create(name, fromAddress, subject, html, plaintext) {
var toReturn;
return Database.query(
`INSERT INTO campaigns(name, from_address, subject, html, plaintext)
VALUES($1, $2, $3, $4, $5)
RETURNING *`,
[name, fromAddress, subject, html, plaintext])
.catch( error => {
throw new ConfirmedError(400, 14, "Error creating Campaign", error);
})
.then( result => {
toReturn = new Campaign(result.rows[0]);
// add all non-opted-out, confirmed emails to this
return CampaignEmail.addLockdownEmailsToCampaign(toReturn)
})
.then( result => {
return toReturn;
});
}

}

module.exports = Campaign;

// Models - Refer after export to avoid circular/incomplete reference
const CampaignEmail = require("./campaign-email-model.js");
@@ -40,8 +40,11 @@ class User {
this.deleteDate = userRow.delete_date ? new Date(userRow.delete_date) : null;
this.deleteReason = userRow.delete_reason;
this.doNotEmail = userRow.do_not_email;
this.doNotEmailCode = userRow.do_not_email_code;
this.banned = userRow.banned;
this.lockdown = userRow.lockdown;
this.newsletterSubscribed = userRow.newsletter_subscribed;
this.newsletterUnsubscribeCode = userRow.newsletter_unsubscribe_code;
}

get email() {
@@ -923,6 +926,25 @@ class User {
});
}

static setNewsletterUnsubscribe(email, code) {
var emailHashed = Secure.hashSha512(email, EMAIL_SALT);
return Database.query(
`UPDATE users
SET newsletter_subscribed = false
WHERE email = $1 AND newsletter_unsubscribe_code = $2
RETURNING *`,
[emailHashed, code])
.catch( error => {
throw new ConfirmedError(500, 7, "Error setting newsletter unsubscribe", error);
})
.then( result => {
if (result.rowCount !== 1) {
throw new ConfirmedError(400, 89, "Wrong code and/or email for newsletter unsubscribe");
}
return true;
});
}

}

module.exports = User;
@@ -70,7 +70,9 @@ CREATE TABLE users (
banned boolean DEFAULT false NOT NULL,
do_not_email boolean DEFAULT false NOT NULL,
do_not_email_code text DEFAULT upper(substring(md5(random()::text) from 0 for 20)) NOT NULL,
lockdown boolean DEFAULT false NOT NULL
lockdown boolean DEFAULT false NOT NULL,
newsletter_subscribed boolean NOT NULL DEFAULT true,
newsletter_unsubscribe_code text NOT NULL DEFAULT upper("substring"(md5(random()::text), 0, 20))
);

ALTER TABLE ONLY users
@@ -229,6 +231,36 @@ CREATE TABLE reviews_trackers (
tracker_id integer NOT NULL REFERENCES trackers(id) ON DELETE CASCADE
);

/*****************************************************/
/********************* CAMPAIGNS *********************/
/*****************************************************/

CREATE TABLE campaigns (
id SERIAL PRIMARY KEY,
name text NOT NULL,
from_address text NOT NULL,
subject text NOT NULL,
html text NOT NULL,
plaintext text NOT NULL,
create_date timestamp without time zone NOT NULL DEFAULT now(),
last_sent_date timestamp without time zone
);

/*****************************************************/
/****************** CAMPAIGN EMAILS ******************/
/*****************************************************/

CREATE TABLE campaign_emails (
id SERIAL PRIMARY KEY,
campaign_id integer NOT NULL REFERENCES campaigns(id),
email_encrypted text NOT NULL,
unsubscribe_code text NOT NULL,
sent boolean NOT NULL DEFAULT false,
failed boolean NOT NULL DEFAULT false
);

CREATE UNIQUE INDEX campaign_id_email_unique ON campaign_emails(campaign_id int4_ops,email_encrypted text_ops);

/*****************************************************/
/************************ ROLES **********************/
/*****************************************************/

0 comments on commit 91c2ff4

Please sign in to comment.