Skip to content

Commit

Permalink
ES6 refactor
Browse files Browse the repository at this point in the history
- unit tests!
- separate routers
- separate mail adapters
- SCSS build (update Foundation)
  • Loading branch information
cloakedninjas committed Jan 4, 2019
1 parent 6fd1f87 commit ccd90ad
Show file tree
Hide file tree
Showing 23 changed files with 2,040 additions and 7,632 deletions.
41 changes: 41 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Javascript Node CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
#
version: 2
jobs:
build:
docker:
# specify the version you desire here
- image: circleci/node:10.15

# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
# - image: circleci/mongo:3.4.4

working_directory: ~/repo

steps:
- checkout

# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-

- run: npm install

- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package.json" }}

- run: npm run init-config

# run tests!
- run: npm test


6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/node_modules
/public/components
.idea/
.c9/
/data/storage.json
/data/
/app/config/config.json
.DS_Store
.DS_Store
public/css/app.css
61 changes: 33 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

## About

This is an ExpressJS-based application that provides a simple form for users to sign up to a Secret Santa mailing group.
This is an Express-based application that provides a simple form for users to sign up to a Secret Santa mailing group.
The host can then login and send out the emails, one per person, telling each person who they need to buy a gift for.

Build Status: [![CircleCI](https://circleci.com/gh/cloakedninjas/secret-santa/tree/master.svg?style=shield)](https://circleci.com/gh/cloakedninjas/secret-santa/tree/master)

## Installation

$ git clone https://github.com/cloakedninjas/secret-santa.git --depth 1
`$ git clone https://github.com/cloakedninjas/secret-santa.git --depth 1`

## Usage

Expand All @@ -16,8 +18,7 @@ admin password as well as the default secrets for Session and Cookie storage.

Run the server using:

$ cd secret-santa
$ node index.js
`$ npm start`

## Config

Expand All @@ -31,42 +32,46 @@ The password to access the admin page located at `/admin`

**deadline**

Your chosen deadline, appear on the top left of the screen
Your chosen deadline, appears on the top left of the screen

**spend-limit**

Your chosen purchase allowance, appear on the top left of the screen
Your chosen purchase allowance, appears on the top left of the screen

**email-server**

Configure the email server to send out emails to the participants. Can be either an SMTP server or Mailgun (https://mailgun.com)

If you use Mailgun, `api-key` and `domain` are required:
Currently only Send Grid (https://sendgrid.com/) is supported, sign up for a trial account to send free emails.
Then paste your API key into the config

"email-server": {
"type": "mailgun",
"api-key": "key-abc123def456",
"domain": "sandboxabc123def456.mailgun.org",
"from-address": "santa@example.com"
}

If you choose SMTP, you need to pass in config options to Nodemailer (http://nodemailer.com) via `options`. Here is a sample config:

"email-server": {
"type": "smtp",
"options": {
"service": "Gmail",
"auth": {
"user": "gmail.user@gmail.com",
"pass": "userpass"
}
}
```
"email-server": {
"type": "sendgrid",
"from-address": "santa@example.com",
"options": {
"api-key": "abc-123"
}
}
```

**cookie-secret**

Used for Express's cookie parser middleware

**session-secret**

Used for Express's session middleware
Used for Express's session middleware

## Customizing

All templates (including the email) are stored in `app/views`, and are fully customizable.

- `layout.ejs` is the main template
- `index.ejs` is what users will see when they register

## Other commands

`npm run <cmd>`
- `test` runs tests
- `lint` runs eslint over code
- `css` build and minifies the CSS
1 change: 1 addition & 0 deletions app/adapters/mailgun.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// todo
16 changes: 16 additions & 0 deletions app/adapters/send-grid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
const sgMail = require('@sendgrid/mail');

module.exports = class SendGridAdapter {
constructor (opts, fromAddress) {
this.fromAddress = fromAddress;
sgMail.setApiKey(opts['api-key']);
}

send (messages) {
messages.forEach((message) => {
message.from = this.fromAddress;
});

return sgMail.sendMultiple(messages);
}
};
51 changes: 51 additions & 0 deletions app/adapters/send-grid.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const sinon = require('sinon');
const expect = require('chai').expect;
const sgMail = require('@sendgrid/mail');
const SendGridAdapter = require('../adapters/send-grid');

describe('SendGridAdapter', () => {
let sandbox;

beforeEach(() => {
sandbox = sinon.createSandbox();
});

afterEach(() => {
sandbox.restore();
});

describe('constructor()', () => {
it('should set the API key', () => {
const stub = sandbox.stub(sgMail, 'setApiKey');

new SendGridAdapter({
'api-key': 'test-123'
}, '');

expect(stub.calledWith('test-123')).to.equal(true);
});
});

describe('send()', () => {
it('should send messages', () => {
const stub = sandbox.stub(sgMail, 'sendMultiple');

const adapter = new SendGridAdapter({
'api-key': 'test-123'
}, 'sender@test.com');

adapter.send([
{
to: 'test@example.com'
}
]);

expect(stub.calledWith([
{
to: 'test@example.com',
from: 'sender@test.com'
}
])).to.equal(true);
});
});
});
11 changes: 7 additions & 4 deletions app/config/default.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
{
"title": "Node Secret Santa",
"signup-password": false,
"admin-password": "asda",
"deadline": "Dec 5th",
"spend-limit": "$50",

"email-server": {
"type": "mailgun"
"type": "sendgrid",
"from-address": "santa@thenorthpole.com",

"options": {
"api-key": "REPLACE_ME"
}
},

"cookie-secret": "xxxdsfsdfyyyzzz111",
"session-secret": "zzzxsdfsdfxxcccc222"

}
}
31 changes: 31 additions & 0 deletions app/controllers/admin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const express = require('express');
const app = require('../lib/app');

const router = new express.Router();

router.use(app.ensureLoggedIn);

router.get('/', (req, res) => {
const storage = app.getStorage();

res.render('admin', {
subscribers: storage.subscribers,
alreadySent: app.haveEmailsAlreadySent()
});
});

router.post('/create-send', (req, res) => {
const list = app.createAndSendEmails();
res.render('admin-review-send', {
recipients: list
});
});

router.post('/re-send', (req, res) => {
const list = app.resendRecipientList();
res.render('admin-review-send', {
recipients: list
});
});

module.exports = router;
42 changes: 42 additions & 0 deletions app/controllers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const express = require('express');
const app = require('../lib/app');
const config = app.getConfig();
const router = new express.Router();

router.get('/', function (req, res) {
res.render('index', {
title: config.title,
deadline: config.deadline,
spendLimit: config['spend-limit']
});
});

router.post('/save', function (req, res) {
app.addSubscriber({
name: req.body.name,
email: req.body.email,
colour: req.body.colour,
animal: req.body.animal,
idea: req.body.idea
});

res.render('registered');
});

router.get('/login', app.ensureLoggedIn, function (req, res) {
res.render('login');
});

router.post('/login', app.ensureLoggedIn, function (req, res, next) {
if (req.body.password === config['admin-password']) {
app.initSession(req, res);
res.redirect('/admin');
next();
} else {
res.render('login', {
error: 'Incorrect password'
});
}
});

module.exports = router;
Loading

0 comments on commit ccd90ad

Please sign in to comment.