diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ae0866a3..3b66614c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1 +1 @@ -All Open Source for Good projects follow Free Code Camp's [contributor's guide](https://github.com/FreeCodeCamp/FreeCodeCamp/blob/staging/CONTRIBUTING.md). +All Open Source for Good projects follow Free Code Camp's [contributor's guide](https://github.com/freeCodeCamp/freeCodeCamp/blob/master/CONTRIBUTING.md). diff --git a/README.md b/README.md index cc14d001..f2fc81d6 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ With Mail for Good you can: ### Performance -We're currently sending weekly email blasts of over 800,000 emails in 4 hours on a $10 per month Digital Ocean VPS with 1gb memory and 1 core processor. +We're currently sending weekly email blasts of over 800,000 emails in 4 hours on a $10 per month Digital Ocean VPS with 1 GB memory and 1 core processor. Mail for Good is fast and scales to the rate limit enforced by AWS. diff --git a/app.json b/app.json index 93faa954..b41439dd 100644 --- a/app.json +++ b/app.json @@ -1,7 +1,7 @@ { "name": "Mail for Good", "description": "An open source email campaign management tool for nonprofits", - "repository": "https://github.com/freeCodeCamp/mail-for-good", + "repository": "https://github.com/saviorand/mail-for-good/tree/heroku/stable", "logo": "https://raw.githubusercontent.com/freeCodeCamp/assets/master/assets/logos/fcc_puck600.png", "keywords": ["email", "nonprofit", "freecodecamp"], "formation": { diff --git a/client/containers/lists/ImportCSV.js b/client/containers/lists/ImportCSV.js index 19102843..c75a8064 100644 --- a/client/containers/lists/ImportCSV.js +++ b/client/containers/lists/ImportCSV.js @@ -46,8 +46,8 @@ export class ImportCSVComponent extends Component { handleNewFile(file) { const callback = results => { - if (!results.meta.fields.some(field => field.toLowerCase() === 'email')) { // Check if any header field is labeled email - this.handleErrorWithFile('Please ensure the CSV file contains at least one column field labeled "email" (check the first row)'); + if (!results.meta.fields.some(field => field.toLowerCase() === 'email' || 'e-mail')) { // Check if any header field is labeled email + this.handleErrorWithFile('Please ensure the CSV file contains at least one column field labeled "email/e-mail" (check the first row)'); } else { const errors = results.errors; diff --git a/docs/aws_deploy.md b/docs/aws_deploy.md index ab2c60c2..ba4fdec9 100644 --- a/docs/aws_deploy.md +++ b/docs/aws_deploy.md @@ -23,7 +23,7 @@ Select Ubuntu as your operating system. ![](resources/deploy_images/5.png) -Here is the preselected instance is fine. (t2.micro instances are eligible for the free tier). +Here the preselected instance is fine. (t2.micro instances are eligible for the free tier). ![](resources/deploy_images/6.png) @@ -235,8 +235,8 @@ In the left menu, select **Dashboard**. Now select **Enable API**, search for `G In the left menu, select **Credentials**. Then click **Create Credentials** > **OAuth client ID**. Select **Web Application**. Name it as you wish - - Under **Authorised Javascript Origins** put the “Public DNS (IPv4)” you received from Amazon earlier. - - Under **Authorised redirect URIs** put your “Public DNS (IPv4)” followed by `/auth/google/callback`. + - Under **Authorized Javascript Origins** put the “Public DNS (IPv4)” you received from Amazon earlier. + - Under **Authorized redirect URIs** put your “Public DNS (IPv4)” followed by `/auth/google/callback`. ![](resources/deploy_images/google_origins.png) @@ -274,7 +274,7 @@ GOOGLE_CONSUMER_KEY= # E.g abcdefQVw1ghijzeaQklmnop GOOGLE_CONSUMER_SECRET= # The URL Google will send you back to after logging in. - # It must match what is under "Authorised redirect URIs" in the Google Dashboard exactly + # It must match what is under "Authorized redirect URIs" in the Google Dashboard exactly GOOGLE_CALLBACK= # A random, preferably long sequence of characters. You do not need to remember this. ENCRYPTION_PASSWORD= diff --git a/docs/docs-hugo-templates/content/google-api-guide.md b/docs/docs-hugo-templates/content/google-api-guide.md index ddf14259..1d988cca 100644 --- a/docs/docs-hugo-templates/content/google-api-guide.md +++ b/docs/docs-hugo-templates/content/google-api-guide.md @@ -6,5 +6,5 @@ date: 2017-09-30T16:56:37-05:00 1. Login to [Google API Manager](https://console.developers.google.com/apis/). 2. In the left menu, select **Dashboard**. Now select **Enable API**, search for `Google+` and select it. At the top of the screen, ensure it's enabled by clicking on **Enable**. 3. In the left menu, select **Credentials**. Then click **Create Credentials** > **OAuth client ID**. -4. Select **Web Application**. Name is as you wish, but under **Authorised Javascript Origins** put `http://localhost:8080`, and under **Authorised redirect URIs** put `http://localhost:8080/auth/google/callback`. -5. Click **Create**. You will now have a Client ID and Client Secret. In your .env file, put the Client ID as your GOOGLE_CONSUMER_KEY, and the Client Secret as your GOOGLE_CONSUMER_SECRET. \ No newline at end of file +4. Select **Web Application**. Name is as you wish, but under **Authorized Javascript Origins** put `http://localhost:8080`, and under **Authorized redirect URIs** put `http://localhost:8080/auth/google/callback`. +5. Click **Create**. You will now have a Client ID and Client Secret. In your .env file, put the Client ID as your GOOGLE_CONSUMER_KEY, and the Client Secret as your GOOGLE_CONSUMER_SECRET. diff --git a/docs/docs-hugo-templates/content/index.md b/docs/docs-hugo-templates/content/index.md index 490c5eff..eee46ffe 100644 --- a/docs/docs-hugo-templates/content/index.md +++ b/docs/docs-hugo-templates/content/index.md @@ -33,7 +33,7 @@ With Mail for Good you can: - Send email campaigns of unlimited size. - Import emails saved in CSV format. - Create templates to reuse for convenience when sending email campaigns. -- Track bounce rate and other standard metrics. You can also insert tracking pixels and unsubcribe links at the click of a button. +- Track bounce rate and other standard metrics. You can also insert tracking pixels and unsubscribe links at the click of a button. - Add custom fields to imported email lists such as names or cities. - Grant other users (limited or otherwise) permissions to use your account on your behalf. - Add embedded HTML newsletter sign up forms to your site. These are snippets of code that will let your users sign up with you at the click of a button. @@ -46,4 +46,4 @@ Mail for Good is fast and scales to the rate limit enforced by AWS. ## Why are we doing this? -We want to help nonprofits manage their email campaigns as inexpensively as possible, and have full control over their data. \ No newline at end of file +We want to help nonprofits manage their email campaigns as inexpensively as possible, and have full control over their data. diff --git a/package.json b/package.json index 635ff648..6f86f39f 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,10 @@ "name": "nonprofit-email-service", "version": "1.0.0", "description": "A cost effective, open source solution to sending mass emails", - "main": "index.js", "engines": { - "npm": "5", - "node": "7.6.0" + "node": "8.x" }, + "main": "index.js", "scripts": { "check-env-file": "python ./tools/setup/initial_setup.py", "client:lint": "esw webpack.config.* client tools --color", @@ -25,7 +24,7 @@ "test:server:functional": "babel-tape-runner -r dotenv/config server/tests/**/*.func.js | node_modules/.bin/tap-spec", "test:client": "mocha --require tools/testClientSetup.js tools/testSetup.js \"client/**/*.spec.js\"", "test": "npm run test:client && npm run test:server", - "updateDB":"node_modules/.bin/sequelize db:migrate --env production" + "updateDB": "node_modules/.bin/sequelize db:migrate --env production" }, "repository": { "type": "git", @@ -41,14 +40,15 @@ "admin-lte": "^2.3.6", "async": "^2.4.1", "aws-sdk": "^2.69.0", - "bcrypt": "^1.0.3", + "bcrypt": "^3.0.0", "bluebird": "^3.5.0", "body-parser": "^1.17.2", - "bootstrap": "^3.3.7", + "bootstrap": "^3.4.1", "bottleneck": "^1.15.1", "connect-history-api-fallback": "^1.3.0", "connect-redis": "^3.3.0", "cookie-parser": "^1.4.3", + "cookie-session": "^1.4.0", "csv": "^1.1.0", "debug": "^2.6.8", "dompurify": "^0.9.0", @@ -57,21 +57,21 @@ "express": "^4.15.3", "express-session": "^1.15.3", "geoip-lite": "^1.2.1", - "handlebars": "^4.0.10", - "helmet": "^3.6.1", + "handlebars": "^4.7.6", + "helmet": "^3.22.0", "highcharts": "^5.0.6", - "jquery": "^3.2.1", - "lodash": "^4.15.0", + "jquery": "^3.5.1", + "lodash": "^4.17.15", "moment": "^2.18.1", "multer": "^1.2.0", "npm-run-all": "^4.0.2", "papaparse": "^4.3.3", "passport": "^0.3.2", - "passport-google-oauth": "^1.0.0", + "passport-google-oauth": "^2.0.0", "passport-local": "^1.0.0", "pg": "^6.2.4", "pug": "^2.0.0-rc.4", - "quill": "^1.2.6", + "quill": "^1.3.7", "react": "^15.6.1", "react-bootstrap": "^0.31.0", "react-bootstrap-table": "^3.4.0", @@ -96,14 +96,14 @@ "sequelize": "^3.24.3", "sequelize-cli": "^4.0.0", "shortid": "^2.2.8", - "slug": "^0.9.1", + "slug": "^0.9.4", "socket.io": "^2.0.3", "sqs-consumer": "^3.6.0", "ua-parser-js": "^0.7.10", "webpack": "^2.6.1" }, "devDependencies": { - "axios": "^0.16.2", + "axios": "^0.19.2", "babel-cli": "^6.24.1", "babel-core": "^6.25.0", "babel-eslint": "^7.2.3", @@ -136,7 +136,7 @@ "mocha": "^3.2.0", "node-amazon-ses-simulator": "^1.0.1", "node-mocks-http": "^1.6.2", - "node-sass": "^4.5.3", + "node-sass": "^4.14.1", "react-addons-test-utils": "^15.6.0", "react-fontawesome": "^1.6.1", "redux-immutable-state-invariant": "^2.0.0", diff --git a/server/config/secrets.js b/server/config/secrets.js index 59111cac..e5112de7 100644 --- a/server/config/secrets.js +++ b/server/config/secrets.js @@ -8,10 +8,9 @@ module.exports = { secretAccessKey: process.env.AMAZON_SECRET_ACCESS_KEY || null } }, - // - + smtpServer: { - port: process.env.SMTP_TEST_PORT || '2025', // Linux envs disallo use of port <= 1024 without root + port: process.env.SMTP_TEST_PORT || '2025', // Linux envs disallow use of port <= 1024 without root host: process.env.SMTP_TEST_HOST || '127.0.0.1' }, @@ -20,5 +19,4 @@ module.exports = { consumerSecret: process.env.GOOGLE_CONSUMER_SECRET, callbackURL: process.env.GOOGLE_CALLBACK || 'http://localhost:8080/auth/google/callback' }, - }; diff --git a/server/config/server/index.js b/server/config/server/index.js index 3e7e5ab0..8e28c5c3 100644 --- a/server/config/server/index.js +++ b/server/config/server/index.js @@ -3,7 +3,7 @@ const path = require('path'); const express = require('express'); const passport = require('passport'); const helmet = require('helmet'); - +const cookieSession = require('cookie-session'); const configureSequelize = require('./sequelize'); const configureWebpackDevMiddleware = require('./webpack-dev-middleware'); const configureRedis = require('./redis'); @@ -23,11 +23,7 @@ module.exports = () => { configureWebpackDevMiddleware(app); // Configure redis, receiving connections to client, subscriber and publisher - const { - client, - subscriber, - publisher, - } = configureRedis(); + const { client, subscriber, publisher } = configureRedis(); // Configure session handling with redis, through the client connection. const { sessionMiddleware } = configureSession(client); @@ -42,6 +38,13 @@ module.exports = () => { app.use(passport.session()); // Use passport middleware for auth app.use(helmet()); // Implements various security tweaks to http response headers + app.use( + cookieSession({ + maxAge: 30 * 24 * 60 * 60 * 1000, + keys: [process.env.COOKIE_SESSION] + }) + ); + app.use('/public', express.static(path.join(__dirname, '../../../public'))); // Serve /public static files when unauth app.use('/dist', express.static(path.join(__dirname, '../../../dist'))); // Serve /dist static diles when auth diff --git a/server/controllers/campaign/email/amazon-ses/send-test.js b/server/controllers/campaign/email/amazon-ses/send-test.js index b4ea2171..399b6050 100644 --- a/server/controllers/campaign/email/amazon-ses/send-test.js +++ b/server/controllers/campaign/email/amazon-ses/send-test.js @@ -106,6 +106,9 @@ module.exports = (req, res) => { ? new AWS.SES({ accessKeyId: accessKey, secretAccessKey: secretKey, region, endpoint: 'http://localhost:9999' }) : new AWS.SES({ accessKeyId: accessKey, secretAccessKey: secretKey, region, apiVersion: '2010-12-01'}); + + //replace all variables by their names to not get their '{}' processed by wrapLink() + campaign.emailBody = campaign.emailBody.replace(/{{.*}}/gm,(match)=>{return match.slice(2,-2);}); // Modify email body for analytics if (campaign.trackLinksEnabled) { campaign.emailBody = wrapLink(campaign.emailBody, 'example-tracking-id', campaign.type, whiteLabelUrl); diff --git a/server/controllers/list/import-csv.js b/server/controllers/list/import-csv.js index 108db802..56327761 100644 --- a/server/controllers/list/import-csv.js +++ b/server/controllers/list/import-csv.js @@ -310,4 +310,4 @@ module.exports = (req, res, io) => { .catch(err => { res.status(400).send({ message: err.message }); }); -}; +}; \ No newline at end of file