├── .gitignore ├── README.md ├── app.js ├── bin └── www ├── config.json.dist ├── mailer └── mailer.js ├── package.json ├── public └── stylesheets │ ├── style.css │ └── style.styl ├── routes └── index.js ├── tests └── testRootPost.js └── views ├── error.jade ├── index.jade └── layout.jade /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | config.json 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mailer 2 | ====== 3 | This is a small node js server that can send emails using [nodemailer](https://www.npmjs.com/package/nodemailer). 4 | 5 | ## Running 6 | 7 | `git clone https://github.com/fossasia/mailer.git` 8 | `cd mailer` 9 | 10 | Install dependancies. 11 | 12 | `npm install` 13 | 14 | Visit localhost:3100 on your browser. 15 | 16 | You can actually make it send emails using your gmail account. However it might involve you clearing some security for your account. Proceed at your own risk. 17 | 18 | copy the config files: 19 | 20 | `cp config.json.dist config.json` 21 | 22 | Edit config.json with your gmail username and password. Fire up the mail server again. Send emails! 23 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | 8 | var routes = require('./routes/index').router; 9 | 10 | var flash = require('connect-flash'); 11 | var session = require('express-session'); 12 | 13 | var app = express(); 14 | 15 | // view engine setup 16 | app.set('views', path.join(__dirname, 'views')); 17 | app.set('view engine', 'jade'); 18 | 19 | // uncomment after placing your favicon in /public 20 | //app.use(favicon(__dirname + '/public/favicon.ico')); 21 | app.use(logger('dev')); 22 | app.use(bodyParser.json()); 23 | app.use(bodyParser.urlencoded({ extended: false })); 24 | app.use(cookieParser()); 25 | app.use(session({ 26 | secret: 'keyboard cat', 27 | resave: false, 28 | saveUninitialized: true 29 | })); 30 | app.use(flash()); 31 | app.use(require('stylus').middleware(path.join(__dirname, 'public'))); 32 | app.use(express.static(path.join(__dirname, 'public'))); 33 | 34 | app.use('/', routes); 35 | 36 | // catch 404 and forward to error handler 37 | app.use(function(req, res, next) { 38 | var err = new Error('Not Found'); 39 | err.status = 404; 40 | next(err); 41 | }); 42 | 43 | // error handlers 44 | 45 | // development error handler 46 | // will print stacktrace 47 | if (app.get('env') === 'development') { 48 | app.use(function(err, req, res, next) { 49 | res.status(err.status || 500); 50 | res.render('error', { 51 | message: err.message, 52 | error: err 53 | }); 54 | }); 55 | } 56 | 57 | // production error handler 58 | // no stacktraces leaked to user 59 | app.use(function(err, req, res, next) { 60 | res.status(err.status || 500); 61 | res.render('error', { 62 | message: err.message, 63 | error: {} 64 | }); 65 | }); 66 | 67 | 68 | module.exports = app; 69 | -------------------------------------------------------------------------------- /bin/www: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var debug = require('debug')('mailer'); 3 | var app = require('../app'); 4 | 5 | app.set('port', process.env.PORT || 3100); 6 | 7 | var server = app.listen(app.get('port'), function() { 8 | debug('Express server listening on port ' + server.address().port); 9 | }); 10 | -------------------------------------------------------------------------------- /config.json.dist: -------------------------------------------------------------------------------- 1 | { 2 | "username": "example@gmail.com", 3 | "password": "password" 4 | } 5 | -------------------------------------------------------------------------------- /mailer/mailer.js: -------------------------------------------------------------------------------- 1 | var nodemailer = require('nodemailer'); 2 | var config = require('../config.json'); 3 | 4 | var transporter = nodemailer.createTransport({ 5 | service: 'Gmail', 6 | auth: { 7 | user: config.username, 8 | pass: config.password 9 | } 10 | }); 11 | 12 | exports.send = function(email, callback){ 13 | transporter.sendMail(email, function(error){ 14 | callback(error); 15 | }); 16 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mailer", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/www" 7 | }, 8 | "dependencies": { 9 | "body-parser": "~1.6.6", 10 | "connect-flash": "^0.1.1", 11 | "cookie-parser": "~1.3.2", 12 | "debug": "~1.0.4", 13 | "express": "~4.8.6", 14 | "express-session": "^1.9.3", 15 | "jade": "~1.5.0", 16 | "morgan": "~1.2.3", 17 | "nodemailer": "^1.3.0", 18 | "serve-favicon": "~2.0.1", 19 | "stylus": "0.42.3" 20 | }, 21 | "devDependencies": { 22 | "sinon": "*" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 3em auto 6em; 3 | width: 30em; 4 | } 5 | body, 6 | input, 7 | textarea, 8 | button { 9 | font-size: 100%; 10 | font-family: 'Segoe UI'; 11 | line-height: 1.75; 12 | } 13 | label { 14 | display: block; 15 | margin: 1em 0 0.5em; 16 | } 17 | label span { 18 | display: block; 19 | margin-bottom: 0.5em; 20 | } 21 | input, 22 | textarea { 23 | padding: 1em; 24 | border: thin solid #e6e6e6; 25 | display: block; 26 | width: 100%; 27 | box-sizing: border-box; 28 | } 29 | textarea { 30 | resize: none; 31 | height: 20em; 32 | } 33 | form { 34 | font-size: 125%; 35 | } 36 | button { 37 | background: #87ceeb; 38 | border: 0; 39 | padding: 1em; 40 | color: #fff; 41 | display: block; 42 | cursor: pointer; 43 | width: 100%; 44 | box-sizing: border-box; 45 | margin-top: 1em; 46 | } 47 | .msg { 48 | color: #999; 49 | font-style: italic; 50 | margin-bottom: 1em; 51 | } 52 | article { 53 | margin: 3em 0 0; 54 | } 55 | .heart { 56 | color: #ff69b4; 57 | } 58 | -------------------------------------------------------------------------------- /public/stylesheets/style.styl: -------------------------------------------------------------------------------- 1 | body 2 | margin 3em auto 6em 3 | width 30em 4 | 5 | body, input, textarea, button 6 | font-size 100% 7 | font-family 'Segoe UI' 8 | line-height 1.75 9 | 10 | label 11 | display block 12 | margin 1em 0 .5em 13 | 14 | span 15 | display block 16 | margin-bottom .5em 17 | 18 | input, textarea 19 | padding 1em 20 | border thin solid #e6e6e6 21 | display block 22 | width 100% 23 | box-sizing border-box 24 | 25 | textarea 26 | resize none 27 | height 20em 28 | 29 | form 30 | font-size 125% 31 | 32 | button 33 | background skyblue 34 | border 0 35 | padding 1em 36 | color white 37 | display block 38 | cursor pointer 39 | width 100% 40 | box-sizing border-box 41 | margin-top 1em 42 | 43 | .msg 44 | color #999 45 | font-style italic 46 | margin-bottom 1em 47 | 48 | article 49 | margin 3em 0 0 50 | 51 | .heart 52 | color hotpink -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var flash = require('connect-flash'); 3 | 4 | var mailer = require('../mailer/mailer'); 5 | 6 | var router = express.Router(); 7 | 8 | /* GET home page. */ 9 | router.get('/', function(req, res) { 10 | res.render('index', { flash: req.flash('info') }); 11 | }); 12 | 13 | function rootPost(req, res) { 14 | var email = { 15 | from: 'Fossasia ', 16 | to: req.body.to, 17 | subject: req.body.subject, 18 | text: req.body.message, 19 | }; 20 | 21 | 22 | mailer.send(email, function(error, info){ 23 | 24 | if ( error ) { 25 | req.flash('info', error); 26 | } else { 27 | req.flash('info', 'Email sent sucessfully!'); 28 | } 29 | 30 | 31 | res.render('index', { flash: req.flash('info') }); 32 | }); 33 | } 34 | 35 | router.post('/', rootPost); 36 | 37 | exports.rootPost = rootPost; 38 | exports.router = router; 39 | -------------------------------------------------------------------------------- /tests/testRootPost.js: -------------------------------------------------------------------------------- 1 | var sinon = require('sinon'); 2 | var routes = require('../routes/index'); 3 | var mocha = require('mocha'); 4 | var mailer = require('../mailer/mailer'); 5 | var assert = require('assert'); 6 | 7 | 8 | describe('/ request (post)', function() { 9 | this.timeout(5000) 10 | it('should call mailer with proper dummy body and reach "render"', function(done){ 11 | var testRequest = {}; 12 | 13 | testRequest.body = { 14 | to: 'mail@namanyayg.com', 15 | subject: 'Test', 16 | message: 'Test', 17 | }; 18 | 19 | testRequest.flash = sinon.spy(); 20 | 21 | testResponse = { 22 | render: function() { 23 | console.log( ' -', testRequest.flash.args[0][1] ) 24 | 25 | assert.equal(testRequest.flash.args[0][1], 'Email sent sucessfully!') 26 | done(); 27 | } 28 | }; 29 | 30 | 31 | var mailerSpy = sinon.stub(mailer, 'send', function(arguments, callback) { 32 | // Compares test request body and actual body 33 | assert.equal( arguments.to, testRequest.body.to, 'To field does not match' ); 34 | assert.equal( arguments.subject, testRequest.body.subject, 'Subject does not match' ); 35 | assert.equal( arguments.text, testRequest.body.message, 'Message body does not match' ); 36 | console.log(' - Given arguments match used arguments') 37 | 38 | callback(); 39 | 40 | return false; 41 | }); 42 | 43 | 44 | 45 | routes.rootPost(testRequest, testResponse); 46 | 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /views/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | form(method="POST" action="/") 5 | div.msg= flash 6 | div From: 7 | div fossasia@gmail.com 8 | label 9 | span To: 10 | input(type="email" name="to" placeholder="foo@gmail.com" required) 11 | 12 | label 13 | span Subject: 14 | input(type="text" name="subject" placeholder="Go FOSSASIA!" required) 15 | 16 | label 17 | span Message: 18 | textarea(name="message" required) 19 | 20 | button Send Email 21 | 22 | article 23 | h1 Demo mailer made in Node JS for FOSSASIA 24 | p Made using Express and Nodemailer for FOSSASIA, Google Code In. 25 | p Currently handled through Gmail, can be configured to use other services such as Outlook; or even server's SMPT. 26 | p Source attached (without Gmail authentication). 27 | p Made with for Code-In 28 | -------------------------------------------------------------------------------- /views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title Demo Mailer for FOSSASIA 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | block content --------------------------------------------------------------------------------