├── .slugignore ├── public ├── img │ ├── .gitignore │ ├── apple │ │ ├── splash.png │ │ ├── splash2x.png │ │ ├── apple-touch-icon.png │ │ ├── apple-touch-icon-precomposed.png │ │ ├── apple-touch-icon-57x57-precomposed.png │ │ ├── apple-touch-icon-72x72-precomposed.png │ │ ├── apple-touch-icon-114x114-precomposed.png │ │ └── apple-touch-icon-144x144-precomposed.png │ ├── icons │ │ ├── favicon.ico │ │ ├── github.png │ │ ├── google.png │ │ ├── twitter.png │ │ └── facebook.png │ ├── loaders │ │ └── loader.gif │ └── sprites │ │ ├── glyphicons-halflings.png │ │ └── glyphicons-halflings-white.png ├── js │ ├── directives.js │ ├── filters.js │ ├── controllers │ │ ├── index.js │ │ ├── header.js │ │ └── users.js │ ├── app.js │ ├── services │ │ ├── users.js │ │ └── global.js │ ├── init.js │ └── config.js ├── robots.txt ├── pics │ └── Thumbs.db ├── views │ ├── index.html │ ├── users │ │ ├── list.html │ │ └── view.html │ └── header.html ├── humans.txt ├── css │ └── common.css └── sass │ └── common.scss ├── Procfile ├── .bowerrc ├── app ├── views │ ├── index.jade │ ├── 500.jade │ ├── 404.jade │ ├── layouts │ │ └── default.jade │ ├── users │ │ ├── signin.jade │ │ ├── auth.jade │ │ └── signup.jade │ └── includes │ │ ├── head.jade │ │ └── foot.jade ├── controllers │ ├── index.js │ └── users.js └── models │ └── user.js ├── .gitignore ├── .travis.yml ├── README.md ├── config ├── env │ ├── all.js │ ├── travis.json │ ├── test.json │ ├── development.json │ └── production.json ├── config.js ├── middlewares │ └── authorization.js ├── express.js ├── routes.js └── passport.js ├── bower.json ├── Makefile ├── test └── user │ └── model.js ├── server.js ├── package.json ├── .jshintrc └── gruntfile.js /.slugignore: -------------------------------------------------------------------------------- 1 | /test -------------------------------------------------------------------------------- /public/img/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/js/directives.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/js/filters.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: ./node_modules/.bin/forever -m 5 server.js 2 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org/ 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "public/lib", 3 | "json": "bower.json" 4 | } 5 | -------------------------------------------------------------------------------- /app/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layouts/default 2 | 3 | block content 4 | section(data-ng-view) -------------------------------------------------------------------------------- /public/pics/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaeldaime/restful-api/master/public/pics/Thumbs.db -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .nodemonignore 3 | .sass-cache/ 4 | node_modules/ 5 | public/lib 6 | public/pics/* -------------------------------------------------------------------------------- /public/img/apple/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaeldaime/restful-api/master/public/img/apple/splash.png -------------------------------------------------------------------------------- /public/img/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaeldaime/restful-api/master/public/img/icons/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaeldaime/restful-api/master/public/img/icons/github.png -------------------------------------------------------------------------------- /public/img/icons/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaeldaime/restful-api/master/public/img/icons/google.png -------------------------------------------------------------------------------- /public/img/icons/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaeldaime/restful-api/master/public/img/icons/twitter.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | env: 5 | - NODE_ENV=travis 6 | services: 7 | - mongodb -------------------------------------------------------------------------------- /public/img/apple/splash2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaeldaime/restful-api/master/public/img/apple/splash2x.png -------------------------------------------------------------------------------- /public/img/icons/facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaeldaime/restful-api/master/public/img/icons/facebook.png -------------------------------------------------------------------------------- /public/img/loaders/loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaeldaime/restful-api/master/public/img/loaders/loader.gif -------------------------------------------------------------------------------- /public/views/index.html: -------------------------------------------------------------------------------- 1 |
2 |

This is the home view

3 |
-------------------------------------------------------------------------------- /public/img/apple/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaeldaime/restful-api/master/public/img/apple/apple-touch-icon.png -------------------------------------------------------------------------------- /public/img/sprites/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaeldaime/restful-api/master/public/img/sprites/glyphicons-halflings.png -------------------------------------------------------------------------------- /public/img/apple/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaeldaime/restful-api/master/public/img/apple/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /public/img/sprites/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaeldaime/restful-api/master/public/img/sprites/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /public/img/apple/apple-touch-icon-57x57-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaeldaime/restful-api/master/public/img/apple/apple-touch-icon-57x57-precomposed.png -------------------------------------------------------------------------------- /public/img/apple/apple-touch-icon-72x72-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaeldaime/restful-api/master/public/img/apple/apple-touch-icon-72x72-precomposed.png -------------------------------------------------------------------------------- /public/img/apple/apple-touch-icon-114x114-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaeldaime/restful-api/master/public/img/apple/apple-touch-icon-114x114-precomposed.png -------------------------------------------------------------------------------- /public/img/apple/apple-touch-icon-144x144-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rafaeldaime/restful-api/master/public/img/apple/apple-touch-icon-144x144-precomposed.png -------------------------------------------------------------------------------- /public/js/controllers/index.js: -------------------------------------------------------------------------------- 1 | angular.module('mean.system').controller('IndexController', ['$scope', 'Global', function ($scope, Global) { 2 | $scope.global = Global; 3 | }]); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Install dependencies: 3 | 4 | $ npm install 5 | 6 | $ grunt 7 | 8 | $ node server 9 | 10 | http://localhost:3000 11 | 12 | http://rest-api-rafadev.herokuapp.com/ -------------------------------------------------------------------------------- /public/js/app.js: -------------------------------------------------------------------------------- 1 | window.app = angular.module('mean', ['ngCookies', 'ngResource', 'ui.bootstrap', 'ui.route', 'mean.system', 'mean.users']); 2 | 3 | angular.module('mean.system', []); 4 | angular.module('mean.users', []); -------------------------------------------------------------------------------- /config/env/all.js: -------------------------------------------------------------------------------- 1 | var path = require('path'), 2 | rootPath = path.normalize(__dirname + '/../..'); 3 | 4 | module.exports = { 5 | root: rootPath, 6 | port: process.env.PORT || 3000, 7 | db: process.env.MONGOHQ_URL 8 | } 9 | -------------------------------------------------------------------------------- /app/views/500.jade: -------------------------------------------------------------------------------- 1 | extends layouts/default 2 | 3 | block main 4 | h1 Oops something went wrong 5 | br 6 | span 500 7 | 8 | block content 9 | #error-message-box 10 | #error-stack-trace 11 | pre 12 | code!= error 13 | -------------------------------------------------------------------------------- /app/views/404.jade: -------------------------------------------------------------------------------- 1 | extends layouts/default 2 | 3 | block main 4 | h1 Oops something went wrong 5 | br 6 | span 404 7 | 8 | block content 9 | #error-message-box 10 | #error-stack-trace 11 | pre 12 | code!= error 13 | 14 | -------------------------------------------------------------------------------- /public/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | HTML5, CSS3 15 | jQuery, Modernizr 16 | -------------------------------------------------------------------------------- /config/config.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'); 2 | 3 | // Load app configuration 4 | 5 | module.exports = _.extend( 6 | require(__dirname + '/../config/env/all.js'), 7 | require(__dirname + '/../config/env/' + process.env.NODE_ENV + '.json') || {}) ; 8 | 9 | module.exports.maxAge = 0; 10 | -------------------------------------------------------------------------------- /public/css/common.css: -------------------------------------------------------------------------------- 1 | .navbar .nav>li>a.brand{padding-left:20px;margin-left:0}.content{margin-top:50px;width:100%}footer{position:fixed;left:0px;bottom:0px;height:30px;width:100%;background:#ddd;-webkit-box-shadow:0 8px 6px 6px black;-moz-box-shadow:0 8px 6px 6px black;box-shadow:0 8px 6px 6px black}footer p{padding:5px 0 12px 10px} 2 | -------------------------------------------------------------------------------- /public/js/services/users.js: -------------------------------------------------------------------------------- 1 | //Uers service used for users REST endpoint 2 | angular.module('mean.users').factory("Users", ['$resource', function($resource) { 3 | return $resource('users/:userId', { 4 | userId: '@_id' 5 | }, { 6 | update: { 7 | method: 'PUT' 8 | } 9 | }); 10 | }]); -------------------------------------------------------------------------------- /public/js/services/global.js: -------------------------------------------------------------------------------- 1 | angular.module('mean.system').factory("Global", [function() { 2 | var _this = this; 3 | 4 | 5 | // Until the header gets my user (findMe) 6 | _this._data = { 7 | user: null, 8 | authenticated: false 9 | }; 10 | 11 | 12 | 13 | 14 | return _this._data; 15 | }]); -------------------------------------------------------------------------------- /app/controllers/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var mongoose = require('mongoose'), 5 | async = require('async'), 6 | _ = require('underscore'); 7 | 8 | 9 | exports.render = function(req, res) { 10 | res.setHeader('Cache-Control', 'public, max-age=0'); 11 | res.render('index', { 12 | user: req.user ? JSON.stringify(req.user) : "null" 13 | }); 14 | }; -------------------------------------------------------------------------------- /public/js/init.js: -------------------------------------------------------------------------------- 1 | window.bootstrap = function() { 2 | angular.bootstrap(document, ['mean']); 3 | }; 4 | 5 | window.init = function() { 6 | window.bootstrap(); 7 | }; 8 | 9 | $(document).ready(function() { 10 | //Fixing facebook bug with redirect 11 | if (window.location.hash == "#_=_") window.location.hash = ""; 12 | 13 | //Then init the app 14 | window.init(); 15 | 16 | }); -------------------------------------------------------------------------------- /app/views/layouts/default.jade: -------------------------------------------------------------------------------- 1 | !!! 5 2 | 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') 3 | include ../includes/head 4 | body 5 | header.navbar.navbar-fixed-top.navbar-inverse(data-ng-include="'views/header.html'") 6 | section.content 7 | section.container 8 | block content 9 | include ../includes/foot 10 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angularJS-IL", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "bootstrap": "2.3.2", 6 | "angular": "~1.0.6", 7 | "angular-resource": "~1.0.6", 8 | "angular-cookies": "~1.0.6", 9 | "angular-bootstrap": "~0.4.0", 10 | "angular-ui-utils": "0.0.4", 11 | "json3": "~3.2.4", 12 | "jquery": "~1.9.1", 13 | "jquery-form": "~3.37.0" 14 | }, 15 | "devDependencies": { 16 | "angular-mocks": "~1.0.5", 17 | "angular-scenario": "~1.0.5" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /public/sass/common.scss: -------------------------------------------------------------------------------- 1 | .navbar .nav> li > a.brand { 2 | padding-left: 20px; 3 | margin-left: 0; 4 | } 5 | 6 | .content { 7 | margin-top: 50px; 8 | width: 100%; 9 | } 10 | 11 | footer { 12 | position:fixed; 13 | left:0px; 14 | bottom:0px; 15 | height:30px; 16 | width:100%; 17 | background:#ddd; 18 | -webkit-box-shadow: 0 8px 6px 6px black; 19 | -moz-box-shadow: 0 8px 6px 6px black; 20 | box-shadow: 0 8px 6px 6px black; 21 | } 22 | footer p{ 23 | padding: 5px 0 12px 10px; 24 | } -------------------------------------------------------------------------------- /public/views/users/list.html: -------------------------------------------------------------------------------- 1 |
2 |
    3 |
  • 4 | Name: {{user.name}} / 5 | Username: {{user.username}} / 6 | email: {{user.email}} / 7 | {{user.created | date:'medium'}} 8 |
  • 9 |
10 |

No users yet.
Why don't you Create One?

11 |
-------------------------------------------------------------------------------- /config/middlewares/authorization.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Generic require login routing middleware 3 | */ 4 | exports.requiresLogin = function(req, res, next) { 5 | if (!req.isAuthenticated()) { 6 | return res.send(null); 7 | } 8 | next(); 9 | }; 10 | 11 | /** 12 | * User authorizations routing middleware 13 | */ 14 | exports.user = { 15 | hasAuthorization: function(req, res, next) { 16 | if (req.profile.id != req.user.id) { 17 | console.log('User is not authorized'); 18 | return res.send(null); 19 | } 20 | next(); 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /app/views/users/signin.jade: -------------------------------------------------------------------------------- 1 | extends auth 2 | 3 | block auth 4 | form.signin.form-horizontal(action="/users/session", method="post") 5 | p.error= message 6 | .control-group 7 | label.control-label(for='email') Email 8 | .controls 9 | input#email(type='text', name="email", placeholder='Email') 10 | 11 | .control-group 12 | label.control-label(for='password') Password 13 | .controls 14 | input#password(type='password', name="password", placeholder='Password') 15 | 16 | .form-actions 17 | button.btn.btn-primary(type='submit') Sign in 18 |   19 | | or  20 | a.show-signup(href="/signup") Sign up 21 | -------------------------------------------------------------------------------- /public/js/config.js: -------------------------------------------------------------------------------- 1 | //Setting up route 2 | window.app.config(['$routeProvider', 3 | function($routeProvider) { 4 | $routeProvider. 5 | when('/users', { 6 | templateUrl: 'views/users/list.html' 7 | }). 8 | when('/users/:userId', { 9 | templateUrl: 'views/users/view.html' 10 | }). 11 | when('/', { 12 | templateUrl: 'views/index.html' 13 | }). 14 | otherwise({ 15 | redirectTo: '/' 16 | }); 17 | } 18 | ]); 19 | 20 | //Setting HTML5 Location Mode 21 | window.app.config(['$locationProvider', 22 | function($locationProvider) { 23 | $locationProvider.hashPrefix("!"); 24 | } 25 | ]); -------------------------------------------------------------------------------- /public/js/controllers/header.js: -------------------------------------------------------------------------------- 1 | angular.module('mean.system').controller('HeaderController', ['$scope', 'Global', 'Users', function ($scope, Global, Users) { 2 | $scope.global = Global; 3 | 4 | $scope.menu = [{ 5 | "title": "Users", 6 | "link": "users" 7 | }]; 8 | 9 | 10 | $scope.findMe = function() { 11 | Users.get({ 12 | userId: 'me' 13 | }, function(user) { 14 | if (typeof user._id != 'undefined'){ 15 | $scope.global.user = user; 16 | $scope.global.authenticated = true; 17 | console.log("GET USER BY HEADERCONTROLLER!"); 18 | console.log($scope.global.user); 19 | } 20 | }); 21 | }; 22 | }]); -------------------------------------------------------------------------------- /app/views/users/auth.jade: -------------------------------------------------------------------------------- 1 | extends ../layouts/default 2 | 3 | block content 4 | .row 5 | .offset1.span5 6 | a(href="/auth/facebook") 7 | img(src="/img/icons/facebook.png") 8 | a(href="/auth/github") 9 | img(src="/img/icons/github.png") 10 | a(href="/auth/twitter") 11 | img(src="/img/icons/twitter.png") 12 | //- This comment won't be visible 13 | //- a(href="/auth/google") 14 | //- img(src="/img/icons/google.png") 15 | .span6 16 | if (typeof errors !== 'undefined') 17 | .fade.in.alert.alert-block.alert-error 18 | a.close(data-dismiss="alert", href="javascript:void(0)") x 19 | ul 20 | each error in errors 21 | li= error.type 22 | 23 | block auth 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | REPORTER = spec 2 | NODEARGS = 3 | test: 4 | @if [ ! -n "$(NODE_ENV)" ]; then NODE_ENV=test NODE_PATH=lib ./node_modules/grunt-nodemon/node_modules/.bin/nodemon -x ./node_modules/.bin/mocha -R $(REPORTER) -t 15000 --recursive test $(NODEARGS); else NODE_PATH=lib ./node_modules/.bin/mocha -R $(REPORTER) -t 15000 --recursive test $(NODEARGS); fi 5 | 6 | start: 7 | @if [ ! -n "$(NODE_ENV)" ]; then NODE_ENV=development NODE_PATH=lib ./node_modules/grunt-nodemon/node_modules/.bin/nodemon server.js $(NODEARGS) ; else NODE_PATH=lib ./node_modules/.bin/foreman start; fi 8 | 9 | mocha: 10 | NODE_PATH=lib ./node_modules/.bin/mocha -R $(REPORTER) -t 15000 --recursive test $(NODEARGS) 11 | 12 | repl: 13 | @NODE_ENV=development NODE_PATH=lib node --debug $(NODEARGS) 14 | 15 | webtest: 16 | @NODE_ENV=test NODE_PATH=lib ./node_modules/.bin/web-mocha test $(NODEARGS) 17 | 18 | .PHONY: jshint test repl webtest mocha 19 | -------------------------------------------------------------------------------- /config/env/travis.json: -------------------------------------------------------------------------------- 1 | { 2 | "db": "mongodb://localhost/mean-travis", 3 | "port": 3001, 4 | "app": { 5 | "name": "MEAN - A Modern Stack - Test on travis" 6 | }, 7 | "facebook": { 8 | "clientID": "APP_ID", 9 | "clientSecret": "APP_SECRET", 10 | "callbackURL": "http://localhost:3000/auth/facebook/callback" 11 | }, 12 | "twitter": { 13 | "clientID": "CONSUMER_KEY", 14 | "clientSecret": "CONSUMER_SECRET", 15 | "callbackURL": "http://localhost:3000/auth/twitter/callback" 16 | }, 17 | "github": { 18 | "clientID": "APP_ID", 19 | "clientSecret": "APP_SECRET", 20 | "callbackURL": "http://localhost:3000/auth/github/callback" 21 | }, 22 | "google": { 23 | "clientID": "APP_ID", 24 | "clientSecret": "APP_SECRET", 25 | "callbackURL": "http://localhost:3000/auth/google/callback" 26 | } 27 | } -------------------------------------------------------------------------------- /config/env/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "db": "mongodb://localhost/rafadev-dev", 3 | "app": { 4 | "name": "API RESTful - RafaDev - Development" 5 | }, 6 | "facebook": { 7 | "clientID": "604456682923333", 8 | "clientSecret": "9eacfd254f8d9702ff87da9cd06bb0d8", 9 | "callbackURL": "http://localhost:3000/auth/facebook/callback" 10 | }, 11 | "twitter": { 12 | "clientID": "cLPmH3mAoj7Siulagx8r1w", 13 | "clientSecret": "tDtFiDbKOfa2xApvubFjdpP3CZoGSKY1sESH4UqqYs", 14 | "callbackURL": "http://localhost:3000/auth/twitter/callback" 15 | }, 16 | "github": { 17 | "clientID": "8adf32d54d19693f56c6", 18 | "clientSecret": "6810459607763c761edd7dc4da2193e2448126b5", 19 | "callbackURL": "http://localhost:3000/auth/github/callback" 20 | }, 21 | "google": { 22 | "clientID": "620271931508.apps.googleusercontent.com", 23 | "clientSecret": "jaRYCYrtzCFgJXU8bdhZBA36", 24 | "callbackURL": "http://localhost:3000/auth/google/callback" 25 | } 26 | } -------------------------------------------------------------------------------- /config/env/development.json: -------------------------------------------------------------------------------- 1 | { 2 | "db": "mongodb://localhost/rafadev-dev", 3 | "app": { 4 | "name": "API RESTful - RafaDev - Development" 5 | }, 6 | "facebook": { 7 | "clientID": "604456682923333", 8 | "clientSecret": "9eacfd254f8d9702ff87da9cd06bb0d8", 9 | "callbackURL": "http://localhost:3000/auth/facebook/callback" 10 | }, 11 | "twitter": { 12 | "clientID": "cLPmH3mAoj7Siulagx8r1w", 13 | "clientSecret": "tDtFiDbKOfa2xApvubFjdpP3CZoGSKY1sESH4UqqYs", 14 | "callbackURL": "http://localhost:3000/auth/twitter/callback" 15 | }, 16 | "github": { 17 | "clientID": "8adf32d54d19693f56c6", 18 | "clientSecret": "6810459607763c761edd7dc4da2193e2448126b5", 19 | "callbackURL": "http://localhost:3000/auth/github/callback" 20 | }, 21 | "google": { 22 | "clientID": "620271931508.apps.googleusercontent.com", 23 | "clientSecret": "jaRYCYrtzCFgJXU8bdhZBA36", 24 | "callbackURL": "http://localhost:3000/auth/google/callback" 25 | } 26 | } -------------------------------------------------------------------------------- /config/env/production.json: -------------------------------------------------------------------------------- 1 | { 2 | "db": "mongodb://localhost/rafadev-dev", 3 | "app": { 4 | "name": "API RESTful - RafaDev - Development" 5 | }, 6 | "facebook": { 7 | "clientID": "604456682923333", 8 | "clientSecret": "9eacfd254f8d9702ff87da9cd06bb0d8", 9 | "callbackURL": "http://localhost:3000/auth/facebook/callback" 10 | }, 11 | "twitter": { 12 | "clientID": "cLPmH3mAoj7Siulagx8r1w", 13 | "clientSecret": "tDtFiDbKOfa2xApvubFjdpP3CZoGSKY1sESH4UqqYs", 14 | "callbackURL": "http://localhost:3000/auth/twitter/callback" 15 | }, 16 | "github": { 17 | "clientID": "8adf32d54d19693f56c6", 18 | "clientSecret": "6810459607763c761edd7dc4da2193e2448126b5", 19 | "callbackURL": "http://localhost:3000/auth/github/callback" 20 | }, 21 | "google": { 22 | "clientID": "620271931508.apps.googleusercontent.com", 23 | "clientSecret": "jaRYCYrtzCFgJXU8bdhZBA36", 24 | "callbackURL": "http://localhost:3000/auth/google/callback" 25 | } 26 | } -------------------------------------------------------------------------------- /app/views/users/signup.jade: -------------------------------------------------------------------------------- 1 | extends auth 2 | 3 | block auth 4 | form.signup.form-horizontal(action="/users", method="post") 5 | .control-group 6 | label.control-label(for='name') Full name 7 | .controls 8 | input#name(type='text', name="name", placeholder='Full name', value=user.name) 9 | 10 | .control-group 11 | label.control-label(for='email') Email 12 | .controls 13 | input#email(type='text', name="email", placeholder='Email', value=user.email) 14 | 15 | .control-group 16 | label.control-label(for='username') Username 17 | .controls 18 | input#username(type='text', name="username", placeholder='Username', value=user.username) 19 | 20 | .control-group 21 | label.control-label(for='password') Password 22 | .controls 23 | input#password(type='password', name="password", placeholder='Password') 24 | 25 | .form-actions 26 | button.btn.btn-primary(type='submit') Sign up 27 |   28 | | or  29 | a.show-login(href="/signin") login 30 | -------------------------------------------------------------------------------- /public/views/header.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/includes/head.jade: -------------------------------------------------------------------------------- 1 | head 2 | meta(charset='utf-8') 3 | meta(http-equiv='X-UA-Compatible', content='IE=edge,chrome=1') 4 | meta(name='viewport', content='width=device-width,initial-scale=1') 5 | 6 | title= appName+' - '+title 7 | meta(http-equiv='Content-type', content='text/html;charset=UTF-8') 8 | meta(name="keywords", content="RafaDev") 9 | meta(name="description", content="RafaDev") 10 | 11 | link(href='/img/icons/favicon.ico', rel='shortcut icon', type='image/x-icon') 12 | 13 | meta(property='fb:app_id', content='APP_ID') 14 | meta(property='og:title', content='#{appName} - #{title}') 15 | meta(property='og:description', content='RafaDev') 16 | meta(property='og:type', content='website') 17 | meta(property='og:url', content='APP_URL') 18 | meta(property='og:image', content='APP_LOGO') 19 | meta(property='og:site_name', content='RafaDev') 20 | meta(property='fb:admins', content='APP_ADMIN') 21 | 22 | link(rel='stylesheet', href='lib/bootstrap/docs/assets/css/bootstrap.css') 23 | link(rel='stylesheet', href='lib/bootstrap/docs/assets/css/bootstrap-responsive.css') 24 | link(rel='stylesheet', href='css/common.css') 25 | 26 | 27 | //if lt IE 9 28 | script(src='http://html5shim.googlecode.com/svn/trunk/html5.js') 29 | -------------------------------------------------------------------------------- /test/user/model.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var should = require('should'), 5 | app = require('../../server'), 6 | mongoose = require('mongoose'), 7 | User = mongoose.model('User'); 8 | 9 | //Globals 10 | var user; 11 | 12 | //The tests 13 | describe('', function() { 14 | describe('Model User:', function() { 15 | before(function(done) { 16 | user = new User({ 17 | name: 'Full name', 18 | email: 'test@test.com', 19 | username: 'user', 20 | password: 'password' 21 | }); 22 | 23 | done(); 24 | }); 25 | 26 | describe('Method Save', function() { 27 | it('should be able to save whithout problems', function(done) { 28 | return user.save(function(err) { 29 | should.not.exist(err); 30 | done(); 31 | }); 32 | }); 33 | 34 | it('should be able to show an error when try to save witout name', function(done) { 35 | user.name = ''; 36 | return user.save(function(err) { 37 | should.exist(err); 38 | done(); 39 | }); 40 | }); 41 | }); 42 | 43 | after(function(done) { 44 | done(); 45 | }); 46 | }); 47 | }); -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var express = require('express'), 5 | fs = require('fs'), 6 | passport = require('passport'), 7 | logger = require('mean-logger'); 8 | 9 | /** 10 | * Main application entry file. 11 | * Please note that the order of loading is important. 12 | */ 13 | 14 | //Load configurations 15 | //if test env, load example file 16 | var env = process.env.NODE_ENV = process.env.NODE_ENV || 'development', 17 | config = require('./config/config'), 18 | auth = require('./config/middlewares/authorization'), 19 | mongoose = require('mongoose'); 20 | 21 | //Bootstrap db connection 22 | var db = mongoose.connect(config.db); 23 | 24 | //Bootstrap models 25 | var models_path = __dirname + '/app/models'; 26 | fs.readdirSync(models_path).forEach(function(file) { 27 | require(models_path + '/' + file); 28 | }); 29 | 30 | //bootstrap passport config 31 | require('./config/passport')(passport); 32 | 33 | var app = express(); 34 | 35 | //express settings 36 | require('./config/express')(app, passport); 37 | 38 | //Bootstrap routes 39 | require('./config/routes')(app, passport, auth); 40 | 41 | //Start the app by listening on 42 | var port = config.port; 43 | app.listen(port); 44 | console.log('Express app started on port ' + port); 45 | 46 | //Initializing logger 47 | logger.init(app, passport, mongoose); 48 | 49 | //expose app 50 | exports = module.exports = app; -------------------------------------------------------------------------------- /app/views/includes/foot.jade: -------------------------------------------------------------------------------- 1 | script(type='text/javascript', src='lib/jquery/jquery.min.js') 2 | script(type='text/javascript', src='lib/jquery-form/jquery.form.js') 3 | 4 | //AngularJS 5 | script(type='text/javascript', src='lib/angular/angular.min.js') 6 | script(type='text/javascript', src='lib/angular-cookies/angular-cookies.min.js') 7 | script(type='text/javascript', src='lib/angular-resource/angular-resource.min.js') 8 | 9 | //Angular UI 10 | script(type='text/javascript', src='lib/angular-bootstrap/ui-bootstrap-tpls.min.js') 11 | script(type='text/javascript', src='lib/angular-ui-utils/modules/route/route.js') 12 | 13 | //Application Init 14 | script(type='text/javascript', src='js/app.js') 15 | script(type='text/javascript', src='js/config.js') 16 | script(type='text/javascript', src='js/directives.js') 17 | script(type='text/javascript', src='js/filters.js') 18 | 19 | //Application Services 20 | script(type='text/javascript', src='js/services/global.js') 21 | script(type='text/javascript', src='js/services/users.js') 22 | 23 | //Application Controllers 24 | script(type='text/javascript', src='js/controllers/users.js') 25 | script(type='text/javascript', src='js/controllers/index.js') 26 | script(type='text/javascript', src='js/controllers/header.js') 27 | script(type='text/javascript', src='js/init.js') 28 | 29 | //Livereload script rendered 30 | script(type='text/javascript', src='http://localhost:35729/livereload.js') 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RESTfulRafaDev", 3 | "description": "RESTful API - RafaDev", 4 | "version": "1.0.0", 5 | "private": false, 6 | "author": "RafaDev", 7 | "engines": { 8 | "node": "0.10.x", 9 | "npm": "1.2.x" 10 | }, 11 | "scripts": { 12 | "start": "make start", 13 | "test": "make test", 14 | "postinstall": "node node_modules/bower/bin/bower install" 15 | }, 16 | "dependencies": { 17 | "express": "latest", 18 | "jade": "latest", 19 | "mongoose": "latest", 20 | "connect-mongo": "latest", 21 | "connect-flash": "latest", 22 | "passport": "latest", 23 | "passport-local": "latest", 24 | "passport-facebook": "latest", 25 | "passport-twitter": "latest", 26 | "passport-github": "latest", 27 | "passport-google-oauth": "latest", 28 | "underscore": "latest", 29 | "async": "latest", 30 | "view-helpers": "latest", 31 | "mean-logger": "latest", 32 | "bower": "latest", 33 | "forever": "latest", 34 | "foreman": "0.0.25", 35 | "random-string": "latest" 36 | }, 37 | "devDependencies": { 38 | "supertest": "latest", 39 | "should": "latest", 40 | "mocha": "latest", 41 | "web-mocha": "0.0.10", 42 | "grunt": "~0.4.1", 43 | "grunt-contrib-compass": "~0.3.0", 44 | "grunt-contrib-watch": "~0.4.4", 45 | "grunt-contrib-jshint": "~0.6.0", 46 | "grunt-nodemon": "0.0.8", 47 | "grunt-concurrent": "~0.3.0" 48 | } 49 | } -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, // Enable globals available when code is running inside of the NodeJS runtime environment. 3 | "browser": true, // Standard browser globals e.g. `window`, `document`. 4 | "es5": true, // Allow EcmaScript 5 syntax. 5 | "esnext": true, // Allow ES.next specific features such as `const` and `let`. 6 | "bitwise": false, // Prohibit bitwise operators (&, |, ^, etc.). 7 | "camelcase": false, // Permit only camelcase for `var` and `object indexes`. 8 | "curly": false, // Require {} for every new block or scope. 9 | "eqeqeq": true, // Require triple equals i.e. `===`. 10 | "immed": true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );` 11 | "latedef": true, // Prohibit variable use before definition. 12 | "newcap": true, // Require capitalization of all constructor functions e.g. `new F()`. 13 | "noarg": true, // Prohibit use of `arguments.caller` and `arguments.callee`. 14 | "quotmark": "single", // Define quotes to string values. 15 | "regexp": true, // Prohibit `.` and `[^...]` in regular expressions. 16 | "undef": true, // Require all non-global variables be declared before they are used. 17 | "unused": true, // Warn unused variables. 18 | "strict": false, // Require `use strict` pragma in every file. 19 | "trailing": true, // Prohibit trailing whitespaces. 20 | "smarttabs": false, // Suppresses warnings about mixed tabs and spaces 21 | "globals": { // Globals variables. 22 | "angular": true 23 | }, 24 | "predef": [ // Extra globals. 25 | "define", 26 | "require", 27 | "exports", 28 | "module", 29 | "describe", 30 | "before", 31 | "beforeEach", 32 | "after", 33 | "afterEach", 34 | "it" 35 | ], 36 | "indent": 4, // Specify indentation spacing 37 | "maxlen": 120, // Max line lenght 38 | "devel": false, // Allow development statements e.g. `console.log();`. 39 | "noempty": true // Prohibit use of empty blocks. 40 | } -------------------------------------------------------------------------------- /config/express.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var express = require('express'), 5 | mongoStore = require('connect-mongo')(express), 6 | flash = require('connect-flash'), 7 | helpers = require('view-helpers'), 8 | config = require('./config'); 9 | 10 | module.exports = function(app, passport) { 11 | app.set('showStackError', true); 12 | 13 | //Should be placed before express.static 14 | app.use(express.compress({ 15 | filter: function(req, res) { 16 | return (/json|text|javascript|css/).test(res.getHeader('Content-Type')); 17 | }, 18 | level: 9 19 | })); 20 | 21 | //Setting the fav icon and static folder 22 | app.use(express.favicon()); 23 | app.use(express.static(config.root + '/public')); 24 | 25 | //Don't use logger for test env 26 | if (process.env.NODE_ENV !== 'test') { 27 | app.use(express.logger('dev')); 28 | } 29 | 30 | //Set views path, template engine and default layout 31 | app.set('views', config.root + '/app/views'); 32 | app.set('view engine', 'jade'); 33 | 34 | //Enable jsonp 35 | app.enable("jsonp callback"); 36 | 37 | app.configure(function() { 38 | //cookieParser should be above session 39 | app.use(express.cookieParser()); 40 | 41 | //bodyParser should be above methodOverride 42 | app.use(express.bodyParser()); 43 | app.use(express.methodOverride()); 44 | 45 | //express/mongo session storage 46 | app.use(express.session({ 47 | secret: 'MEAN', 48 | store: new mongoStore({ 49 | url: config.db, 50 | collection: 'sessions' 51 | }) 52 | })); 53 | 54 | //connect flash for flash messages 55 | app.use(flash()); 56 | 57 | //dynamic helpers 58 | app.use(helpers(config.app.name)); 59 | 60 | //use passport session 61 | app.use(passport.initialize()); 62 | app.use(passport.session()); 63 | 64 | //routes should be at the last 65 | app.use(app.router); 66 | 67 | //Assume "not found" in the error msgs is a 404. this is somewhat silly, but valid, you can do whatever you like, set properties, use instanceof etc. 68 | app.use(function(err, req, res, next) { 69 | //Treat as 404 70 | if (~err.message.indexOf('not found')) return next(); 71 | 72 | //Log it 73 | console.error(err.stack); 74 | 75 | //Error page 76 | res.status(500).render('500', { 77 | error: err.stack 78 | }); 79 | }); 80 | 81 | //Assume 404 since no middleware responded 82 | app.use(function(req, res, next) { 83 | res.status(404).render('404', { 84 | url: req.originalUrl, 85 | error: 'Not found' 86 | }); 87 | }); 88 | 89 | }); 90 | }; -------------------------------------------------------------------------------- /config/routes.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | 3 | module.exports = function(app, passport, auth) { 4 | //User Routes 5 | var users = require('../app/controllers/users'); 6 | app.get('/signin', users.signin); 7 | app.get('/signup', users.signup); 8 | app.get('/signout', users.signout); 9 | 10 | //Setting up the users api 11 | app.post('/users', users.create); 12 | // GET all users 13 | app.get('/users', auth.requiresLogin, users.all); 14 | 15 | app.post('/users/session', passport.authenticate('local', { 16 | failureRedirect: '/signin', 17 | failureFlash: 'Invalid email or password.' 18 | }), users.session); 19 | 20 | app.get('/users/me', auth.requiresLogin, users.me); 21 | app.get('/users/:userId', auth.requiresLogin, users.show); 22 | //Modifying the user account *restricted* 23 | app.put('/users/:userId', auth.requiresLogin, auth.user.hasAuthorization, users.update); 24 | 25 | // Friends in common with 2 users 26 | app.get('/users/:userId/common/:userId2', auth.requiresLogin, users.commonFriends); 27 | 28 | // User posting a pic 29 | app.post('/users/:userId/pic', auth.requiresLogin, auth.user.hasAuthorization, users.addPic); 30 | 31 | //Setting the facebook oauth routes 32 | app.get('/auth/facebook', passport.authenticate('facebook', { 33 | scope: ['email', 'user_about_me'], 34 | failureRedirect: '/signin' 35 | }), users.signin); 36 | 37 | app.get('/auth/facebook/callback', passport.authenticate('facebook', { 38 | failureRedirect: '/signin' 39 | }), users.authCallback); 40 | 41 | //Setting the github oauth routes 42 | app.get('/auth/github', passport.authenticate('github', { 43 | failureRedirect: '/signin' 44 | }), users.signin); 45 | 46 | app.get('/auth/github/callback', passport.authenticate('github', { 47 | failureRedirect: '/signin' 48 | }), users.authCallback); 49 | 50 | //Setting the twitter oauth routes 51 | app.get('/auth/twitter', passport.authenticate('twitter', { 52 | failureRedirect: '/signin' 53 | }), users.signin); 54 | 55 | app.get('/auth/twitter/callback', passport.authenticate('twitter', { 56 | failureRedirect: '/signin' 57 | }), users.authCallback); 58 | 59 | //Setting the google oauth routes 60 | app.get('/auth/google', passport.authenticate('google', { 61 | failureRedirect: '/signin', 62 | scope: [ 63 | 'https://www.googleapis.com/auth/userinfo.profile', 64 | 'https://www.googleapis.com/auth/userinfo.email' 65 | ] 66 | }), users.signin); 67 | 68 | app.get('/auth/google/callback', passport.authenticate('google', { 69 | failureRedirect: '/signin' 70 | }), users.authCallback); 71 | 72 | //Finish with setting up the userId param 73 | app.param('userId', users.user); 74 | 75 | //Home route 76 | var index = require('../app/controllers/index'); 77 | app.get('/', index.render); 78 | 79 | 80 | 81 | 82 | 83 | }; -------------------------------------------------------------------------------- /gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | // Project Configuration 3 | grunt.initConfig({ 4 | pkg: grunt.file.readJSON('package.json'), 5 | watch: { 6 | jade: { 7 | files: ['app/views/**'], 8 | options: { 9 | livereload: true, 10 | }, 11 | }, 12 | html: { 13 | files: ['public/views/**'], 14 | options: { 15 | livereload: true, 16 | }, 17 | }, 18 | js: { 19 | files: ['public/js/**'], 20 | options: { 21 | livereload: true, 22 | }, 23 | }, 24 | css: { 25 | files: ['public/sass/**'], 26 | tasks: ['compass'], 27 | options: { 28 | livereload: true, 29 | force: true 30 | } 31 | } 32 | }, 33 | jshint: { 34 | all: ['gruntfile.js', 'public/js/**/*.js', 'test/**/*.js', 'app/**/*.js'] 35 | }, 36 | compass: { //Task 37 | dist: { //Target 38 | options: { //Target options 39 | sassDir: 'public/sass', 40 | cssDir: 'public/css', 41 | environment: 'production' 42 | } 43 | }, 44 | dev: { //Another target 45 | options: { 46 | sassDir: 'public/sass', 47 | cssDir: 'public/css' 48 | } 49 | } 50 | }, 51 | nodemon: { 52 | dev: { 53 | options: { 54 | file: 'server.js', 55 | args: [], 56 | ignoredFiles: ['README.md', 'node_modules/**', '.DS_Store'], 57 | watchedExtensions: ['js'], 58 | watchedFolders: ['app', 'config'], 59 | debug: true, 60 | delayTime: 1, 61 | env: { 62 | PORT: 3000 63 | }, 64 | cwd: __dirname 65 | } 66 | }, 67 | exec: { 68 | options: { 69 | exec: 'less' 70 | } 71 | } 72 | }, 73 | concurrent: { 74 | target: { 75 | tasks: ['nodemon', 'watch'], 76 | options: { 77 | logConcurrentOutput: true 78 | } 79 | } 80 | } 81 | }); 82 | 83 | //Load NPM tasks 84 | grunt.loadNpmTasks('grunt-contrib-compass'); 85 | grunt.loadNpmTasks('grunt-contrib-watch'); 86 | grunt.loadNpmTasks('grunt-contrib-jshint'); 87 | grunt.loadNpmTasks('grunt-nodemon'); 88 | grunt.loadNpmTasks('grunt-concurrent'); 89 | 90 | //Making grunt default to force in order not to break the project. 91 | grunt.option('force', true); 92 | 93 | //Default task(s). 94 | grunt.registerTask('default', ['jshint', 'compass', 'concurrent:target']); 95 | }; 96 | -------------------------------------------------------------------------------- /public/views/users/view.html: -------------------------------------------------------------------------------- 1 |
2 | Name: {{profile.name}} / 3 | Username: {{profile.username}} / 4 | email: {{profile.email}} / 5 | {{profile.created | date:'medium'}} 6 | 7 |
8 | Signin to add this Friend. 9 |
10 |
11 | 12 |
13 | 14 |
15 | He is your friend, 16 | Undo the relashionship! 17 |
18 |
19 | He is not your friend, 20 | Add as a Friend! 21 |
22 | 23 |
24 | 25 |
26 |

It's your profile!

27 | 28 | 29 | 30 |
34 | 35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 | 47 | 48 | 49 | 50 | 51 |
52 | 53 |

I HAVE NO FRIENDS :(

54 |

FRIENDS:

55 |
  • 56 | userId: 57 | {{userId}} 58 |
  • 59 | 60 |
    61 |

    YOU HAVE NO COMMON FRIENDS :(

    62 |

    {{profile.commonFriends.length}} COMMON FRIENDS:

    63 |
  • 64 | userId: 65 | {{userId}} 66 |
  • 67 |
    68 | 69 |

    NO PICS YET

    70 |

    PICS:

    71 |
      72 |
    • 73 | 74 |
    • 75 |
    76 | 77 | 78 |
    79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /app/models/user.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var mongoose = require('mongoose'), 5 | Schema = mongoose.Schema, 6 | crypto = require('crypto'), 7 | _ = require('underscore'), 8 | authTypes = ['github', 'twitter', 'facebook', 'google']; 9 | 10 | 11 | /** 12 | * User Schema 13 | */ 14 | var UserSchema = new Schema({ 15 | name: String, 16 | email: String, 17 | username: String, 18 | provider: String, 19 | hashed_password: String, 20 | salt: String, 21 | facebook: {}, 22 | twitter: {}, 23 | github: {}, 24 | google: {}, 25 | friends: [{ 26 | type: Schema.Types.ObjectId, 27 | ref: 'User' 28 | }], 29 | pics: [String], 30 | // These are completed on HTTP GET requests 31 | yourFriend: { type: Boolean, default: false }, 32 | itsYou: { type: Boolean, default: false } 33 | }); 34 | 35 | /** 36 | * Virtuals 37 | */ 38 | UserSchema.virtual('password').set(function(password) { 39 | this._password = password; 40 | this.salt = this.makeSalt(); 41 | this.hashed_password = this.encryptPassword(password); 42 | }).get(function() { 43 | return this._password; 44 | }); 45 | 46 | /** 47 | * Validations 48 | */ 49 | var validatePresenceOf = function(value) { 50 | return value && value.length; 51 | }; 52 | 53 | // the below 4 validations only apply if you are signing up traditionally 54 | UserSchema.path('name').validate(function(name) { 55 | // if you are authenticating by any of the oauth strategies, don't validate 56 | if (authTypes.indexOf(this.provider) !== -1) return true; 57 | return name.length; 58 | }, 'Name cannot be blank'); 59 | 60 | UserSchema.path('email').validate(function(email) { 61 | // if you are authenticating by any of the oauth strategies, don't validate 62 | if (authTypes.indexOf(this.provider) !== -1) return true; 63 | return email.length; 64 | }, 'Email cannot be blank'); 65 | 66 | UserSchema.path('username').validate(function(username) { 67 | // if you are authenticating by any of the oauth strategies, don't validate 68 | if (authTypes.indexOf(this.provider) !== -1) return true; 69 | return username.length; 70 | }, 'Username cannot be blank'); 71 | 72 | UserSchema.path('hashed_password').validate(function(hashed_password) { 73 | // if you are authenticating by any of the oauth strategies, don't validate 74 | if (authTypes.indexOf(this.provider) !== -1) return true; 75 | return hashed_password.length; 76 | }, 'Password cannot be blank'); 77 | 78 | 79 | /** 80 | * Pre-save hook 81 | */ 82 | UserSchema.pre('save', function(next) { 83 | if (!this.isNew) return next(); 84 | 85 | if (!validatePresenceOf(this.password) && authTypes.indexOf(this.provider) === -1) 86 | next(new Error('Invalid password')); 87 | else 88 | next(); 89 | }); 90 | 91 | /** 92 | * Methods 93 | */ 94 | UserSchema.methods = { 95 | /** 96 | * Authenticate - check if the passwords are the same 97 | * 98 | * @param {String} plainText 99 | * @return {Boolean} 100 | * @api public 101 | */ 102 | authenticate: function(plainText) { 103 | return this.encryptPassword(plainText) === this.hashed_password; 104 | }, 105 | 106 | /** 107 | * Make salt 108 | * 109 | * @return {String} 110 | * @api public 111 | */ 112 | makeSalt: function() { 113 | return Math.round((new Date().valueOf() * Math.random())) + ''; 114 | }, 115 | 116 | /** 117 | * Encrypt password 118 | * 119 | * @param {String} password 120 | * @return {String} 121 | * @api public 122 | */ 123 | encryptPassword: function(password) { 124 | if (!password) return ''; 125 | return crypto.createHmac('sha1', this.salt).update(password).digest('hex'); 126 | }, 127 | 128 | /** 129 | * Document to STRING!, or JSON 130 | */ 131 | toJSON: function(options) { 132 | var document = this.toObject(options); 133 | document.id = document._id.toHexString(); 134 | //delete(document._id); 135 | return document; 136 | }, 137 | }; 138 | 139 | mongoose.model('User', UserSchema); -------------------------------------------------------------------------------- /public/js/controllers/users.js: -------------------------------------------------------------------------------- 1 | angular.module('mean.users').controller('UsersController', ['$scope', '$routeParams', '$location', 'Global', 'Users', function ($scope, $routeParams, $location, Global, Users) { 2 | $scope.global = Global; 3 | 4 | $scope.create = function() { 5 | var user = new Users({ 6 | title: this.title, 7 | content: this.content 8 | }); 9 | user.$save(function(response) { 10 | $location.path("users/" + response._id); 11 | }); 12 | 13 | this.title = ""; 14 | this.content = ""; 15 | }; 16 | 17 | $scope.remove = function(user) { 18 | user.$remove(); 19 | 20 | for (var i in $scope.users) { 21 | if ($scope.users[i] == user) { 22 | $scope.users.splice(i, 1); 23 | } 24 | } 25 | }; 26 | 27 | $scope.update = function() { 28 | var user = $scope.user; 29 | alert('UPDATE USER NOT UTILIZED'); 30 | /* 31 | if (!user.updated) { 32 | user.updated = []; 33 | } 34 | user.updated.push(new Date().getTime()); 35 | 36 | user.$update(function() { 37 | $location.path('users/' + user._id); 38 | });*/ 39 | }; 40 | 41 | // It is used to add a new friend. 42 | $scope.addFriend = function() { 43 | var user = $scope.global.user; 44 | 45 | 46 | if (!user.updated) { 47 | user.updated = []; 48 | } 49 | user.updated.push(new Date().getTime()); 50 | 51 | 52 | user.friends.push($scope.profile._id); 53 | 54 | 55 | user.$update(function(user) { 56 | console.log("You have a new friend now!"); 57 | // UPDATE THE CURRENT PROFILE 58 | $scope.findOne(); 59 | 60 | }); 61 | 62 | }; 63 | 64 | 65 | 66 | // It is used to remove a friend. 67 | $scope.removeFriend = function() { 68 | profile = $scope.profile; 69 | var user = $scope.global.user; 70 | 71 | 72 | if (!user.updated) { 73 | user.updated = []; 74 | } 75 | user.updated.push(new Date().getTime()); 76 | 77 | 78 | var friends = []; 79 | for (var i in user.friends) { 80 | if (user.friends[i] != profile._id) { 81 | friends.push(user.friends[i]); 82 | } 83 | } 84 | user.friends = friends; 85 | 86 | 87 | 88 | user.$update(function () { 89 | console.log("He is not your friend anymore!"); 90 | // UPDATE THE CURRENT PROFILE 91 | $scope.findOne(); 92 | }); 93 | 94 | 95 | }; 96 | 97 | 98 | 99 | $scope.find = function(query) { 100 | Users.query(query, function(users) { 101 | $scope.users = users; 102 | }); 103 | }; 104 | 105 | 106 | $scope.findOne = function() { 107 | Users.get({ 108 | userId: $routeParams.userId 109 | }, function(profile) { 110 | $scope.profile = profile; 111 | console.log("PROFILE BY USERCONTROLE.FINDONE!"); 112 | console.log($scope.profile); 113 | 114 | var user = $scope.global.user; 115 | 116 | // ADDING THE FRIENDS IN COMMON AT .commonFriends 117 | common = []; 118 | if (user){ // We don't know if USER is already ready 119 | for (var i=0; i