diff --git a/jordan_bundy/.gitignore b/jordan_bundy/.gitignore
index 83bfdcc..c105170 100644
--- a/jordan_bundy/.gitignore
+++ b/jordan_bundy/.gitignore
@@ -1,6 +1,10 @@
.
..
**/.DS_Store
+../.DS_Store
**/*.swp
node_modules
/db
+/build
+**/test_bundle.js
+**/*bundle.js
diff --git a/jordan_bundy/app/index.html b/jordan_bundy/app/index.html
new file mode 100644
index 0000000..6c44ba6
--- /dev/null
+++ b/jordan_bundy/app/index.html
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+ Welcome, thirsty Developer
+
+
+ {{greeting}}
+
+
+
+
+ -
+
+ {{developer.name}}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jordan_bundy/app/js/developers/controllers/developers_controller.js b/jordan_bundy/app/js/developers/controllers/developers_controller.js
new file mode 100644
index 0000000..1ecdf44
--- /dev/null
+++ b/jordan_bundy/app/js/developers/controllers/developers_controller.js
@@ -0,0 +1,72 @@
+module.exports = function(app) {
+ app.controller('DevelopersController', ['$scope', '$http', function($scope, $http) {
+ $scope.errors = [];
+ $scope.developers = [];
+ $scope.defaults = {language: 'javascript'};
+ $scope.newDeveloper = angular.copy($scope.defaults);
+ $scope.developer = {};
+
+ // create function to resuse same form
+ $scope.editForm = function(developer) {
+ $scope.newDeveloper = {
+ _id: developer._id,
+ name: developer.name,
+ language: developer.language,
+ edit: true,
+ index: $scope.developers.indexOf(developer)
+ };
+ };
+
+ $scope.processForm = function(developer) {
+ developer.edit ? $scope.update(developer) :
+ $scope.create(developer);
+ };
+
+ $scope.cancel = function() {
+ $scope.newDeveloper = null;
+ };
+
+ $scope.getAll = function() {
+ $http.get('/drip/developers')
+ .then(function(res) {
+ console.log(res);
+ $scope.developers = res.data;
+ }, function(res) {
+ console.log(res.data)
+ });
+ };
+
+ $scope.create = function(developer) {
+ $http.post('/drip/developer', developer)
+ .then(function(res) {
+ $scope.developers.push(res.data);
+ $scope.newDeveloper = angular.copy($scope.defaults);
+ }, function(err) {
+ console.log(err);
+ });
+ };
+
+ $scope.update = function(developer) {
+ $scope.developers[developer.index] = developer;
+ $http.put('/drip/developer/' + developer._id, developer)
+ .then(function(res) {
+ console.log('is update');
+ $scope.newDeveloper = angular.copy($scope.defaults);
+ }, function(err) {
+ console.log(err)
+ });
+ };
+
+ $scope.remove = function(developer) {
+ $scope.developers.splice($scope.developers.indexOf(developer), 1);
+ $http.delete('/drip/developer/' + developer._id)
+ .then(function(res) {
+ console.log('developer removed');
+ }, function(err) {
+ console.log(err);
+ $scope.getAll();
+ });
+ };
+ }]);
+};
+
diff --git a/jordan_bundy/app/js/developers/developers.js b/jordan_bundy/app/js/developers/developers.js
new file mode 100644
index 0000000..4512519
--- /dev/null
+++ b/jordan_bundy/app/js/developers/developers.js
@@ -0,0 +1,4 @@
+module.exports = function(app) {
+ require('./controllers/developers_controller')(app);
+};
+
diff --git a/jordan_bundy/app/js/entry.js b/jordan_bundy/app/js/entry.js
new file mode 100644
index 0000000..8e99f9c
--- /dev/null
+++ b/jordan_bundy/app/js/entry.js
@@ -0,0 +1,6 @@
+require('angular/angular');
+var angular = window.angular;
+
+var caffeineApp = angular.module('CaffeineApp', []);
+require('./developers/developers')(caffeineApp);
+
diff --git a/jordan_bundy/app/sass/_base.scss b/jordan_bundy/app/sass/_base.scss
new file mode 100644
index 0000000..bab885a
--- /dev/null
+++ b/jordan_bundy/app/sass/_base.scss
@@ -0,0 +1,430 @@
+/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
+
+/**
+ * 1. Set default font family to sans-serif.
+ * 2. Prevent iOS and IE text size adjust after device orientation change,
+ * without disabling user zoom.
+ */
+
+html {
+ font-family: sans-serif; /* 1 */
+ -ms-text-size-adjust: 100%; /* 2 */
+ -webkit-text-size-adjust: 100%; /* 2 */
+}
+
+/**
+ * Remove default margin.
+ */
+
+body {
+ margin: 0;
+}
+
+/* HTML5 display definitions
+ ========================================================================== */
+
+/**
+ * Correct `block` display not defined for any HTML5 element in IE 8/9.
+ * Correct `block` display not defined for `details` or `summary` in IE 10/11
+ * and Firefox.
+ * Correct `block` display not defined for `main` in IE 11.
+ */
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+menu,
+nav,
+section,
+summary {
+ display: block;
+}
+
+/**
+ * 1. Correct `inline-block` display not defined in IE 8/9.
+ * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
+ */
+
+audio,
+canvas,
+progress,
+video {
+ display: inline-block; /* 1 */
+ vertical-align: baseline; /* 2 */
+}
+
+/**
+ * Prevent modern browsers from displaying `audio` without controls.
+ * Remove excess height in iOS 5 devices.
+ */
+
+audio:not([controls]) {
+ display: none;
+ height: 0;
+}
+
+/**
+ * Address `[hidden]` styling not present in IE 8/9/10.
+ * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22.
+ */
+
+[hidden],
+template {
+ display: none;
+}
+
+/* Links
+ ========================================================================== */
+
+/**
+ * Remove the gray background color from active links in IE 10.
+ */
+
+a {
+ background-color: transparent;
+}
+
+/**
+ * Improve readability of focused elements when they are also in an
+ * active/hover state.
+ */
+
+a:active,
+a:hover {
+ outline: 0;
+}
+
+/* Text-level semantics
+ ========================================================================== */
+
+/**
+ * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
+ */
+
+abbr[title] {
+ border-bottom: 1px dotted;
+}
+
+/**
+ * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
+ */
+
+b,
+strong {
+ font-weight: bold;
+}
+
+/**
+ * Address styling not present in Safari and Chrome.
+ */
+
+dfn {
+ font-style: italic;
+}
+
+/**
+ * Address variable `h1` font-size and margin within `section` and `article`
+ * contexts in Firefox 4+, Safari, and Chrome.
+ */
+
+h1 {
+ font-size: 2em;
+ margin: 0.67em 0;
+}
+
+/**
+ * Address styling not present in IE 8/9.
+ */
+
+mark {
+ background: #ff0;
+ color: #000;
+}
+
+/**
+ * Address inconsistent and variable font size in all browsers.
+ */
+
+small {
+ font-size: 80%;
+}
+
+/**
+ * Prevent `sub` and `sup` affecting `line-height` in all browsers.
+ */
+
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sup {
+ top: -0.5em;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+/* Embedded content
+ ========================================================================== */
+
+/**
+ * Remove border when inside `a` element in IE 8/9/10.
+ */
+
+img {
+ border: 0;
+}
+
+/**
+ * Correct overflow not hidden in IE 9/10/11.
+ */
+
+svg:not(:root) {
+ overflow: hidden;
+}
+
+/* Grouping content
+ ========================================================================== */
+
+/**
+ * Address margin not present in IE 8/9 and Safari.
+ */
+
+figure {
+ margin: 1em 40px;
+}
+
+/**
+ * Address differences between Firefox and other browsers.
+ */
+
+hr {
+ box-sizing: content-box;
+ height: 0;
+}
+
+/**
+ * Contain overflow in all browsers.
+ */
+
+pre {
+ overflow: auto;
+}
+
+/**
+ * Address odd `em`-unit font size rendering in all browsers.
+ */
+
+code,
+kbd,
+pre,
+samp {
+ font-family: monospace, monospace;
+ font-size: 1em;
+}
+
+/* Forms
+ ========================================================================== */
+
+/**
+ * Known limitation: by default, Chrome and Safari on OS X allow very limited
+ * styling of `select`, unless a `border` property is set.
+ */
+
+/**
+ * 1. Correct color not being inherited.
+ * Known issue: affects color of disabled elements.
+ * 2. Correct font properties not being inherited.
+ * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
+ */
+
+button,
+input,
+optgroup,
+select,
+textarea {
+ color: inherit; /* 1 */
+ font: inherit; /* 2 */
+ margin: 0; /* 3 */
+}
+
+/**
+ * Address `overflow` set to `hidden` in IE 8/9/10/11.
+ */
+
+button {
+ overflow: visible;
+}
+
+/**
+ * Address inconsistent `text-transform` inheritance for `button` and `select`.
+ * All other form control elements do not inherit `text-transform` values.
+ * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
+ * Correct `select` style inheritance in Firefox.
+ */
+
+button,
+select {
+ text-transform: none;
+}
+
+/**
+ * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
+ * and `video` controls.
+ * 2. Correct inability to style clickable `input` types in iOS.
+ * 3. Improve usability and consistency of cursor style between image-type
+ * `input` and others.
+ */
+
+button,
+html input[type="button"], /* 1 */
+input[type="reset"],
+input[type="submit"] {
+ -webkit-appearance: button; /* 2 */
+ cursor: pointer; /* 3 */
+}
+
+/**
+ * Re-set default cursor for disabled elements.
+ */
+
+button[disabled],
+html input[disabled] {
+ cursor: default;
+}
+
+/**
+ * Remove inner padding and border in Firefox 4+.
+ */
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+
+/**
+ * Address Firefox 4+ setting `line-height` on `input` using `!important` in
+ * the UA stylesheet.
+ */
+
+input {
+ line-height: normal;
+}
+
+/**
+ * It's recommended that you don't attempt to style these elements.
+ * Firefox's implementation doesn't respect box-sizing, padding, or width.
+ *
+ * 1. Address box sizing set to `content-box` in IE 8/9/10.
+ * 2. Remove excess padding in IE 8/9/10.
+ */
+
+input[type="checkbox"],
+input[type="radio"] {
+ box-sizing: border-box; /* 1 */
+ padding: 0; /* 2 */
+}
+
+/**
+ * Fix the cursor style for Chrome's increment/decrement buttons. For certain
+ * `font-size` values of the `input`, it causes the cursor style of the
+ * decrement button to change from `default` to `text`.
+ */
+
+input[type="number"]::-webkit-inner-spin-button,
+input[type="number"]::-webkit-outer-spin-button {
+ height: auto;
+}
+
+/**
+ * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
+ * 2. Address `box-sizing` set to `border-box` in Safari and Chrome.
+ */
+
+input[type="search"] {
+ -webkit-appearance: textfield; /* 1 */
+ box-sizing: content-box; /* 2 */
+}
+
+/**
+ * Remove inner padding and search cancel button in Safari and Chrome on OS X.
+ * Safari (but not Chrome) clips the cancel button when the search input has
+ * padding (and `textfield` appearance).
+ */
+
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+/**
+ * Define consistent border, margin, and padding.
+ */
+
+fieldset {
+ border: 1px solid #c0c0c0;
+ margin: 0 2px;
+ padding: 0.35em 0.625em 0.75em;
+}
+
+/**
+ * 1. Correct `color` not being inherited in IE 8/9/10/11.
+ * 2. Remove padding so people aren't caught out if they zero out fieldsets.
+ */
+
+legend {
+ border: 0; /* 1 */
+ padding: 0; /* 2 */
+}
+
+/**
+ * Remove default vertical scrollbar in IE 8/9/10/11.
+ */
+
+textarea {
+ overflow: auto;
+}
+
+/**
+ * Don't inherit the `font-weight` (applied by a rule above).
+ * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
+ */
+
+optgroup {
+ font-weight: bold;
+}
+
+/* Tables
+ ========================================================================== */
+
+/**
+ * Remove most spacing between table cells.
+ */
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+td,
+th {
+ padding: 0;
+}
+
+li {
+ list-style-type: none;
+ margin: 5px;
+}
+
diff --git a/jordan_bundy/app/sass/_layout.scss b/jordan_bundy/app/sass/_layout.scss
new file mode 100644
index 0000000..d0b8ea0
--- /dev/null
+++ b/jordan_bundy/app/sass/_layout.scss
@@ -0,0 +1,49 @@
+$space: 5px;
+
+.app {
+ width: 600px;
+ margin: auto;
+ @media (max-width: 600px) {
+ width: 100%;
+ }
+}
+
+.center-column {
+ display: flex;
+ flex-direction: column;
+}
+
+
+.common {
+ padding: $space;
+}
+
+.fields {
+ display: flex;
+ @media (max-width: 600px) {
+ flex-direction: column;
+ width: 80%;
+ }
+}
+
+.fields label {
+ margin: $space;
+}
+
+.form button {
+ margin: $space;
+}
+
+.list {
+ display: flex;
+ width: 80%
+}
+
+.list-item {
+ flex-grow: 3;
+}
+
+.list-buttons {
+ justify-content: flex-end;
+}
+
diff --git a/jordan_bundy/app/sass/_module.scss b/jordan_bundy/app/sass/_module.scss
new file mode 100644
index 0000000..95ea36c
--- /dev/null
+++ b/jordan_bundy/app/sass/_module.scss
@@ -0,0 +1,17 @@
+$blue: #0066ff;
+$gradient: #112233;
+
+.form {
+ box-shadow: 3px 3px 3px #ccc;
+}
+
+.form h2 {
+ background: $blue;
+}
+
+// create a series of multi-colored backgrounds
+@for $i from 1 to 10 {
+ .background#{$i} {
+ background-color: $gradient * $i
+ }
+}
diff --git a/jordan_bundy/app/sass/application.scss b/jordan_bundy/app/sass/application.scss
new file mode 100644
index 0000000..294f70d
--- /dev/null
+++ b/jordan_bundy/app/sass/application.scss
@@ -0,0 +1,3 @@
+@import "base";
+@import "layout";
+@import "module";
diff --git a/jordan_bundy/gulpfile.js b/jordan_bundy/gulpfile.js
index af1e4ce..524f12a 100644
--- a/jordan_bundy/gulpfile.js
+++ b/jordan_bundy/gulpfile.js
@@ -1,6 +1,13 @@
var gulp = require('gulp');
var jshint = require('gulp-jshint');
var mocha = require('gulp-mocha');
+var webpack = require('webpack-stream');
+var concatCss = require('gulp-concat-css');
+var minifyCss = require('gulp-minify-css');
+var sass = require('gulp-sass');
+var maps = require('gulp-sourcemaps');
+
+
var appFiles = ['index.js', 'lib/**/*.js'];
var testFiles = ['test/**/*.js'];
@@ -32,6 +39,47 @@ gulp.task('mocha:test', function() {
read: false,
reporter: 'nyan'
}))
+ .once('end', function() {
+ console.log(process.exit);
+ })
+});
+
+gulp.task('css:sass', function() {
+ return gulp.src('./app/sass/**/*.scss')
+ .pipe(maps.init())
+ .pipe(sass().on('error', sass.logError))
+ .pipe(minifyCss())
+ .pipe(maps.write('./'))
+ .pipe(gulp.dest('build/'))
+});
+
+gulp.task('css:watch', function() {
+ gulp.watch(['./app/sass/**/*.scss', './app/index.html'], ['css:sass', 'static:dev']);
+});
+
+gulp.task('static:dev', function() {
+ gulp.src('app/**/*.html')
+ .pipe(gulp.dest('build/'));
+});
+
+gulp.task('webpack:dev', function() {
+ gulp.src('app/js/entry.js')
+ .pipe(webpack({
+ output: {
+ filename: 'bundle.js'
+ }
+ }))
+ .pipe(gulp.dest('build/'));
+});
+
+gulp.task('webpack:test', function() {
+ return gulp.src('test/client/test_entry.js')
+ .pipe(webpack({
+ output: {
+ filename: 'test_bundle.js'
+ }
+ }))
+ .pipe(gulp.dest('test/client/'));
});
gulp.task('watch', function() {
@@ -40,5 +88,6 @@ gulp.task('watch', function() {
gulp.task('jshint', ['jshint:test', 'jshint:app']);
gulp.task('mocha', ['mocha:test']);
-gulp.task('default', ['jshint', 'mocha', 'watch']);
+gulp.task('build:dev', ['webpack:dev', 'static:dev']);
+gulp.task('default', ['jshint', 'mocha', 'watch', 'build:dev']);
diff --git a/jordan_bundy/karma.conf.js b/jordan_bundy/karma.conf.js
new file mode 100644
index 0000000..11e3e77
--- /dev/null
+++ b/jordan_bundy/karma.conf.js
@@ -0,0 +1,69 @@
+// Karma configuration
+// Generated on Thu Dec 03 2015 10:03:14 GMT-0800 (PST)
+
+module.exports = function(config) {
+ config.set({
+
+ // base path that will be used to resolve all patterns (eg. files, exclude)
+ basePath: '',
+
+
+ // frameworks to use
+ // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
+ frameworks: ['jasmine'],
+
+
+ // list of files / patterns to load in the browser
+ files: [
+ './test/client/test_bundle.js'
+ ],
+
+
+ // list of files to exclude
+ exclude: [
+ ],
+
+
+ // preprocess matching files before serving them to the browser
+ // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
+ preprocessors: {
+ },
+
+
+ // test results reporter to use
+ // possible values: 'dots', 'progress'
+ // available reporters: https://npmjs.org/browse/keyword/karma-reporter
+ reporters: ['progress'],
+
+
+ // web server port
+ port: 9876,
+
+
+ // enable / disable colors in the output (reporters and logs)
+ colors: true,
+
+
+ // level of logging
+ // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
+ logLevel: config.LOG_INFO,
+
+
+ // enable / disable watching file and executing tests whenever any file changes
+ autoWatch: true,
+
+
+ // start these browsers
+ // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
+ browsers: ['PhantomJS'],
+
+
+ // Continuous Integration mode
+ // if true, Karma captures browsers, runs the tests and exits
+ singleRun: true,
+
+ // Concurrency level
+ // how many browser should be started simultanous
+ concurrency: Infinity
+ })
+}
diff --git a/jordan_bundy/lib/basic_auth.js b/jordan_bundy/lib/basic_auth.js
new file mode 100644
index 0000000..bf0da62
--- /dev/null
+++ b/jordan_bundy/lib/basic_auth.js
@@ -0,0 +1,15 @@
+module.exports = function(req, res, next) {
+ try {
+ var authArray = new Buffer(req.headers.authorization.split(' ')[1], 'base64')
+ .toString()
+ .split(':');
+ req.auth = {
+ username: authArray[0],
+ password: authArray[1]
+ };
+ next();
+ } catch(e) {
+ return res.status(401).json({msg: 'toats errrs'});
+ }
+};
+
diff --git a/jordan_bundy/lib/eat_auth.js b/jordan_bundy/lib/eat_auth.js
new file mode 100644
index 0000000..4d93c85
--- /dev/null
+++ b/jordan_bundy/lib/eat_auth.js
@@ -0,0 +1,17 @@
+var eat = require('eat');
+var User = require(__dirname + '/../models/userSchema');
+
+module.exports = function(req, res, next) {
+ var token = req.headers.token || (req.body) ? req.body.token : '';
+ if (!token) return res.status(401).json({msg: 'You must log in'});
+ eat.decode(token, process.env.APP_SECRET, function (err, decoded) {
+ if (err) return res.status(401).json({msg: 'Invalid'});
+
+ User.findOne({_id: decoded.id}, function(err, user) {
+ if (err) return res.status(401).json({msg: 'Invalid'});
+ req.user = user;
+ next();
+ });
+ });
+};
+
diff --git a/jordan_bundy/models/user.js b/jordan_bundy/models/user.js
new file mode 100644
index 0000000..24566a4
--- /dev/null
+++ b/jordan_bundy/models/user.js
@@ -0,0 +1,41 @@
+var mongoose = require('mongoose');
+var bcrypt = require('bcrypt');
+var eat = require('eat');
+
+var userSchema = new mongoose.Schema({
+ username: String,
+ auth: {
+ basic: {
+ username: String,
+ password: String
+ }
+ }
+});
+
+userSchema.methods.hashPassword = function(password, cb) {
+ this.username = this.auth.basic.username;
+ bcrypt.genSalt(10, function(err, salt) {
+ if (err) throw err;
+ return bcrypt.hash(password, salt, function(err, hash) {
+ if (err) throw err;
+ this.auth.basic.password = hash;
+ cb();
+ }.bind(this));
+ }.bind(this));
+};
+
+userSchema.methods.checkPassword = function(password, cb) {
+ bcrypt.compare(password, this.auth.basic.password, function(err, res) {
+ if (err) throw err;
+ cb(res);
+ });
+};
+
+userSchema.methods.generateToken = function(cb) {
+ var id = this._id;
+ eat.encode({id:id}, process.env.APP_SECRET, cb);
+};
+
+var User = mongoose.model('User', userSchema);
+module.exports = User;
+
diff --git a/jordan_bundy/modules.css b/jordan_bundy/modules.css
new file mode 100644
index 0000000..e69de29
diff --git a/jordan_bundy/package.json b/jordan_bundy/package.json
index 3ddedcf..b16cb27 100644
--- a/jordan_bundy/package.json
+++ b/jordan_bundy/package.json
@@ -9,12 +9,27 @@
"author": "",
"license": "ISC",
"dependencies": {
+ "bcrypt": "^0.8.5",
"body-parser": "^1.14.1",
+ "eat": "^0.1.1",
"express": "^4.13.3",
"mongoose": "^4.2.5"
},
"devDependencies": {
+ "angular": "^1.4.8",
+ "angular-mocks": "^1.4.8",
+ "gulp": "^3.9.0",
+ "gulp-concat-css": "^2.2.0",
"gulp-jshint": "^1.12.0",
- "gulp-mocha": "^2.1.3"
+ "gulp-minify-css": "^1.2.2",
+ "gulp-mocha": "^2.1.3",
+ "gulp-sass": "^2.1.0",
+ "gulp-sourcemaps": "^1.6.0",
+ "jasmine-core": "^2.4.0",
+ "karma": "^0.13.15",
+ "karma-jasmine": "^0.3.6",
+ "karma-phantomjs-launcher": "^0.2.1",
+ "phantomjs": "^1.9.19",
+ "webpack-stream": "^2.1.1"
}
}
diff --git a/jordan_bundy/routes/auth_routes.js b/jordan_bundy/routes/auth_routes.js
new file mode 100644
index 0000000..703d27c
--- /dev/null
+++ b/jordan_bundy/routes/auth_routes.js
@@ -0,0 +1,49 @@
+var express = require('express');
+var bodyParser = require('body-parser').json();
+var errHandle = require(__dirname + '/../lib/handleErr');
+var basicAuth = require(__dirname + '/../lib/basic_auth');
+var User = require(__dirname + '/../models/user');
+
+var authRouter = module.exports = express.Router();
+
+authRouter.post('/signup', bodyParser, function(req, res) {
+
+ User.findOne({username: req.body.username}, createUser)
+ function createUser(err, user) {
+ if (err) throw err;
+ if (user) return res.json({msg: 'already taken'});
+
+ var user = new User();
+ user.auth.basic.username = req.body.username;
+ user.hashPassword(req.body.password, function() {
+
+ user.save(function(err, data) {
+ if (err) return errHandle(err, res);
+
+ user.generateToken(function(err, token) {
+ if (err) return errHandle(err, res);
+ res.json({token: token});
+ });
+ });
+ });
+ };
+});
+
+authRouter.get('/signin', basicAuth, function (req, res) {
+ if (!(req.auth.username && req.auth.password)) {
+ return res.status(401).json({msg: 'nothing sent'});
+ }
+ User.findOne({'auth.basic.username': req.auth.username}, function (err, user) {
+ if (err) return res.status(401).json({msg: 'big ol err'});
+ if (!user) return res.status(401).json({msg: 'you arent here'});
+ user.checkPassword(req.auth.password, function(check) {
+
+ if (!check) return res.status(401).json({msg: 'fix'});
+ user.generateToken(function(err, token) {
+ if (err) errHandle(err, res);
+ res.json({token: token});
+ });
+ });
+ });
+});
+
diff --git a/jordan_bundy/routes/caffeine_routes.js b/jordan_bundy/routes/caffeine_routes.js
index 8a1a548..4bdb457 100644
--- a/jordan_bundy/routes/caffeine_routes.js
+++ b/jordan_bundy/routes/caffeine_routes.js
@@ -1,5 +1,5 @@
var express = require('express');
-var bodyParser = require('body-parser');
+var jsonParse = require('body-parser').json();
var Caffeine = require(__dirname + '/../models/caffeine');
var handleErr = require(__dirname + '/../lib/handleErr');
@@ -12,7 +12,7 @@ caffeineRouter.get('/caffeine/:type', function(req, res) {
})
});
-caffeineRouter.post('/caffeine', bodyParser.json(), function(req, res) {
+caffeineRouter.post('/caffeine', jsonParse, function(req, res) {
var caffUnit = new Caffeine(req.body);
caffUnit.save(function(err, data) {
if (err) return handleErr(err, res);
@@ -20,3 +20,7 @@ caffeineRouter.post('/caffeine', bodyParser.json(), function(req, res) {
})
});
+//caffeineRouter.put('/caffeine/:id', jsonParse, function(req, res) {
+
+//});
+
diff --git a/jordan_bundy/routes/developer_routes.js b/jordan_bundy/routes/developer_routes.js
index e9f61cb..2686b30 100644
--- a/jordan_bundy/routes/developer_routes.js
+++ b/jordan_bundy/routes/developer_routes.js
@@ -6,8 +6,15 @@ var Caffeine = require(__dirname + '/../models/caffeine');
var developerRouter = module.exports = express.Router();
-developerRouter.get('/developer/:name', function(req, res) {
- Developer.find({name: req.params.name}, function(err, data) {
+developerRouter.get('/developers/', function(req, res) {
+ Developer.find({}, function(err, data) {
+ if (err) return handleErr(err, res);
+ res.json(data);
+ });
+});
+
+developerRouter.get('/developer/:id', function(req, res) {
+ Developer.find({_id: req.params.id}, function(err, data) {
if (err) return handleErr(err, res);
res.json(data);
})
@@ -21,6 +28,22 @@ developerRouter.post('/developer', bodyParser.json(), function(req, res) {
})
});
+developerRouter.put('/developer/:id', bodyParser.json(), function(req,res) {
+ var devPerson = req.body;
+ delete devPerson._id;
+ Developer.update({_id: req.params.id}, devPerson, function(err) {
+ if (err) return handleErr(err, res);
+ res.json({msg: 'success'});
+ });
+});
+
+developerRouter.delete('/developer/:id', function(req, res) {
+ Developer.remove({_id: req.params.id}, function(err) {
+ if (err) return handleErr(err, res);
+ res.json({msg: 'success'});
+ });
+});
+
developerRouter.put('/developer/caffeine/:name', function(req, res) {
Developer.update({name: req.params.name}, {caffeine: 1}, function(err, data) {
if (err) return handleErr(err, res);
@@ -43,3 +66,4 @@ developerRouter.delete('/developer/:name', function(req, res) {
res.json({info: 'dead'});
})
});
+
diff --git a/jordan_bundy/server.js b/jordan_bundy/server.js
index 4d4975b..72463dd 100644
--- a/jordan_bundy/server.js
+++ b/jordan_bundy/server.js
@@ -2,10 +2,30 @@ var mongoose = require('mongoose');
var app = require('express')();
var developerRouter = require(__dirname + '/routes/developer_routes');
var caffeineRouter = require(__dirname + '/routes/caffeine_routes');
+var authRouter = require(__dirname + '/routes/auth_routes');
+var fs = require('fs');
+process.env.APP_SECRET = process.env.APP_SECRET || 'justatest';
mongoose.connect(process.env.MONGOLAB_URI || 'mongodb://localhost/caffeine_dev');
+app.get('/', function(req, res) {
+ res.sendFile(__dirname + '/app/index.html');
+});
+
app.use('/drip', developerRouter, caffeineRouter);
+app.use(authRouter);
+app.get('/:filename', function(req, res, next) {
+ fs.stat(__dirname + '/build/' + req.params.filename, function(err, stats) {
+ if (err) {
+ console.log(err);
+ return next();
+ }
+ if (!stats.isFile()) return next();
+ var file = fs.createReadStream(__dirname + '/build/' + req.params.filename);
+ file.pipe(res);
+ });
+});
+
app.get('/drip', function(req, res) {
res.writeHead(200, {'content-type': 'text/plain'});
diff --git a/jordan_bundy/stat.css b/jordan_bundy/stat.css
new file mode 100644
index 0000000..e69de29
diff --git a/jordan_bundy/test/auth_test.js b/jordan_bundy/test/auth_test.js
new file mode 100644
index 0000000..62c52c1
--- /dev/null
+++ b/jordan_bundy/test/auth_test.js
@@ -0,0 +1,60 @@
+var chai = require('chai');
+var chaihttp = require('chai-http');
+chai.use(chaihttp);
+var expect = chai.expect;
+
+process.env.MONGOLAB_URI = 'mongodb://localhost/caffeine_test';
+process.env.APP_SECRET = 'hello';
+require(__dirname + '/../server');
+var mongoose = require('mongoose');
+
+describe('The Auth routes', function() {
+
+ before(function(done) {
+ chai.request('localhost:3000')
+ .post('/signup')
+ .send({username:'test',password:'nopass'})
+ .end(function(err, res) {
+ done();
+ });
+ });
+ after(function(done) {
+ mongoose.connection.db.dropDatabase(function() {
+ done();
+ });
+ });
+
+ it('should receive status code 200', function(done) {
+ chai.request('localhost:3000')
+ .post('/signup')
+ .send({username:'jordan',password:'secret123'})
+ .end(function(err, res) {
+ expect(err).to.eql(null);
+ expect(res.body).to.have.property('token');
+ done();
+ });
+ });
+
+ it('should only allow unique usernames', function(done) {
+ chai.request('localhost:3000')
+ .post('/signup')
+ .send({username:'test',password:'whatever'})
+ .end(function(err, res) {
+ expect(err).to.eql(null);
+ expect(res.body).to.have.property('msg');
+ done();
+ });
+ });
+
+ it('should log a user in', function(done) {
+ chai.request('localhost:3000')
+ .get('/signin')
+ .auth('test', 'nopass')
+ .end(function(err, res) {
+ expect(err).to.eql(null);
+ expect(res.body).to.have.property('token');
+ done();
+ });
+ });
+});
+
diff --git a/jordan_bundy/test/client/developers_controller_test.js b/jordan_bundy/test/client/developers_controller_test.js
new file mode 100644
index 0000000..ee6bc41
--- /dev/null
+++ b/jordan_bundy/test/client/developers_controller_test.js
@@ -0,0 +1,79 @@
+require(__dirname + '/../../app/js/entry');
+require('angular-mocks');
+
+describe('developer controller', function() {
+ var $httpBackend;
+ var $ControllerConstructor;
+ var $scope;
+
+ beforeEach(angular.mock.module('CaffeineApp'));
+
+ beforeEach(angular.mock.inject(function($rootScope, $controller) {
+ $scope = $rootScope.$new();
+ $ControllerConstructor = $controller;
+ $ControllerConstructor('DevelopersController', {$scope: $scope});
+ }));
+
+ it('should be able to run', function() {
+ var controller = new $ControllerConstructor('DevelopersController', {$scope: $scope});
+ expect(typeof $scope).toBe('object');
+ expect(typeof controller).toBe('object');
+ expect(Array.isArray($scope.developers)).toBe(true);
+ });
+
+ describe('rest requests', function() {
+ var developer = {_id: 1, name: 'Mike', language: 'love'};
+ var developer2 = {_id: 2, name: 'Gregory', language: 'espanol'};
+
+ beforeEach(angular.mock.inject(function(_$httpBackend_, $rootScope) {
+ $httpBackend = _$httpBackend_;
+ //$ControllerConstructor('DevelopersController', {$scope: $scope})
+ }));
+
+ afterEach(function() {
+ $httpBackend.verifyNoOutstandingExpectation();
+ $httpBackend.verifyNoOutstandingRequest();
+ });
+
+ it('should add an array to developers with a GET all', function() {
+ $httpBackend.expectGET('/drip/developers').respond(200, [{_id: 1, name: 'TJ'}]);
+ $scope.getAll();
+ $httpBackend.flush();
+ expect($scope.developers[0].name).toBe('TJ');
+ });
+
+ it('should be able to create new Developer', function() {
+ $httpBackend.expectPOST('/drip/developer').respond(200, {
+ _id: 1,
+ name: 'Mark',
+ language: 'javascript'
+ });
+ expect($scope.developers.length).toBe(0);
+ $scope.newDeveloper.name = 'not Mark';
+ $scope.create($scope.newDeveloper);
+ $httpBackend.flush();
+ console.log($scope.developers[0].name);
+ expect($scope.developers[0].name).toBe('Mark');
+ });
+
+ it('should delete a bear', function() {
+ $httpBackend.expectDELETE('/drip/developer/1').respond(200, {});
+ $scope.developers.push(developer, developer2);
+ expect($scope.developers[0].name).toBe('Mike');
+ $scope.remove(developer);
+ $httpBackend.flush();
+ expect($scope.developers[0].name).toBe('Gregory');
+ });
+
+ it('should edit a bear', function() {
+ developer2.index = 0;
+ $httpBackend.expectPUT('/drip/developer/2').respond(200, {});
+ $scope.developers.push(developer);
+ expect($scope.developers[0].name).toBe('Mike');
+ $scope.update(developer2);
+ $httpBackend.flush();
+ expect($scope.developers[0].name).toBe('Gregory');
+ });
+ });
+});
+
diff --git a/jordan_bundy/test/client/test_entry.js b/jordan_bundy/test/client/test_entry.js
new file mode 100644
index 0000000..2a15a2d
--- /dev/null
+++ b/jordan_bundy/test/client/test_entry.js
@@ -0,0 +1,2 @@
+require('./developers_controller_test');
+
diff --git a/jordan_bundy/test/mongo_test.js b/jordan_bundy/test/mongo_test.js
index db43c86..3553462 100644
--- a/jordan_bundy/test/mongo_test.js
+++ b/jordan_bundy/test/mongo_test.js
@@ -6,8 +6,6 @@ chai.use(chaihttp);
process.env.MONGOLAB_URI = 'mongodb://localhost/caffeine_test';
require(__dirname + '/../server');
var mongoose = require('mongoose');
-var Developer = require(__dirname + '/../models/developer');
-var Caffeine = require(__dirname + '/../models/caffeine');
describe('developer caffeine routes', function() {
after(function(done) {