Cleanup
This commit is contained in:
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2013 Sahat Yalkabov
|
Copyright (c) 2013 <Author>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
157
README.md
157
README.md
@ -1,157 +0,0 @@
|
|||||||
# MEAN Stack
|
|
||||||
|
|
||||||
MEAN is a boilerplate that provides a nice starting point for [MongoDB](http://www.mongodb.org/), [Node.js](http://www.nodejs.org/), [Express](http://expressjs.com/), and [AngularJS](http://angularjs.org/) based applications. It is designed to give you quick and organized way to start developing of MEAN based web apps with useful modules like mongoose and passport pre-bundled and configured. We mainly try to take care of the connection points between existing popular frameworks and solve common integration problems.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
* Node.js - Download and Install [Node.js](http://www.nodejs.org/download/). You can also follow [this gist](https://gist.github.com/isaacs/579814) for a quick and easy way to install Node.js and npm
|
|
||||||
* MongoDB - Download and Install [MongoDB](http://www.mongodb.org/downloads) - Make sure it's running on the default port (27017).
|
|
||||||
|
|
||||||
### Tools Prerequisites
|
|
||||||
* NPM - Node.js package manager, should be installed when you install node.js.
|
|
||||||
* Bower - Web package manager, installing [Bower](http://bower.io/) is simple when you have npm:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ npm install -g bower
|
|
||||||
```
|
|
||||||
|
|
||||||
### Optional
|
|
||||||
* Grunt - Download and Install [Grunt](http://gruntjs.com).
|
|
||||||
|
|
||||||
## Additional Packages
|
|
||||||
* Express - Defined as npm module in the [package.json](package.json) file.
|
|
||||||
* Mongoose - Defined as npm module in the [package.json](package.json) file.
|
|
||||||
* Passport - Defined as npm module in the [package.json](package.json) file.
|
|
||||||
* AngularJS - Defined as bower module in the [bower.json](bower.json) file.
|
|
||||||
* Twitter Bootstrap - Defined as bower module in the [bower.json](bower.json) file.
|
|
||||||
* UI Bootstrap - Defined as bower module in the [bower.json](bower.json) file.
|
|
||||||
|
|
||||||
## Quick Install
|
|
||||||
The quickest way to get started with MEAN is to clone the project and utilize it like this:
|
|
||||||
|
|
||||||
Install dependencies:
|
|
||||||
|
|
||||||
$ npm install
|
|
||||||
|
|
||||||
We recommend using [Grunt](https://github.com/gruntjs/grunt-cli) to start the server:
|
|
||||||
|
|
||||||
$ grunt
|
|
||||||
|
|
||||||
When not using grunt you can use:
|
|
||||||
|
|
||||||
$ node server
|
|
||||||
|
|
||||||
Then open a browser and go to:
|
|
||||||
|
|
||||||
http://localhost:3000
|
|
||||||
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
During install some of you may encounter some issues, most of this issues can be solved by one of the following tips.
|
|
||||||
If you went through all this and still can't solve the issue, feel free to contact me(Amos), via the repository issue tracker or the links provided below.
|
|
||||||
|
|
||||||
#### Update NPM, Bower or Grunt
|
|
||||||
Sometimes you may find there is a weird error during install like npm's *Error: ENOENT*, usually updating those tools to the latest version solves the issue.
|
|
||||||
|
|
||||||
Updating NPM:
|
|
||||||
```
|
|
||||||
$ npm update -g npm
|
|
||||||
```
|
|
||||||
|
|
||||||
Updating Grunt:
|
|
||||||
```
|
|
||||||
$ npm update -g grunt-cli
|
|
||||||
```
|
|
||||||
|
|
||||||
Updating Bower:
|
|
||||||
```
|
|
||||||
$ npm update -g bower
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Cleaning NPM and Bower cache
|
|
||||||
NPM and Bower has a caching system for holding packages that you already installed.
|
|
||||||
We found that often cleaning the cache solves some troubles this system creates.
|
|
||||||
|
|
||||||
NPM Clean Cache:
|
|
||||||
```
|
|
||||||
$ npm cache clean
|
|
||||||
```
|
|
||||||
|
|
||||||
Bower Clean Cache:
|
|
||||||
```
|
|
||||||
$ bower cache clean
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
All configuration is specified in the [config](config/) folder, particularly the [config.js](config/config.js) file and the [env](config/env/) files. Here you will need to specify your application name, database name, as well as hook up any social app keys if you want integration with Twitter, Facebook, GitHub or Google.
|
|
||||||
|
|
||||||
### Environmental Settings
|
|
||||||
|
|
||||||
There are three environments provided by default, __development__, __test__, and __production__. Each of these environments has the following configuration options:
|
|
||||||
* __db__ - This is the name of the MongoDB database to use, and is set by default to __mean-dev__ for the development environment.
|
|
||||||
* __app.name__ - This is the name of your app or website, and can be different for each environment. You can tell which environment you are running by looking at the TITLE attribute that your app generates.
|
|
||||||
* __Social OAuth Keys__ - Facebook, GitHub, Google, Twitter. You can specify your own social application keys here for each platform:
|
|
||||||
* __clientID__
|
|
||||||
* __clientSecret__
|
|
||||||
* __callbackURL__
|
|
||||||
|
|
||||||
To run with a different environment, just specify NODE_ENV as you call grunt:
|
|
||||||
|
|
||||||
$ NODE_ENV=test grunt
|
|
||||||
|
|
||||||
If you are using node instead of grunt, it is very similar:
|
|
||||||
|
|
||||||
$ NODE_ENV=test node server
|
|
||||||
|
|
||||||
> NOTE: Running Node.js applications in the __production__ environment enables caching, which is disabled by default in all other environments.
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
We pre-included an article example, check it out:
|
|
||||||
* [The Model](https://github.com/linnovate/mean/blob/master/app/models/article.js) - Where we define our object schema.
|
|
||||||
* [The Controller](https://github.com/linnovate/mean/blob/master/app/controllers/articles.js) - Where we take care of our backend logic.
|
|
||||||
* [NodeJS Routes](https://github.com/linnovate/mean/blob/master/config/routes.js) - Where we define our REST service routes.
|
|
||||||
* [AngularJs Routes](https://github.com/linnovate/mean/blob/master/public/js/config.js) - Where we define our CRUD routes.
|
|
||||||
* [The AngularJs Service](https://github.com/linnovate/mean/blob/master/public/js/services/articles.js) - Where we connect to our REST service.
|
|
||||||
* [The AngularJs Controller](https://github.com/linnovate/mean/blob/master/public/js/controllers/articles.js) - Where we take care of our frontend logic.
|
|
||||||
* [The AngularJs Views Folder](https://github.com/linnovate/mean/blob/master/public/views/articles) - Where we keep our CRUD views.
|
|
||||||
|
|
||||||
## Heroku Quick Deployment
|
|
||||||
Before you start make sure you have <a href="https://toolbelt.heroku.com/">heroku toolbelt</a> installed and an accessible mongo db instance - you can try <a href="http://www.mongohq.com/">mongohq</a> which have an easy setup )
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git init
|
|
||||||
git add .
|
|
||||||
git commit -m "initial version"
|
|
||||||
heroku apps:create
|
|
||||||
git push heroku master
|
|
||||||
```
|
|
||||||
|
|
||||||
## More Information
|
|
||||||
* Contact Amos Haviv on any issue via [E-Mail](mailto:mail@amoshaviv.com), [Facebook](http://www.facebook.com/amoshaviv), or [Twitter](http://www.twitter.com/amoshaviv).
|
|
||||||
* Visit us at [Linnovate.net](http://www.linnovate.net/).
|
|
||||||
* Visit our [Ninja's Zone](http://www.meanleanstartupmachine.com/) for extended support.
|
|
||||||
|
|
||||||
## Credits
|
|
||||||
Inspired by the great work of [Madhusudhan Srinivasa](https://github.com/madhums/)
|
|
||||||
|
|
||||||
## License
|
|
||||||
(The MIT License)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
'Software'), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
||||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
||||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
||||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
||||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,51 +0,0 @@
|
|||||||
/**
|
|
||||||
* Module dependencies.
|
|
||||||
*/
|
|
||||||
var mongoose = require('mongoose'),
|
|
||||||
config = require('../../config/config'),
|
|
||||||
Schema = mongoose.Schema;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Article Schema
|
|
||||||
*/
|
|
||||||
var ArticleSchema = new Schema({
|
|
||||||
created: {
|
|
||||||
type: Date,
|
|
||||||
default: Date.now
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
trim: true
|
|
||||||
},
|
|
||||||
content: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
trim: true
|
|
||||||
},
|
|
||||||
user: {
|
|
||||||
type: Schema.ObjectId,
|
|
||||||
ref: 'User'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validations
|
|
||||||
*/
|
|
||||||
ArticleSchema.path('title').validate(function(title) {
|
|
||||||
return title.length;
|
|
||||||
}, 'Title cannot be blank');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Statics
|
|
||||||
*/
|
|
||||||
ArticleSchema.statics = {
|
|
||||||
load: function(id, cb) {
|
|
||||||
this.findOne({
|
|
||||||
_id: id
|
|
||||||
}).populate('user', 'name username').exec(cb);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
mongoose.model('Article', ArticleSchema);
|
|
@ -1,124 +0,0 @@
|
|||||||
/**
|
|
||||||
* Module dependencies.
|
|
||||||
*/
|
|
||||||
var mongoose = require('mongoose'),
|
|
||||||
Schema = mongoose.Schema,
|
|
||||||
crypto = require('crypto'),
|
|
||||||
_ = require('underscore'),
|
|
||||||
authTypes = ['github', 'twitter', 'facebook', 'google'];
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User Schema
|
|
||||||
*/
|
|
||||||
var UserSchema = new Schema({
|
|
||||||
name: String,
|
|
||||||
email: String,
|
|
||||||
username: {
|
|
||||||
type: String,
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
provider: String,
|
|
||||||
hashed_password: String,
|
|
||||||
salt: String,
|
|
||||||
facebook: {},
|
|
||||||
twitter: {},
|
|
||||||
github: {},
|
|
||||||
google: {}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Virtuals
|
|
||||||
*/
|
|
||||||
UserSchema.virtual('password').set(function(password) {
|
|
||||||
this._password = password;
|
|
||||||
this.salt = this.makeSalt();
|
|
||||||
this.hashed_password = this.encryptPassword(password);
|
|
||||||
}).get(function() {
|
|
||||||
return this._password;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validations
|
|
||||||
*/
|
|
||||||
var validatePresenceOf = function(value) {
|
|
||||||
return value && value.length;
|
|
||||||
};
|
|
||||||
|
|
||||||
// the below 4 validations only apply if you are signing up traditionally
|
|
||||||
UserSchema.path('name').validate(function(name) {
|
|
||||||
// if you are authenticating by any of the oauth strategies, don't validate
|
|
||||||
if (authTypes.indexOf(this.provider) !== -1) return true;
|
|
||||||
return name.length;
|
|
||||||
}, 'Name cannot be blank');
|
|
||||||
|
|
||||||
UserSchema.path('email').validate(function(email) {
|
|
||||||
// if you are authenticating by any of the oauth strategies, don't validate
|
|
||||||
if (authTypes.indexOf(this.provider) !== -1) return true;
|
|
||||||
return email.length;
|
|
||||||
}, 'Email cannot be blank');
|
|
||||||
|
|
||||||
UserSchema.path('username').validate(function(username) {
|
|
||||||
// if you are authenticating by any of the oauth strategies, don't validate
|
|
||||||
if (authTypes.indexOf(this.provider) !== -1) return true;
|
|
||||||
return username.length;
|
|
||||||
}, 'Username cannot be blank');
|
|
||||||
|
|
||||||
UserSchema.path('hashed_password').validate(function(hashed_password) {
|
|
||||||
// if you are authenticating by any of the oauth strategies, don't validate
|
|
||||||
if (authTypes.indexOf(this.provider) !== -1) return true;
|
|
||||||
return hashed_password.length;
|
|
||||||
}, 'Password cannot be blank');
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pre-save hook
|
|
||||||
*/
|
|
||||||
UserSchema.pre('save', function(next) {
|
|
||||||
if (!this.isNew) return next();
|
|
||||||
|
|
||||||
if (!validatePresenceOf(this.password) && authTypes.indexOf(this.provider) === -1)
|
|
||||||
next(new Error('Invalid password'));
|
|
||||||
else
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Methods
|
|
||||||
*/
|
|
||||||
UserSchema.methods = {
|
|
||||||
/**
|
|
||||||
* Authenticate - check if the passwords are the same
|
|
||||||
*
|
|
||||||
* @param {String} plainText
|
|
||||||
* @return {Boolean}
|
|
||||||
* @api public
|
|
||||||
*/
|
|
||||||
authenticate: function(plainText) {
|
|
||||||
return this.encryptPassword(plainText) === this.hashed_password;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make salt
|
|
||||||
*
|
|
||||||
* @return {String}
|
|
||||||
* @api public
|
|
||||||
*/
|
|
||||||
makeSalt: function() {
|
|
||||||
return Math.round((new Date().valueOf() * Math.random())) + '';
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt password
|
|
||||||
*
|
|
||||||
* @param {String} password
|
|
||||||
* @return {String}
|
|
||||||
* @api public
|
|
||||||
*/
|
|
||||||
encryptPassword: function(password) {
|
|
||||||
if (!password) return '';
|
|
||||||
return crypto.createHmac('sha1', this.salt).update(password).digest('hex');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
mongoose.model('User', UserSchema);
|
|
@ -1,13 +0,0 @@
|
|||||||
extends layouts/default
|
|
||||||
|
|
||||||
block main
|
|
||||||
h1 Oops something went wrong
|
|
||||||
br
|
|
||||||
span 404
|
|
||||||
|
|
||||||
block content
|
|
||||||
#error-message-box
|
|
||||||
#error-stack-trace
|
|
||||||
pre
|
|
||||||
code!= error
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
|||||||
extends layouts/default
|
|
||||||
|
|
||||||
block main
|
|
||||||
h1 Oops something went wrong
|
|
||||||
br
|
|
||||||
span 500
|
|
||||||
|
|
||||||
block content
|
|
||||||
#error-message-box
|
|
||||||
#error-stack-trace
|
|
||||||
pre
|
|
||||||
code!= error
|
|
@ -1,29 +0,0 @@
|
|||||||
//AngularJS
|
|
||||||
script(type='text/javascript', src='/lib/angular/angular.js')
|
|
||||||
script(type='text/javascript', src='/lib/angular-cookies/angular-cookies.js')
|
|
||||||
script(type='text/javascript', src='/lib/angular-resource/angular-resource.js')
|
|
||||||
|
|
||||||
//Angular UI
|
|
||||||
script(type='text/javascript', src='/lib/angular-bootstrap/ui-bootstrap-tpls.js')
|
|
||||||
script(type='text/javascript', src='/lib/angular-bootstrap/ui-bootstrap.js')
|
|
||||||
script(type='text/javascript', src='/lib/angular-ui-utils/modules/route/route.js')
|
|
||||||
|
|
||||||
//Application Init
|
|
||||||
script(type='text/javascript', src='/js/app.js')
|
|
||||||
script(type='text/javascript', src='/js/config.js')
|
|
||||||
script(type='text/javascript', src='/js/directives.js')
|
|
||||||
script(type='text/javascript', src='/js/filters.js')
|
|
||||||
|
|
||||||
//Application Services
|
|
||||||
script(type='text/javascript', src='/js/services/global.js')
|
|
||||||
script(type='text/javascript', src='/js/services/articles.js')
|
|
||||||
|
|
||||||
//Application Controllers
|
|
||||||
script(type='text/javascript', src='/js/controllers/articles.js')
|
|
||||||
script(type='text/javascript', src='/js/controllers/index.js')
|
|
||||||
script(type='text/javascript', src='/js/controllers/header.js')
|
|
||||||
script(type='text/javascript', src='/js/init.js')
|
|
||||||
|
|
||||||
if (req.host='localhost')
|
|
||||||
//Livereload script rendered
|
|
||||||
script(type='text/javascript', src='http://localhost:35729/livereload.js')
|
|
@ -1,29 +0,0 @@
|
|||||||
head
|
|
||||||
meta(charset='utf-8')
|
|
||||||
meta(http-equiv='X-UA-Compatible', content='IE=edge,chrome=1')
|
|
||||||
meta(name='viewport', content='width=device-width,initial-scale=1')
|
|
||||||
|
|
||||||
title= appName+' - '+title
|
|
||||||
meta(http-equiv='Content-type', content='text/html;charset=UTF-8')
|
|
||||||
meta(name="keywords", content="node.js, express, mongoose, mongodb, angularjs")
|
|
||||||
meta(name="description", content="MEAN - A Modern Stack: MongoDB, ExpressJS, AngularJS, NodeJS. (BONUS: Passport User Support).")
|
|
||||||
|
|
||||||
link(href='/img/icons/favicon.ico', rel='shortcut icon', type='image/x-icon')
|
|
||||||
|
|
||||||
meta(property='fb:app_id', content='APP_ID')
|
|
||||||
meta(property='og:title', content='#{appName} - #{title}')
|
|
||||||
meta(property='og:description', content='MEAN - A Modern Stack: MongoDB, ExpressJS, AngularJS, NodeJS. (BONUS: Passport User Support).')
|
|
||||||
meta(property='og:type', content='website')
|
|
||||||
meta(property='og:url', content='APP_URL')
|
|
||||||
meta(property='og:image', content='APP_LOGO')
|
|
||||||
meta(property='og:site_name', content='MEAN - A Modern Stack')
|
|
||||||
meta(property='fb:admins', content='APP_ADMIN')
|
|
||||||
|
|
||||||
link(rel='stylesheet', href='/lib/bootstrap/docs/assets/css/bootstrap.css')
|
|
||||||
//- link(rel='stylesheet', href='/lib/bootstrap/dist/css/bootstrap-responsive.css')
|
|
||||||
link(rel='stylesheet', href='/css/common.css')
|
|
||||||
|
|
||||||
link(rel='stylesheet', href='/css/views/articles.css')
|
|
||||||
|
|
||||||
//if lt IE 9
|
|
||||||
script(src='http://html5shim.googlecode.com/svn/trunk/html5.js')
|
|
@ -1,6 +0,0 @@
|
|||||||
extends layouts/default
|
|
||||||
|
|
||||||
block content
|
|
||||||
section(data-ng-view)
|
|
||||||
script(type="text/javascript").
|
|
||||||
window.user = !{user};
|
|
@ -1,9 +0,0 @@
|
|||||||
!!! 5
|
|
||||||
html(lang='en', xmlns='http://www.w3.org/1999/xhtml', xmlns:fb='https://www.facebook.com/2008/fbml', itemscope='itemscope', itemtype='http://schema.org/Product')
|
|
||||||
include ../includes/head
|
|
||||||
body
|
|
||||||
.navbar.navbar-inverse.navbar-fixed-top(data-ng-include="'views/header.html'", data-role="navigation")
|
|
||||||
section.content
|
|
||||||
section.container
|
|
||||||
block content
|
|
||||||
include ../includes/foot
|
|
@ -1,22 +0,0 @@
|
|||||||
extends ../layouts/default
|
|
||||||
|
|
||||||
block content
|
|
||||||
.row
|
|
||||||
.offset1.span5
|
|
||||||
a(href="/auth/facebook")
|
|
||||||
img(src="/img/icons/facebook.png")
|
|
||||||
a(href="/auth/github")
|
|
||||||
img(src="/img/icons/github.png")
|
|
||||||
a(href="/auth/twitter")
|
|
||||||
img(src="/img/icons/twitter.png")
|
|
||||||
a(href="/auth/google")
|
|
||||||
img(src="/img/icons/google.png")
|
|
||||||
.span6
|
|
||||||
if (typeof errors !== 'undefined')
|
|
||||||
.fade.in.alert.alert-block.alert-error
|
|
||||||
a.close(data-dismiss="alert", href="javascript:void(0)") x
|
|
||||||
ul
|
|
||||||
each error in errors
|
|
||||||
li= error.type
|
|
||||||
|
|
||||||
block auth
|
|
@ -1,20 +0,0 @@
|
|||||||
extends auth
|
|
||||||
|
|
||||||
block auth
|
|
||||||
form.signin.form-horizontal(action="/users/session", method="post")
|
|
||||||
p.error= message
|
|
||||||
.control-group
|
|
||||||
label.control-label(for='email') Email
|
|
||||||
.controls
|
|
||||||
input#email(type='text', name="email", placeholder='Email')
|
|
||||||
|
|
||||||
.control-group
|
|
||||||
label.control-label(for='password') Password
|
|
||||||
.controls
|
|
||||||
input#password(type='password', name="password", placeholder='Password')
|
|
||||||
|
|
||||||
.form-actions
|
|
||||||
button.btn.btn-primary(type='submit') Sign in
|
|
||||||
|
|
||||||
| or
|
|
||||||
a.show-signup(href="/signup") Sign up
|
|
@ -1,29 +0,0 @@
|
|||||||
extends auth
|
|
||||||
|
|
||||||
block auth
|
|
||||||
form.signup.form-horizontal(action="/users", method="post")
|
|
||||||
.control-group
|
|
||||||
label.control-label(for='name') Full name
|
|
||||||
.controls
|
|
||||||
input#name(type='text', name="name", placeholder='Full name', value=user.name)
|
|
||||||
|
|
||||||
.control-group
|
|
||||||
label.control-label(for='email') Email
|
|
||||||
.controls
|
|
||||||
input#email(type='text', name="email", placeholder='Email', value=user.email)
|
|
||||||
|
|
||||||
.control-group
|
|
||||||
label.control-label(for='username') Username
|
|
||||||
.controls
|
|
||||||
input#username(type='text', name="username", placeholder='Username', value=user.username)
|
|
||||||
|
|
||||||
.control-group
|
|
||||||
label.control-label(for='password') Password
|
|
||||||
.controls
|
|
||||||
input#password(type='password', name="password", placeholder='Password')
|
|
||||||
|
|
||||||
.form-actions
|
|
||||||
button.btn.btn-primary(type='submit') Sign up
|
|
||||||
|
|
||||||
| or
|
|
||||||
a.show-login(href="/signin") login
|
|
12
bower.json
12
bower.json
@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "angular-quickstart",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"dependencies": {
|
|
||||||
"bootstrap": "2.3.2",
|
|
||||||
"angular": "1.0.6",
|
|
||||||
"angular-resource": "1.0.6",
|
|
||||||
"angular-cookies": "1.0.6",
|
|
||||||
"angular-bootstrap": "0.6.0",
|
|
||||||
"angular-ui-utils": "0.0.4"
|
|
||||||
}
|
|
||||||
}
|
|
18
conf.json
Normal file
18
conf.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"db": "mongodb://localhost",
|
||||||
|
"facebook": {
|
||||||
|
"clientID": "APP_ID",
|
||||||
|
"clientSecret": "APP_SECRET",
|
||||||
|
"callbackURL": "http://localhost:3000/auth/facebook/callback"
|
||||||
|
},
|
||||||
|
"twitter": {
|
||||||
|
"clientID": "CONSUMER_KEY",
|
||||||
|
"clientSecret": "CONSUMER_SECRET",
|
||||||
|
"callbackURL": "http://localhost:3000/auth/twitter/callback"
|
||||||
|
},
|
||||||
|
"google": {
|
||||||
|
"clientID": "APP_ID",
|
||||||
|
"clientSecret": "APP_SECRET",
|
||||||
|
"callbackURL": "http://localhost:3000/auth/google/callback"
|
||||||
|
}
|
||||||
|
}
|
8
config/env/all.js
vendored
8
config/env/all.js
vendored
@ -1,8 +0,0 @@
|
|||||||
var path = require('path'),
|
|
||||||
rootPath = path.normalize(__dirname + '/../..');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
root: rootPath,
|
|
||||||
port: process.env.PORT || 3000,
|
|
||||||
db: process.env.MONGOHQ_URL
|
|
||||||
}
|
|
26
config/env/development.json
vendored
26
config/env/development.json
vendored
@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"db": "mongodb://localhost/mean-dev",
|
|
||||||
"app": {
|
|
||||||
"name": "MEAN - A Modern Stack - Development"
|
|
||||||
},
|
|
||||||
"facebook": {
|
|
||||||
"clientID": "APP_ID",
|
|
||||||
"clientSecret": "APP_SECRET",
|
|
||||||
"callbackURL": "http://localhost:3000/auth/facebook/callback"
|
|
||||||
},
|
|
||||||
"twitter": {
|
|
||||||
"clientID": "CONSUMER_KEY",
|
|
||||||
"clientSecret": "CONSUMER_SECRET",
|
|
||||||
"callbackURL": "http://localhost:3000/auth/twitter/callback"
|
|
||||||
},
|
|
||||||
"github": {
|
|
||||||
"clientID": "APP_ID",
|
|
||||||
"clientSecret": "APP_SECRET",
|
|
||||||
"callbackURL": "http://localhost:3000/auth/github/callback"
|
|
||||||
},
|
|
||||||
"google": {
|
|
||||||
"clientID": "APP_ID",
|
|
||||||
"clientSecret": "APP_SECRET",
|
|
||||||
"callbackURL": "http://localhost:3000/auth/google/callback"
|
|
||||||
}
|
|
||||||
}
|
|
26
config/env/production.json
vendored
26
config/env/production.json
vendored
@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"db": "mongodb://localhost/mean",
|
|
||||||
"app": {
|
|
||||||
"name": "MEAN - A Modern Stack - Production"
|
|
||||||
},
|
|
||||||
"facebook": {
|
|
||||||
"clientID": "APP_ID",
|
|
||||||
"clientSecret": "APP_SECRET",
|
|
||||||
"callbackURL": "http://localhost:3000/auth/facebook/callback"
|
|
||||||
},
|
|
||||||
"twitter": {
|
|
||||||
"clientID": "CONSUMER_KEY",
|
|
||||||
"clientSecret": "CONSUMER_SECRET",
|
|
||||||
"callbackURL": "http://localhost:3000/auth/twitter/callback"
|
|
||||||
},
|
|
||||||
"github": {
|
|
||||||
"clientID": "APP_ID",
|
|
||||||
"clientSecret": "APP_SECRET",
|
|
||||||
"callbackURL": "http://localhost:3000/auth/github/callback"
|
|
||||||
},
|
|
||||||
"google": {
|
|
||||||
"clientID": "APP_ID",
|
|
||||||
"clientSecret": "APP_SECRET",
|
|
||||||
"callbackURL": "http://localhost:3000/auth/google/callback"
|
|
||||||
}
|
|
||||||
}
|
|
27
config/env/test.json
vendored
27
config/env/test.json
vendored
@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"db": "mongodb://localhost/mean-test",
|
|
||||||
"port": 3001,
|
|
||||||
"app": {
|
|
||||||
"name": "MEAN - A Modern Stack - Test"
|
|
||||||
},
|
|
||||||
"facebook": {
|
|
||||||
"clientID": "APP_ID",
|
|
||||||
"clientSecret": "APP_SECRET",
|
|
||||||
"callbackURL": "http://localhost:3000/auth/facebook/callback"
|
|
||||||
},
|
|
||||||
"twitter": {
|
|
||||||
"clientID": "CONSUMER_KEY",
|
|
||||||
"clientSecret": "CONSUMER_SECRET",
|
|
||||||
"callbackURL": "http://localhost:3000/auth/twitter/callback"
|
|
||||||
},
|
|
||||||
"github": {
|
|
||||||
"clientID": "APP_ID",
|
|
||||||
"clientSecret": "APP_SECRET",
|
|
||||||
"callbackURL": "http://localhost:3000/auth/github/callback"
|
|
||||||
},
|
|
||||||
"google": {
|
|
||||||
"clientID": "APP_ID",
|
|
||||||
"clientSecret": "APP_SECRET",
|
|
||||||
"callbackURL": "http://localhost:3000/auth/google/callback"
|
|
||||||
}
|
|
||||||
}
|
|
27
config/env/travis.json
vendored
27
config/env/travis.json
vendored
@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"db": "mongodb://localhost/mean-travis",
|
|
||||||
"port": 3001,
|
|
||||||
"app": {
|
|
||||||
"name": "MEAN - A Modern Stack - Test on travis"
|
|
||||||
},
|
|
||||||
"facebook": {
|
|
||||||
"clientID": "APP_ID",
|
|
||||||
"clientSecret": "APP_SECRET",
|
|
||||||
"callbackURL": "http://localhost:3000/auth/facebook/callback"
|
|
||||||
},
|
|
||||||
"twitter": {
|
|
||||||
"clientID": "CONSUMER_KEY",
|
|
||||||
"clientSecret": "CONSUMER_SECRET",
|
|
||||||
"callbackURL": "http://localhost:3000/auth/twitter/callback"
|
|
||||||
},
|
|
||||||
"github": {
|
|
||||||
"clientID": "APP_ID",
|
|
||||||
"clientSecret": "APP_SECRET",
|
|
||||||
"callbackURL": "http://localhost:3000/auth/github/callback"
|
|
||||||
},
|
|
||||||
"google": {
|
|
||||||
"clientID": "APP_ID",
|
|
||||||
"clientSecret": "APP_SECRET",
|
|
||||||
"callbackURL": "http://localhost:3000/auth/google/callback"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
/**
|
|
||||||
* Generic require login routing middleware
|
|
||||||
*/
|
|
||||||
exports.requiresLogin = function(req, res, next) {
|
|
||||||
if (!req.isAuthenticated()) {
|
|
||||||
return res.send(401, 'User is not authorized');
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User authorizations routing middleware
|
|
||||||
*/
|
|
||||||
exports.user = {
|
|
||||||
hasAuthorization: function(req, res, next) {
|
|
||||||
if (req.profile.id != req.user.id) {
|
|
||||||
return res.send(401, 'User is not authorized');
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Article authorizations routing middleware
|
|
||||||
*/
|
|
||||||
exports.article = {
|
|
||||||
hasAuthorization: function(req, res, next) {
|
|
||||||
if (req.article.user.id != req.user.id) {
|
|
||||||
return res.send(401, 'User is not authorized');
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,172 +0,0 @@
|
|||||||
var mongoose = require('mongoose'),
|
|
||||||
LocalStrategy = require('passport-local').Strategy,
|
|
||||||
TwitterStrategy = require('passport-twitter').Strategy,
|
|
||||||
FacebookStrategy = require('passport-facebook').Strategy,
|
|
||||||
GitHubStrategy = require('passport-github').Strategy,
|
|
||||||
GoogleStrategy = require('passport-google-oauth').OAuth2Strategy,
|
|
||||||
User = mongoose.model('User'),
|
|
||||||
config = require('./config');
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = function(passport) {
|
|
||||||
//Serialize sessions
|
|
||||||
passport.serializeUser(function(user, done) {
|
|
||||||
done(null, user.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
passport.deserializeUser(function(id, done) {
|
|
||||||
User.findOne({
|
|
||||||
_id: id
|
|
||||||
}, function(err, user) {
|
|
||||||
done(err, user);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//Use local strategy
|
|
||||||
passport.use(new LocalStrategy({
|
|
||||||
usernameField: 'email',
|
|
||||||
passwordField: 'password'
|
|
||||||
},
|
|
||||||
function(email, password, done) {
|
|
||||||
User.findOne({
|
|
||||||
email: email
|
|
||||||
}, function(err, user) {
|
|
||||||
if (err) {
|
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
if (!user) {
|
|
||||||
return done(null, false, {
|
|
||||||
message: 'Unknown user'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!user.authenticate(password)) {
|
|
||||||
return done(null, false, {
|
|
||||||
message: 'Invalid password'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return done(null, user);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
//Use twitter strategy
|
|
||||||
passport.use(new TwitterStrategy({
|
|
||||||
consumerKey: config.twitter.clientID,
|
|
||||||
consumerSecret: config.twitter.clientSecret,
|
|
||||||
callbackURL: config.twitter.callbackURL
|
|
||||||
},
|
|
||||||
function(token, tokenSecret, profile, done) {
|
|
||||||
User.findOne({
|
|
||||||
'twitter.id_str': profile.id
|
|
||||||
}, function(err, user) {
|
|
||||||
if (err) {
|
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
if (!user) {
|
|
||||||
user = new User({
|
|
||||||
name: profile.displayName,
|
|
||||||
username: profile.username,
|
|
||||||
provider: 'twitter',
|
|
||||||
twitter: profile._json
|
|
||||||
});
|
|
||||||
user.save(function(err) {
|
|
||||||
if (err) console.log(err);
|
|
||||||
return done(err, user);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return done(err, user);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
//Use facebook strategy
|
|
||||||
passport.use(new FacebookStrategy({
|
|
||||||
clientID: config.facebook.clientID,
|
|
||||||
clientSecret: config.facebook.clientSecret,
|
|
||||||
callbackURL: config.facebook.callbackURL
|
|
||||||
},
|
|
||||||
function(accessToken, refreshToken, profile, done) {
|
|
||||||
User.findOne({
|
|
||||||
'facebook.id': profile.id
|
|
||||||
}, function(err, user) {
|
|
||||||
if (err) {
|
|
||||||
return done(err);
|
|
||||||
}
|
|
||||||
if (!user) {
|
|
||||||
user = new User({
|
|
||||||
name: profile.displayName,
|
|
||||||
email: profile.emails[0].value,
|
|
||||||
username: profile.username,
|
|
||||||
provider: 'facebook',
|
|
||||||
facebook: profile._json
|
|
||||||
});
|
|
||||||
user.save(function(err) {
|
|
||||||
if (err) console.log(err);
|
|
||||||
return done(err, user);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return done(err, user);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
//Use github strategy
|
|
||||||
passport.use(new GitHubStrategy({
|
|
||||||
clientID: config.github.clientID,
|
|
||||||
clientSecret: config.github.clientSecret,
|
|
||||||
callbackURL: config.github.callbackURL
|
|
||||||
},
|
|
||||||
function(accessToken, refreshToken, profile, done) {
|
|
||||||
User.findOne({
|
|
||||||
'github.id': profile.id
|
|
||||||
}, function(err, user) {
|
|
||||||
if (!user) {
|
|
||||||
user = new User({
|
|
||||||
name: profile.displayName,
|
|
||||||
email: profile.emails[0].value,
|
|
||||||
username: profile.username,
|
|
||||||
provider: 'github',
|
|
||||||
github: profile._json
|
|
||||||
});
|
|
||||||
user.save(function(err) {
|
|
||||||
if (err) console.log(err);
|
|
||||||
return done(err, user);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return done(err, user);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
//Use google strategy
|
|
||||||
passport.use(new GoogleStrategy({
|
|
||||||
clientID: config.google.clientID,
|
|
||||||
clientSecret: config.google.clientSecret,
|
|
||||||
callbackURL: config.google.callbackURL
|
|
||||||
},
|
|
||||||
function(accessToken, refreshToken, profile, done) {
|
|
||||||
User.findOne({
|
|
||||||
'google.id': profile.id
|
|
||||||
}, function(err, user) {
|
|
||||||
if (!user) {
|
|
||||||
user = new User({
|
|
||||||
name: profile.displayName,
|
|
||||||
email: profile.emails[0].value,
|
|
||||||
username: profile.username,
|
|
||||||
provider: 'google',
|
|
||||||
google: profile._json
|
|
||||||
});
|
|
||||||
user.save(function(err) {
|
|
||||||
if (err) console.log(err);
|
|
||||||
return done(err, user);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return done(err, user);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
));
|
|
||||||
};
|
|
@ -1,78 +0,0 @@
|
|||||||
module.exports = function(app, passport, auth) {
|
|
||||||
//User Routes
|
|
||||||
var users = require('../app/controllers/users');
|
|
||||||
app.get('/signin', users.signin);
|
|
||||||
app.get('/signup', users.signup);
|
|
||||||
app.get('/signout', users.signout);
|
|
||||||
|
|
||||||
//Setting up the users api
|
|
||||||
app.post('/users', users.create);
|
|
||||||
|
|
||||||
app.post('/users/session', passport.authenticate('local', {
|
|
||||||
failureRedirect: '/signin',
|
|
||||||
failureFlash: 'Invalid email or password.'
|
|
||||||
}), users.session);
|
|
||||||
|
|
||||||
app.get('/users/me', users.me);
|
|
||||||
app.get('/users/:userId', users.show);
|
|
||||||
|
|
||||||
//Setting the facebook oauth routes
|
|
||||||
app.get('/auth/facebook', passport.authenticate('facebook', {
|
|
||||||
scope: ['email', 'user_about_me'],
|
|
||||||
failureRedirect: '/signin'
|
|
||||||
}), users.signin);
|
|
||||||
|
|
||||||
app.get('/auth/facebook/callback', passport.authenticate('facebook', {
|
|
||||||
failureRedirect: '/signin'
|
|
||||||
}), users.authCallback);
|
|
||||||
|
|
||||||
//Setting the github oauth routes
|
|
||||||
app.get('/auth/github', passport.authenticate('github', {
|
|
||||||
failureRedirect: '/signin'
|
|
||||||
}), users.signin);
|
|
||||||
|
|
||||||
app.get('/auth/github/callback', passport.authenticate('github', {
|
|
||||||
failureRedirect: '/signin'
|
|
||||||
}), users.authCallback);
|
|
||||||
|
|
||||||
//Setting the twitter oauth routes
|
|
||||||
app.get('/auth/twitter', passport.authenticate('twitter', {
|
|
||||||
failureRedirect: '/signin'
|
|
||||||
}), users.signin);
|
|
||||||
|
|
||||||
app.get('/auth/twitter/callback', passport.authenticate('twitter', {
|
|
||||||
failureRedirect: '/signin'
|
|
||||||
}), users.authCallback);
|
|
||||||
|
|
||||||
//Setting the google oauth routes
|
|
||||||
app.get('/auth/google', passport.authenticate('google', {
|
|
||||||
failureRedirect: '/signin',
|
|
||||||
scope: [
|
|
||||||
'https://www.googleapis.com/auth/userinfo.profile',
|
|
||||||
'https://www.googleapis.com/auth/userinfo.email'
|
|
||||||
]
|
|
||||||
}), users.signin);
|
|
||||||
|
|
||||||
app.get('/auth/google/callback', passport.authenticate('google', {
|
|
||||||
failureRedirect: '/signin'
|
|
||||||
}), users.authCallback);
|
|
||||||
|
|
||||||
//Finish with setting up the userId param
|
|
||||||
app.param('userId', users.user);
|
|
||||||
|
|
||||||
//Article Routes
|
|
||||||
var articles = require('../app/controllers/articles');
|
|
||||||
app.get('/articles', articles.all);
|
|
||||||
app.post('/articles', auth.requiresLogin, articles.create);
|
|
||||||
app.get('/articles/:articleId', articles.show);
|
|
||||||
app.put('/articles/:articleId', auth.requiresLogin, auth.article.hasAuthorization, articles.update);
|
|
||||||
app.del('/articles/:articleId', auth.requiresLogin, auth.article.hasAuthorization, articles.destroy);
|
|
||||||
|
|
||||||
//Finish with setting up the articleId param
|
|
||||||
app.param('articleId', articles.article);
|
|
||||||
|
|
||||||
//Home route
|
|
||||||
var index = require('../app/controllers/index');
|
|
||||||
app.get('/', index.render);
|
|
||||||
|
|
||||||
};
|
|
7
models/article.js
Executable file
7
models/article.js
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
var mongoose = require('mongoose'),
|
||||||
|
Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
|
124
models/user.js
Executable file
124
models/user.js
Executable file
@ -0,0 +1,124 @@
|
|||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
var mongoose = require('mongoose'),
|
||||||
|
Schema = mongoose.Schema,
|
||||||
|
crypto = require('crypto'),
|
||||||
|
_ = require('underscore'),
|
||||||
|
authTypes = ['github', 'twitter', 'facebook', 'google'];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User Schema
|
||||||
|
*/
|
||||||
|
var UserSchema = new Schema({
|
||||||
|
name: String,
|
||||||
|
email: String,
|
||||||
|
username: {
|
||||||
|
type: String,
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
provider: String,
|
||||||
|
hashed_password: String,
|
||||||
|
salt: String,
|
||||||
|
facebook: {},
|
||||||
|
twitter: {},
|
||||||
|
github: {},
|
||||||
|
google: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtuals
|
||||||
|
*/
|
||||||
|
UserSchema.virtual('password').set(function(password) {
|
||||||
|
this._password = password;
|
||||||
|
this.salt = this.makeSalt();
|
||||||
|
this.hashed_password = this.encryptPassword(password);
|
||||||
|
}).get(function() {
|
||||||
|
return this._password;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validations
|
||||||
|
*/
|
||||||
|
var validatePresenceOf = function(value) {
|
||||||
|
return value && value.length;
|
||||||
|
};
|
||||||
|
|
||||||
|
// the below 4 validations only apply if you are signing up traditionally
|
||||||
|
UserSchema.path('name').validate(function(name) {
|
||||||
|
// if you are authenticating by any of the oauth strategies, don't validate
|
||||||
|
if (authTypes.indexOf(this.provider) !== -1) return true;
|
||||||
|
return name.length;
|
||||||
|
}, 'Name cannot be blank');
|
||||||
|
|
||||||
|
UserSchema.path('email').validate(function(email) {
|
||||||
|
// if you are authenticating by any of the oauth strategies, don't validate
|
||||||
|
if (authTypes.indexOf(this.provider) !== -1) return true;
|
||||||
|
return email.length;
|
||||||
|
}, 'Email cannot be blank');
|
||||||
|
|
||||||
|
UserSchema.path('username').validate(function(username) {
|
||||||
|
// if you are authenticating by any of the oauth strategies, don't validate
|
||||||
|
if (authTypes.indexOf(this.provider) !== -1) return true;
|
||||||
|
return username.length;
|
||||||
|
}, 'Username cannot be blank');
|
||||||
|
|
||||||
|
UserSchema.path('hashed_password').validate(function(hashed_password) {
|
||||||
|
// if you are authenticating by any of the oauth strategies, don't validate
|
||||||
|
if (authTypes.indexOf(this.provider) !== -1) return true;
|
||||||
|
return hashed_password.length;
|
||||||
|
}, 'Password cannot be blank');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-save hook
|
||||||
|
*/
|
||||||
|
UserSchema.pre('save', function(next) {
|
||||||
|
if (!this.isNew) return next();
|
||||||
|
|
||||||
|
if (!validatePresenceOf(this.password) && authTypes.indexOf(this.provider) === -1)
|
||||||
|
next(new Error('Invalid password'));
|
||||||
|
else
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methods
|
||||||
|
*/
|
||||||
|
UserSchema.methods = {
|
||||||
|
/**
|
||||||
|
* Authenticate - check if the passwords are the same
|
||||||
|
*
|
||||||
|
* @param {String} plainText
|
||||||
|
* @return {Boolean}
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
authenticate: function(plainText) {
|
||||||
|
return this.encryptPassword(plainText) === this.hashed_password;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make salt
|
||||||
|
*
|
||||||
|
* @return {String}
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
makeSalt: function() {
|
||||||
|
return Math.round((new Date().valueOf() * Math.random())) + '';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt password
|
||||||
|
*
|
||||||
|
* @param {String} password
|
||||||
|
* @return {String}
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
encryptPassword: function(password) {
|
||||||
|
if (!password) return '';
|
||||||
|
return crypto.createHmac('sha1', this.salt).update(password).digest('hex');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mongoose.model('User', UserSchema);
|
454
server.js
454
server.js
@ -1,9 +1,10 @@
|
|||||||
var express = require('express'),
|
var express = require('express'),
|
||||||
|
mongoose = require('mongoose'),
|
||||||
mongoStore = require('connect-mongo')(express),
|
mongoStore = require('connect-mongo')(express),
|
||||||
fs = require('fs'),
|
fs = require('fs'),
|
||||||
flash = require('connect-flash'),
|
flash = require('connect-flash'),
|
||||||
helpers = require('view-helpers'),
|
helpers = require('view-helpers'),
|
||||||
config = require('./config'),
|
config = require('./conf'),
|
||||||
passport = require('passport'),
|
passport = require('passport'),
|
||||||
logger = require('mean-logger');
|
logger = require('mean-logger');
|
||||||
|
|
||||||
@ -14,33 +15,377 @@ var express = require('express'),
|
|||||||
|
|
||||||
//Load configurations
|
//Load configurations
|
||||||
//if test env, load example file
|
//if test env, load example file
|
||||||
var env = process.env.NODE_ENV = process.env.NODE_ENV || 'development',
|
var env = process.env.NODE_ENV = process.env.NODE_ENV || 'development';
|
||||||
config = require('./config/config'),
|
|
||||||
auth = require('./config/middlewares/authorization'),
|
/**
|
||||||
mongoose = require('mongoose');
|
* Generic require login routing middleware
|
||||||
|
*/
|
||||||
|
var requiresLogin = function(req, res, next) {
|
||||||
|
if (!req.isAuthenticated()) {
|
||||||
|
return res.send(401, 'User is not authorized');
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User authorizations routing middleware
|
||||||
|
*/
|
||||||
|
var hasAuthorization = function(req, res, next) {
|
||||||
|
if (req.profile.id != req.user.id) {
|
||||||
|
return res.send(401, 'User is not authorized');
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Article authorizations routing middleware
|
||||||
|
*/
|
||||||
|
var hasAuthorization = function(req, res, next) {
|
||||||
|
if (req.article.user.id != req.user.id) {
|
||||||
|
return res.send(401, 'User is not authorized');
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
|
||||||
//Bootstrap db connection
|
//Bootstrap db connection
|
||||||
var db = mongoose.connect(config.db);
|
var db = mongoose.connect(config.db);
|
||||||
|
|
||||||
//Bootstrap models
|
/**
|
||||||
var models_path = __dirname + '/app/models';
|
* Module dependencies.
|
||||||
var walk = function(path) {
|
*/
|
||||||
fs.readdirSync(path).forEach(function(file) {
|
var mongoose = require('mongoose'),
|
||||||
var newPath = path + '/' + file;
|
Schema = mongoose.Schema,
|
||||||
var stat = fs.statSync(newPath);
|
crypto = require('crypto'),
|
||||||
if (stat.isFile()) {
|
_ = require('underscore'),
|
||||||
if (/(.*)\.(js$|coffee$)/.test(file)) {
|
authTypes = ['github', 'twitter', 'facebook', 'google'];
|
||||||
require(newPath);
|
|
||||||
}
|
|
||||||
} else if (stat.isDirectory()) {
|
/**
|
||||||
walk(newPath);
|
* User Schema
|
||||||
}
|
*/
|
||||||
});
|
var UserSchema = new Schema({
|
||||||
|
name: String,
|
||||||
|
email: String,
|
||||||
|
username: {
|
||||||
|
type: String,
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
provider: String,
|
||||||
|
hashed_password: String,
|
||||||
|
salt: String,
|
||||||
|
facebook: {},
|
||||||
|
twitter: {},
|
||||||
|
github: {},
|
||||||
|
google: {}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtuals
|
||||||
|
*/
|
||||||
|
UserSchema.virtual('password').set(function(password) {
|
||||||
|
this._password = password;
|
||||||
|
this.salt = this.makeSalt();
|
||||||
|
this.hashed_password = this.encryptPassword(password);
|
||||||
|
}).get(function() {
|
||||||
|
return this._password;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validations
|
||||||
|
*/
|
||||||
|
var validatePresenceOf = function(value) {
|
||||||
|
return value && value.length;
|
||||||
};
|
};
|
||||||
walk(models_path);
|
|
||||||
|
// the below 4 validations only apply if you are signing up traditionally
|
||||||
|
UserSchema.path('name').validate(function(name) {
|
||||||
|
// if you are authenticating by any of the oauth strategies, don't validate
|
||||||
|
if (authTypes.indexOf(this.provider) !== -1) return true;
|
||||||
|
return name.length;
|
||||||
|
}, 'Name cannot be blank');
|
||||||
|
|
||||||
|
UserSchema.path('email').validate(function(email) {
|
||||||
|
// if you are authenticating by any of the oauth strategies, don't validate
|
||||||
|
if (authTypes.indexOf(this.provider) !== -1) return true;
|
||||||
|
return email.length;
|
||||||
|
}, 'Email cannot be blank');
|
||||||
|
|
||||||
|
UserSchema.path('username').validate(function(username) {
|
||||||
|
// if you are authenticating by any of the oauth strategies, don't validate
|
||||||
|
if (authTypes.indexOf(this.provider) !== -1) return true;
|
||||||
|
return username.length;
|
||||||
|
}, 'Username cannot be blank');
|
||||||
|
|
||||||
|
UserSchema.path('hashed_password').validate(function(hashed_password) {
|
||||||
|
// if you are authenticating by any of the oauth strategies, don't validate
|
||||||
|
if (authTypes.indexOf(this.provider) !== -1) return true;
|
||||||
|
return hashed_password.length;
|
||||||
|
}, 'Password cannot be blank');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-save hook
|
||||||
|
*/
|
||||||
|
UserSchema.pre('save', function(next) {
|
||||||
|
if (!this.isNew) return next();
|
||||||
|
|
||||||
|
if (!validatePresenceOf(this.password) && authTypes.indexOf(this.provider) === -1)
|
||||||
|
next(new Error('Invalid password'));
|
||||||
|
else
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methods
|
||||||
|
*/
|
||||||
|
UserSchema.methods = {
|
||||||
|
/**
|
||||||
|
* Authenticate - check if the passwords are the same
|
||||||
|
*
|
||||||
|
* @param {String} plainText
|
||||||
|
* @return {Boolean}
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
authenticate: function(plainText) {
|
||||||
|
return this.encryptPassword(plainText) === this.hashed_password;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make salt
|
||||||
|
*
|
||||||
|
* @return {String}
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
makeSalt: function() {
|
||||||
|
return Math.round((new Date().valueOf() * Math.random())) + '';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt password
|
||||||
|
*
|
||||||
|
* @param {String} password
|
||||||
|
* @return {String}
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
encryptPassword: function(password) {
|
||||||
|
if (!password) return '';
|
||||||
|
return crypto.createHmac('sha1', this.salt).update(password).digest('hex');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mongoose.model('User', UserSchema);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Article Schema
|
||||||
|
*/
|
||||||
|
var ArticleSchema = new Schema({
|
||||||
|
created: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
trim: true
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
trim: true
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: Schema.ObjectId,
|
||||||
|
ref: 'User'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validations
|
||||||
|
*/
|
||||||
|
ArticleSchema.path('title').validate(function(title) {
|
||||||
|
return title.length;
|
||||||
|
}, 'Title cannot be blank');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Statics
|
||||||
|
*/
|
||||||
|
ArticleSchema.statics = {
|
||||||
|
load: function(id, cb) {
|
||||||
|
this.findOne({
|
||||||
|
_id: id
|
||||||
|
}).populate('user', 'name username').exec(cb);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mongoose.model('Article', ArticleSchema);
|
||||||
|
|
||||||
//bootstrap passport config
|
//bootstrap passport config
|
||||||
require('./config/passport')(passport);
|
var LocalStrategy = require('passport-local').Strategy,
|
||||||
|
TwitterStrategy = require('passport-twitter').Strategy,
|
||||||
|
FacebookStrategy = require('passport-facebook').Strategy,
|
||||||
|
GitHubStrategy = require('passport-github').Strategy,
|
||||||
|
GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
|
||||||
|
|
||||||
|
//Serialize sessions
|
||||||
|
passport.serializeUser(function(user, done) {
|
||||||
|
done(null, user.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
passport.deserializeUser(function(id, done) {
|
||||||
|
User.findOne({
|
||||||
|
_id: id
|
||||||
|
}, function(err, user) {
|
||||||
|
done(err, user);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//Use local strategy
|
||||||
|
passport.use(new LocalStrategy({
|
||||||
|
usernameField: 'email',
|
||||||
|
passwordField: 'password'
|
||||||
|
},
|
||||||
|
function(email, password, done) {
|
||||||
|
User.findOne({
|
||||||
|
email: email
|
||||||
|
}, function(err, user) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
if (!user) {
|
||||||
|
return done(null, false, {
|
||||||
|
message: 'Unknown user'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!user.authenticate(password)) {
|
||||||
|
return done(null, false, {
|
||||||
|
message: 'Invalid password'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return done(null, user);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
//Use twitter strategy
|
||||||
|
passport.use(new TwitterStrategy({
|
||||||
|
consumerKey: config.twitter.clientID,
|
||||||
|
consumerSecret: config.twitter.clientSecret,
|
||||||
|
callbackURL: config.twitter.callbackURL
|
||||||
|
},
|
||||||
|
function(token, tokenSecret, profile, done) {
|
||||||
|
User.findOne({
|
||||||
|
'twitter.id_str': profile.id
|
||||||
|
}, function(err, user) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
if (!user) {
|
||||||
|
user = new User({
|
||||||
|
name: profile.displayName,
|
||||||
|
username: profile.username,
|
||||||
|
provider: 'twitter',
|
||||||
|
twitter: profile._json
|
||||||
|
});
|
||||||
|
user.save(function(err) {
|
||||||
|
if (err) console.log(err);
|
||||||
|
return done(err, user);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return done(err, user);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
//Use facebook strategy
|
||||||
|
passport.use(new FacebookStrategy({
|
||||||
|
clientID: config.facebook.clientID,
|
||||||
|
clientSecret: config.facebook.clientSecret,
|
||||||
|
callbackURL: config.facebook.callbackURL
|
||||||
|
},
|
||||||
|
function(accessToken, refreshToken, profile, done) {
|
||||||
|
User.findOne({
|
||||||
|
'facebook.id': profile.id
|
||||||
|
}, function(err, user) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
if (!user) {
|
||||||
|
user = new User({
|
||||||
|
name: profile.displayName,
|
||||||
|
email: profile.emails[0].value,
|
||||||
|
username: profile.username,
|
||||||
|
provider: 'facebook',
|
||||||
|
facebook: profile._json
|
||||||
|
});
|
||||||
|
user.save(function(err) {
|
||||||
|
if (err) console.log(err);
|
||||||
|
return done(err, user);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return done(err, user);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
//Use github strategy
|
||||||
|
passport.use(new GitHubStrategy({
|
||||||
|
clientID: config.github.clientID,
|
||||||
|
clientSecret: config.github.clientSecret,
|
||||||
|
callbackURL: config.github.callbackURL
|
||||||
|
},
|
||||||
|
function(accessToken, refreshToken, profile, done) {
|
||||||
|
User.findOne({
|
||||||
|
'github.id': profile.id
|
||||||
|
}, function(err, user) {
|
||||||
|
if (!user) {
|
||||||
|
user = new User({
|
||||||
|
name: profile.displayName,
|
||||||
|
email: profile.emails[0].value,
|
||||||
|
username: profile.username,
|
||||||
|
provider: 'github',
|
||||||
|
github: profile._json
|
||||||
|
});
|
||||||
|
user.save(function(err) {
|
||||||
|
if (err) console.log(err);
|
||||||
|
return done(err, user);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return done(err, user);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
//Use google strategy
|
||||||
|
passport.use(new GoogleStrategy({
|
||||||
|
clientID: config.google.clientID,
|
||||||
|
clientSecret: config.google.clientSecret,
|
||||||
|
callbackURL: config.google.callbackURL
|
||||||
|
},
|
||||||
|
function(accessToken, refreshToken, profile, done) {
|
||||||
|
User.findOne({
|
||||||
|
'google.id': profile.id
|
||||||
|
}, function(err, user) {
|
||||||
|
if (!user) {
|
||||||
|
user = new User({
|
||||||
|
name: profile.displayName,
|
||||||
|
email: profile.emails[0].value,
|
||||||
|
username: profile.username,
|
||||||
|
provider: 'google',
|
||||||
|
google: profile._json
|
||||||
|
});
|
||||||
|
user.save(function(err) {
|
||||||
|
if (err) console.log(err);
|
||||||
|
return done(err, user);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return done(err, user);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
||||||
@ -48,6 +393,7 @@ var app = express();
|
|||||||
* Express Settings
|
* Express Settings
|
||||||
*/
|
*/
|
||||||
app.set('showStackError', true);
|
app.set('showStackError', true);
|
||||||
|
app.set('port', process.env.PORT || 3000);
|
||||||
app.locals.pretty = true;
|
app.locals.pretty = true;
|
||||||
app.use(express.compress({
|
app.use(express.compress({
|
||||||
filter: function(req, res) {
|
filter: function(req, res) {
|
||||||
@ -90,23 +436,61 @@ app.use(function(err, req, res, next) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.use(function(req, res, next) {
|
app.use(function(req, res, next) {
|
||||||
res.status(404).render('404', {
|
res.status(404).render('404', {
|
||||||
url: req.originalUrl,
|
url: req.originalUrl,
|
||||||
error: 'Not found'
|
error: 'Not found'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
//Bootstrap routes
|
//User Routes
|
||||||
require('./config/routes')(app, passport, auth);
|
app.post('/users', users.create);
|
||||||
|
app.get('/users/me', users.me);
|
||||||
|
app.get('/users/:userId', users.show);
|
||||||
|
|
||||||
//Start the app by listening on <port>
|
//Setting the facebook oauth routes
|
||||||
var port = process.env.PORT || config.port;
|
app.get('/auth/facebook', passport.authenticate('facebook', {
|
||||||
app.listen(port);
|
scope: ['email', 'user_about_me'],
|
||||||
console.log('Express app started on port ' + port);
|
failureRedirect: '/signin'
|
||||||
|
}), users.signin);
|
||||||
|
|
||||||
//Initializing logger
|
app.get('/auth/facebook/callback', passport.authenticate('facebook', {
|
||||||
logger.init(app, passport, mongoose);
|
failureRedirect: '/signin'
|
||||||
|
}), users.authCallback);
|
||||||
|
|
||||||
//expose app
|
//Setting the github oauth routes
|
||||||
exports = module.exports = app;
|
app.get('/auth/github', passport.authenticate('github', { failureRedirect: '/signin' }), users.signin);
|
||||||
|
app.get('/auth/github/callback', passport.authenticate('github', { failureRedirect: '/signin' }), users.authCallback);
|
||||||
|
|
||||||
|
//Setting the twitter oauth routes
|
||||||
|
app.get('/auth/twitter', passport.authenticate('twitter', { failureRedirect: '/signin' }), users.signin);
|
||||||
|
app.get('/auth/twitter/callback', passport.authenticate('twitter', { failureRedirect: '/signin' }), users.authCallback);
|
||||||
|
|
||||||
|
//Setting the google oauth routes
|
||||||
|
app.get('/auth/google', passport.authenticate('google', {
|
||||||
|
failureRedirect: '/signin',
|
||||||
|
scope: [
|
||||||
|
'https://www.googleapis.com/auth/userinfo.profile',
|
||||||
|
'https://www.googleapis.com/auth/userinfo.email'
|
||||||
|
]
|
||||||
|
}), users.signin);
|
||||||
|
app.get('/auth/google/callback', passport.authenticate('google', { failureRedirect: '/signin' }), users.authCallback);
|
||||||
|
|
||||||
|
//Finish with setting up the userId param
|
||||||
|
app.param('userId', users.user);
|
||||||
|
|
||||||
|
//Article Routes
|
||||||
|
var articles = require('../app/controllers/articles');
|
||||||
|
app.get('/articles', articles.all);
|
||||||
|
app.post('/articles', auth.requiresLogin, articles.create);
|
||||||
|
app.get('/articles/:articleId', articles.show);
|
||||||
|
app.put('/articles/:articleId', auth.requiresLogin, auth.article.hasAuthorization, articles.update);
|
||||||
|
app.del('/articles/:articleId', auth.requiresLogin, auth.article.hasAuthorization, articles.destroy);
|
||||||
|
|
||||||
|
|
||||||
|
//Home route
|
||||||
|
var index = require('../app/controllers/index');
|
||||||
|
app.get('/', index.render);
|
||||||
|
|
||||||
|
console.log('Express app started on port ' + app.get('port'));
|
||||||
|
app.listen(app.get('port'));
|
||||||
|
@ -1,65 +0,0 @@
|
|||||||
/**
|
|
||||||
* Module dependencies.
|
|
||||||
*/
|
|
||||||
var should = require('should'),
|
|
||||||
app = require('../../server'),
|
|
||||||
mongoose = require('mongoose'),
|
|
||||||
User = mongoose.model('User'),
|
|
||||||
Article = mongoose.model('Article');
|
|
||||||
|
|
||||||
//Globals
|
|
||||||
var user;
|
|
||||||
var article;
|
|
||||||
|
|
||||||
//The tests
|
|
||||||
describe('<Unit Test>', function() {
|
|
||||||
describe('Model Article:', function() {
|
|
||||||
beforeEach(function(done) {
|
|
||||||
user = new User({
|
|
||||||
name: 'Full name',
|
|
||||||
email: 'test@test.com',
|
|
||||||
username: 'user',
|
|
||||||
password: 'password'
|
|
||||||
});
|
|
||||||
|
|
||||||
user.save(function(err) {
|
|
||||||
article = new Article({
|
|
||||||
title: 'Article Title',
|
|
||||||
content: 'Article Content',
|
|
||||||
user: user
|
|
||||||
});
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Method Save', function() {
|
|
||||||
it('should be able to save without problems', function(done) {
|
|
||||||
return article.save(function(err) {
|
|
||||||
should.not.exist(err);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to show an error when try to save without title', function(done) {
|
|
||||||
article.title = '';
|
|
||||||
|
|
||||||
return article.save(function(err) {
|
|
||||||
should.exist(err);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(function(done) {
|
|
||||||
Article.remove({});
|
|
||||||
User.remove({});
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
after(function(done){
|
|
||||||
Article.remove().exec();
|
|
||||||
User.remove().exec();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,66 +0,0 @@
|
|||||||
/**
|
|
||||||
* Module dependencies.
|
|
||||||
*/
|
|
||||||
var should = require('should'),
|
|
||||||
app = require('../../server'),
|
|
||||||
mongoose = require('mongoose'),
|
|
||||||
User = mongoose.model('User');
|
|
||||||
|
|
||||||
//Globals
|
|
||||||
var user;
|
|
||||||
|
|
||||||
//The tests
|
|
||||||
describe('<Unit Test>', function() {
|
|
||||||
describe('Model User:', function() {
|
|
||||||
before(function(done) {
|
|
||||||
user = new User({
|
|
||||||
name: 'Full name',
|
|
||||||
email: 'test@test.com',
|
|
||||||
username: 'user',
|
|
||||||
password: 'password'
|
|
||||||
});
|
|
||||||
user2 = new User({
|
|
||||||
name: 'Full name',
|
|
||||||
email: 'test@test.com',
|
|
||||||
username: 'user',
|
|
||||||
password: 'password'
|
|
||||||
});
|
|
||||||
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Method Save', function() {
|
|
||||||
it('should begin with no users', function(done) {
|
|
||||||
User.find({}, function(err, users) {
|
|
||||||
users.should.have.length(0);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to save whithout problems', function(done) {
|
|
||||||
user.save(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should fail to save an existing user again', function(done) {
|
|
||||||
user.save();
|
|
||||||
return user2.save(function(err) {
|
|
||||||
should.exist(err);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to show an error when try to save without name', function(done) {
|
|
||||||
user.name = '';
|
|
||||||
return user.save(function(err) {
|
|
||||||
should.exist(err);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
after(function(done) {
|
|
||||||
User.remove().exec();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
Reference in New Issue
Block a user