├── .buildignore
├── .gitattributes
├── _.gitattributes
├── client
├── app
│ ├── landing
│ │ ├── js
│ │ │ ├── preloader.js
│ │ │ ├── parallax.js
│ │ │ ├── jQuery.headroom.js
│ │ │ ├── jquery.inview.min.js
│ │ │ ├── countUp.min.js
│ │ │ ├── headroom.min.js
│ │ │ ├── jquery.countdown.min.js
│ │ │ └── validator.js
│ │ ├── .DS_Store
│ │ ├── img
│ │ │ ├── 02zoomin.png
│ │ │ ├── about
│ │ │ │ ├── 01.png
│ │ │ │ └── 02.png
│ │ │ ├── logos
│ │ │ │ ├── 01.png
│ │ │ │ ├── 02.png
│ │ │ │ ├── 03.png
│ │ │ │ ├── 04.png
│ │ │ │ ├── 05.png
│ │ │ │ └── 06.png
│ │ │ ├── 03pattern.png
│ │ │ ├── 04textarea.png
│ │ │ ├── avatars
│ │ │ │ ├── 01.jpg
│ │ │ │ ├── 02.jpg
│ │ │ │ ├── 03.jpg
│ │ │ │ ├── 04.jpg
│ │ │ │ ├── 05.jpg
│ │ │ │ ├── 06.jpg
│ │ │ │ ├── 07.jpg
│ │ │ │ ├── 08.jpg
│ │ │ │ ├── 09.jpg
│ │ │ │ ├── 10.jpg
│ │ │ │ ├── 11.jpg
│ │ │ │ ├── 12.jpg
│ │ │ │ ├── jonah.JPG
│ │ │ │ ├── rioa.jpg
│ │ │ │ ├── jordan.jpg
│ │ │ │ └── seunghoon.PNG
│ │ │ ├── gallery
│ │ │ │ ├── 01.jpg
│ │ │ │ ├── 02.jpg
│ │ │ │ ├── 03.jpg
│ │ │ │ ├── 04.jpg
│ │ │ │ ├── 05.jpg
│ │ │ │ └── big
│ │ │ │ │ ├── 01.jpg
│ │ │ │ │ ├── 02.jpg
│ │ │ │ │ ├── 03.jpg
│ │ │ │ │ ├── 04.jpg
│ │ │ │ │ └── 05.jpg
│ │ │ ├── grey_bg
│ │ │ │ ├── 01.jpg
│ │ │ │ └── 02.jpg
│ │ │ ├── logoMidUp.jpg
│ │ │ ├── preloader.gif
│ │ │ ├── header
│ │ │ │ ├── bg02.jpg
│ │ │ │ ├── bg03.jpg
│ │ │ │ ├── bg03-blue.jpg
│ │ │ │ ├── bg03-green.jpg
│ │ │ │ ├── bg03-pink.jpg
│ │ │ │ ├── bg03-yellow.jpg
│ │ │ │ ├── img-iphone.png
│ │ │ │ ├── bg03-coldgrey.jpg
│ │ │ │ ├── bg03-pantone2014.jpg
│ │ │ │ └── bg03-pantone2015.jpg
│ │ │ ├── 01-logo-ORANGE.png
│ │ │ ├── favicon
│ │ │ │ ├── favicon.ico
│ │ │ │ ├── apple-touch-icon.png
│ │ │ │ ├── apple-touch-icon-72x72.png
│ │ │ │ └── apple-touch-icon-114x114.png
│ │ │ ├── parallax_bg
│ │ │ │ ├── 01.jpg
│ │ │ │ ├── 01-old.jpg
│ │ │ │ ├── 01-blue.jpg
│ │ │ │ ├── 01-green.jpg
│ │ │ │ ├── 01-pink.jpg
│ │ │ │ ├── 01-coldgrey.jpg
│ │ │ │ ├── 01-yellow.jpg
│ │ │ │ ├── 01-pantone2014.jpg
│ │ │ │ └── 01-pantone2015.jpg
│ │ │ └── lightbox
│ │ │ │ ├── controls.png
│ │ │ │ └── loading.gif
│ │ └── fonts
│ │ │ ├── ElegantIcons.eot
│ │ │ ├── ElegantIcons.ttf
│ │ │ └── ElegantIcons.woff
│ ├── admin
│ │ ├── admin.css
│ │ ├── admin.js
│ │ ├── admin.controller.js
│ │ └── admin.html
│ ├── main
│ │ ├── main.js
│ │ ├── main.controller.js
│ │ ├── main.css
│ │ └── main.controller.spec.js
│ ├── google-maps
│ │ ├── google-maps.js
│ │ ├── google-maps.css
│ │ └── google-maps.html
│ ├── account
│ │ ├── login
│ │ │ ├── login.css
│ │ │ ├── login.controller.js
│ │ │ └── login.html
│ │ ├── account.js
│ │ ├── settings
│ │ │ ├── settings.controller.js
│ │ │ └── settings.html
│ │ └── signup
│ │ │ ├── signup.controller.js
│ │ │ └── signup.html
│ ├── app.css
│ └── app.js
├── robots.txt
├── .DS_Store
├── assets
│ └── images
│ │ └── yeoman.png
├── components
│ ├── mongoose-error
│ │ └── mongoose-error.directive.js
│ ├── auth
│ │ ├── user.service.js
│ │ └── auth.service.js
│ ├── modal
│ │ ├── modal.css
│ │ ├── modal.html
│ │ └── modal.service.js
│ └── navbar
│ │ ├── navbar.controller.js
│ │ └── navbar.html
├── .jshintrc
├── favicon.ico
└── index.html
├── .bowerrc
├── _.travis.yml
├── .gitignore
├── .travis.yml
├── server
├── .jshintrc-spec
├── config
│ ├── environment
│ │ ├── test.js
│ │ ├── development.js
│ │ ├── production.js
│ │ └── index.js
│ ├── local.env.sample.js
│ ├── seed.js
│ ├── socketio.js
│ └── express.js
├── api
│ ├── thing
│ │ ├── thing.model.js
│ │ ├── index.js
│ │ ├── thing.spec.js
│ │ ├── thing.socket.js
│ │ └── thing.controller.js
│ └── user
│ │ ├── index.js
│ │ ├── user.model.spec.js
│ │ ├── user.controller.js
│ │ └── user.model.js
├── .jshintrc
├── components
│ └── errors
│ │ └── index.js
├── auth
│ ├── twitter
│ │ ├── index.js
│ │ └── passport.js
│ ├── facebook
│ │ ├── index.js
│ │ └── passport.js
│ ├── local
│ │ ├── index.js
│ │ └── passport.js
│ ├── google
│ │ ├── index.js
│ │ └── passport.js
│ ├── index.js
│ └── auth.service.js
├── routes.js
├── app.js
└── views
│ └── 404.html
├── dist
├── server
│ ├── config
│ │ ├── environment
│ │ │ ├── test.js
│ │ │ ├── development.js
│ │ │ ├── production.js
│ │ │ └── index.js
│ │ ├── local.env.js
│ │ ├── local.env.sample.js
│ │ ├── seed.js
│ │ ├── socketio.js
│ │ └── express.js
│ ├── api
│ │ ├── thing
│ │ │ ├── thing.model.js
│ │ │ ├── index.js
│ │ │ ├── thing.spec.js
│ │ │ ├── thing.socket.js
│ │ │ └── thing.controller.js
│ │ └── user
│ │ │ ├── index.js
│ │ │ ├── user.model.spec.js
│ │ │ ├── user.controller.js
│ │ │ └── user.model.js
│ ├── components
│ │ └── errors
│ │ │ └── index.js
│ ├── auth
│ │ ├── twitter
│ │ │ ├── index.js
│ │ │ └── passport.js
│ │ ├── facebook
│ │ │ ├── index.js
│ │ │ └── passport.js
│ │ ├── local
│ │ │ ├── index.js
│ │ │ └── passport.js
│ │ ├── google
│ │ │ ├── index.js
│ │ │ └── passport.js
│ │ ├── index.js
│ │ └── auth.service.js
│ ├── routes.js
│ ├── app.js
│ └── views
│ │ └── 404.html
└── package.json
├── e2e
└── main
│ ├── main.po.js
│ └── main.spec.js
├── _.jshintrc
├── .editorconfig
├── _.editorconfig
├── _.gitignore
├── bower.json
├── README.md
├── .yo-rc.json
├── protractor.conf.js
├── karma.conf.js
├── package.json
├── _PRESS-RELEASE.md
└── _CONTRIBUTING.md
/.buildignore:
--------------------------------------------------------------------------------
1 | *.coffee
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/_.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/client/app/landing/js/preloader.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/client/robots.txt:
--------------------------------------------------------------------------------
1 | # robotstxt.org
2 |
3 | User-agent: *
4 |
--------------------------------------------------------------------------------
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "client/bower_components"
3 | }
4 |
--------------------------------------------------------------------------------
/client/app/admin/admin.css:
--------------------------------------------------------------------------------
1 | .trash { color:rgb(209, 91, 71); }
2 |
--------------------------------------------------------------------------------
/_.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.8'
4 | - '0.10'
5 |
--------------------------------------------------------------------------------
/client/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | public
3 | .tmp
4 | .idea
5 | client/bower_components
6 | /server/config/local.env.js
7 |
--------------------------------------------------------------------------------
/client/app/landing/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/.DS_Store
--------------------------------------------------------------------------------
/client/assets/images/yeoman.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/assets/images/yeoman.png
--------------------------------------------------------------------------------
/client/app/landing/img/02zoomin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/02zoomin.png
--------------------------------------------------------------------------------
/client/app/landing/img/about/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/about/01.png
--------------------------------------------------------------------------------
/client/app/landing/img/about/02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/about/02.png
--------------------------------------------------------------------------------
/client/app/landing/img/logos/01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/logos/01.png
--------------------------------------------------------------------------------
/client/app/landing/img/logos/02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/logos/02.png
--------------------------------------------------------------------------------
/client/app/landing/img/logos/03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/logos/03.png
--------------------------------------------------------------------------------
/client/app/landing/img/logos/04.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/logos/04.png
--------------------------------------------------------------------------------
/client/app/landing/img/logos/05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/logos/05.png
--------------------------------------------------------------------------------
/client/app/landing/img/logos/06.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/logos/06.png
--------------------------------------------------------------------------------
/client/app/landing/img/03pattern.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/03pattern.png
--------------------------------------------------------------------------------
/client/app/landing/img/04textarea.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/04textarea.png
--------------------------------------------------------------------------------
/client/app/landing/img/avatars/01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/avatars/01.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/avatars/02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/avatars/02.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/avatars/03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/avatars/03.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/avatars/04.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/avatars/04.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/avatars/05.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/avatars/05.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/avatars/06.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/avatars/06.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/avatars/07.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/avatars/07.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/avatars/08.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/avatars/08.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/avatars/09.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/avatars/09.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/avatars/10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/avatars/10.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/avatars/11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/avatars/11.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/avatars/12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/avatars/12.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/gallery/01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/gallery/01.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/gallery/02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/gallery/02.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/gallery/03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/gallery/03.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/gallery/04.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/gallery/04.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/gallery/05.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/gallery/05.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/grey_bg/01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/grey_bg/01.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/grey_bg/02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/grey_bg/02.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/logoMidUp.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/logoMidUp.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/preloader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/preloader.gif
--------------------------------------------------------------------------------
/client/app/landing/img/avatars/jonah.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/avatars/jonah.JPG
--------------------------------------------------------------------------------
/client/app/landing/img/avatars/rioa.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/avatars/rioa.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/header/bg02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/header/bg02.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/header/bg03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/header/bg03.jpg
--------------------------------------------------------------------------------
/client/app/landing/fonts/ElegantIcons.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/fonts/ElegantIcons.eot
--------------------------------------------------------------------------------
/client/app/landing/fonts/ElegantIcons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/fonts/ElegantIcons.ttf
--------------------------------------------------------------------------------
/client/app/landing/fonts/ElegantIcons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/fonts/ElegantIcons.woff
--------------------------------------------------------------------------------
/client/app/landing/img/01-logo-ORANGE.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/01-logo-ORANGE.png
--------------------------------------------------------------------------------
/client/app/landing/img/avatars/jordan.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/avatars/jordan.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/favicon/favicon.ico
--------------------------------------------------------------------------------
/client/app/landing/img/gallery/big/01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/gallery/big/01.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/gallery/big/02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/gallery/big/02.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/gallery/big/03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/gallery/big/03.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/gallery/big/04.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/gallery/big/04.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/gallery/big/05.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/gallery/big/05.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/parallax_bg/01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/parallax_bg/01.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/avatars/seunghoon.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/avatars/seunghoon.PNG
--------------------------------------------------------------------------------
/client/app/landing/img/header/bg03-blue.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/header/bg03-blue.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/header/bg03-green.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/header/bg03-green.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/header/bg03-pink.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/header/bg03-pink.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/header/bg03-yellow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/header/bg03-yellow.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/header/img-iphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/header/img-iphone.png
--------------------------------------------------------------------------------
/client/app/landing/img/lightbox/controls.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/lightbox/controls.png
--------------------------------------------------------------------------------
/client/app/landing/img/lightbox/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/lightbox/loading.gif
--------------------------------------------------------------------------------
/client/app/landing/img/parallax_bg/01-old.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/parallax_bg/01-old.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/header/bg03-coldgrey.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/header/bg03-coldgrey.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/parallax_bg/01-blue.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/parallax_bg/01-blue.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/parallax_bg/01-green.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/parallax_bg/01-green.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/parallax_bg/01-pink.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/parallax_bg/01-pink.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/header/bg03-pantone2014.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/header/bg03-pantone2014.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/header/bg03-pantone2015.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/header/bg03-pantone2015.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/parallax_bg/01-coldgrey.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/parallax_bg/01-coldgrey.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/parallax_bg/01-yellow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/parallax_bg/01-yellow.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/favicon/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/favicon/apple-touch-icon.png
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '0.10'
4 | - '0.11'
5 | before_script:
6 | - npm install -g bower grunt-cli
7 | - bower install
8 | services: mongodb
--------------------------------------------------------------------------------
/client/app/landing/img/parallax_bg/01-pantone2014.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/parallax_bg/01-pantone2014.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/parallax_bg/01-pantone2015.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/parallax_bg/01-pantone2015.jpg
--------------------------------------------------------------------------------
/client/app/landing/img/favicon/apple-touch-icon-72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/favicon/apple-touch-icon-72x72.png
--------------------------------------------------------------------------------
/client/app/landing/img/favicon/apple-touch-icon-114x114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RioaM/Meet-Me-In-The-Middle/HEAD/client/app/landing/img/favicon/apple-touch-icon-114x114.png
--------------------------------------------------------------------------------
/server/.jshintrc-spec:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ".jshintrc",
3 | "globals": {
4 | "describe": true,
5 | "it": true,
6 | "before": true,
7 | "beforeEach": true,
8 | "after": true,
9 | "afterEach": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/server/config/environment/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Test specific configuration
4 | // ===========================
5 | module.exports = {
6 | // MongoDB connection options
7 | mongo: {
8 | uri: 'mongodb://localhost/meetmeinthemiddle-test'
9 | }
10 | };
--------------------------------------------------------------------------------
/dist/server/config/environment/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Test specific configuration
4 | // ===========================
5 | module.exports = {
6 | // MongoDB connection options
7 | mongo: {
8 | uri: 'mongodb://localhost/meetmeinthemiddle-test'
9 | }
10 | };
--------------------------------------------------------------------------------
/server/api/thing/thing.model.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var mongoose = require('mongoose'),
4 | Schema = mongoose.Schema;
5 |
6 | var ThingSchema = new Schema({
7 | name: String,
8 | info: String,
9 | active: Boolean
10 | });
11 |
12 | module.exports = mongoose.model('Thing', ThingSchema);
--------------------------------------------------------------------------------
/client/app/main/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('meetMeInTheMiddleApp')
4 | .config(function ($stateProvider) {
5 | $stateProvider
6 | .state('main', {
7 | url: '/',
8 | templateUrl: 'app/main/main.html',
9 | controller: 'MainCtrl'
10 | });
11 | });
--------------------------------------------------------------------------------
/dist/server/api/thing/thing.model.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var mongoose = require('mongoose'),
4 | Schema = mongoose.Schema;
5 |
6 | var ThingSchema = new Schema({
7 | name: String,
8 | info: String,
9 | active: Boolean
10 | });
11 |
12 | module.exports = mongoose.model('Thing', ThingSchema);
--------------------------------------------------------------------------------
/client/app/admin/admin.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('meetMeInTheMiddleApp')
4 | .config(function ($stateProvider) {
5 | $stateProvider
6 | .state('admin', {
7 | url: '/admin',
8 | templateUrl: 'app/admin/admin.html',
9 | controller: 'AdminCtrl'
10 | });
11 | });
--------------------------------------------------------------------------------
/server/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "esnext": true,
4 | "bitwise": true,
5 | "eqeqeq": true,
6 | "immed": true,
7 | "latedef": "nofunc",
8 | "newcap": true,
9 | "noarg": true,
10 | "regexp": true,
11 | "undef": true,
12 | "smarttabs": true,
13 | "asi": true,
14 | "debug": true
15 | }
16 |
--------------------------------------------------------------------------------
/server/config/environment/development.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Development specific configuration
4 | // ==================================
5 | module.exports = {
6 | // MongoDB connection options
7 | mongo: {
8 | uri: 'mongodb://localhost/meetmeinthemiddle-dev'
9 | },
10 |
11 | seedDB: true
12 | };
13 |
--------------------------------------------------------------------------------
/dist/server/config/environment/development.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Development specific configuration
4 | // ==================================
5 | module.exports = {
6 | // MongoDB connection options
7 | mongo: {
8 | uri: 'mongodb://localhost/meetmeinthemiddle-dev'
9 | },
10 |
11 | seedDB: true
12 | };
13 |
--------------------------------------------------------------------------------
/client/app/google-maps/google-maps.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('meetMeInTheMiddleApp')
4 | .config(function ($stateProvider) {
5 | $stateProvider
6 | .state('google-maps', {
7 | url: '/maps',
8 | templateUrl: 'app/google-maps/google-maps.html',
9 | controller: 'MapsCtrl'
10 | });
11 | });
12 |
13 |
--------------------------------------------------------------------------------
/server/api/thing/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var controller = require('./thing.controller');
5 |
6 | var router = express.Router();
7 |
8 | router.get('/', controller.index);
9 | router.get('/:id', controller.show);
10 | router.post('/', controller.create);
11 | router.put('/:id', controller.update);
12 | router.patch('/:id', controller.update);
13 | router.delete('/:id', controller.destroy);
14 |
15 | module.exports = router;
--------------------------------------------------------------------------------
/dist/server/api/thing/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var controller = require('./thing.controller');
5 |
6 | var router = express.Router();
7 |
8 | router.get('/', controller.index);
9 | router.get('/:id', controller.show);
10 | router.post('/', controller.create);
11 | router.put('/:id', controller.update);
12 | router.patch('/:id', controller.update);
13 | router.delete('/:id', controller.destroy);
14 |
15 | module.exports = router;
--------------------------------------------------------------------------------
/client/app/account/login/login.css:
--------------------------------------------------------------------------------
1 | .btn-facebook {
2 | color: #fff;
3 | background-color: #3B5998;
4 | border-color: #133783;
5 | }
6 |
7 | .btn-twitter {
8 | color: #fff;
9 | background-color: #2daddc;
10 | border-color: #0271bf;
11 | }
12 |
13 | .btn-google-plus {
14 | color: #fff;
15 | background-color: #dd4b39;
16 | border-color: #c53727;
17 | }
18 |
19 | .btn-github {
20 | color: #fff;
21 | background-color: #fafafa;
22 | border-color: #ccc;
23 | }
24 |
--------------------------------------------------------------------------------
/e2e/main/main.po.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file uses the Page Object pattern to define the main page for tests
3 | * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ
4 | */
5 |
6 | 'use strict';
7 |
8 | var MainPage = function() {
9 | this.heroEl = element(by.css('.hero-unit'));
10 | this.h1El = this.heroEl.element(by.css('h1'));
11 | this.imgEl = this.heroEl.element(by.css('img'));
12 | };
13 |
14 | module.exports = new MainPage();
15 |
16 |
--------------------------------------------------------------------------------
/server/components/errors/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Error responses
3 | */
4 |
5 | 'use strict';
6 |
7 | module.exports[404] = function pageNotFound(req, res) {
8 | var viewFilePath = '404';
9 | var statusCode = 404;
10 | var result = {
11 | status: statusCode
12 | };
13 |
14 | res.status(result.status);
15 | res.render(viewFilePath, function (err) {
16 | if (err) { return res.json(result, result.status); }
17 |
18 | res.render(viewFilePath);
19 | });
20 | };
21 |
--------------------------------------------------------------------------------
/_.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "esnext": true,
4 | "bitwise": true,
5 | "camelcase": true,
6 | "curly": true,
7 | "eqeqeq": true,
8 | "immed": true,
9 | "indent": 2,
10 | "latedef": true,
11 | "newcap": true,
12 | "noarg": true,
13 | "quotmark": "single",
14 | "regexp": true,
15 | "undef": true,
16 | "unused": true,
17 | "strict": true,
18 | "trailing": true,
19 | "smarttabs": true,
20 | "white": true
21 | }
22 |
--------------------------------------------------------------------------------
/dist/server/components/errors/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Error responses
3 | */
4 |
5 | 'use strict';
6 |
7 | module.exports[404] = function pageNotFound(req, res) {
8 | var viewFilePath = '404';
9 | var statusCode = 404;
10 | var result = {
11 | status: statusCode
12 | };
13 |
14 | res.status(result.status);
15 | res.render(viewFilePath, function (err) {
16 | if (err) { return res.json(result, result.status); }
17 |
18 | res.render(viewFilePath);
19 | });
20 | };
21 |
--------------------------------------------------------------------------------
/e2e/main/main.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Main View', function() {
4 | var page;
5 |
6 | beforeEach(function() {
7 | browser.get('/');
8 | page = require('./main.po');
9 | });
10 |
11 | it('should include jumbotron with correct data', function() {
12 | expect(page.h1El.getText()).toBe('\'Allo, \'Allo!');
13 | expect(page.imgEl.getAttribute('src')).toMatch(/assets\/images\/yeoman.png$/);
14 | expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman');
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
--------------------------------------------------------------------------------
/_.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
--------------------------------------------------------------------------------
/client/components/mongoose-error/mongoose-error.directive.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Removes server error when user updates input
5 | */
6 | angular.module('meetMeInTheMiddleApp')
7 | .directive('mongooseError', function () {
8 | return {
9 | restrict: 'A',
10 | require: 'ngModel',
11 | link: function(scope, element, attrs, ngModel) {
12 | element.on('keydown', function() {
13 | return ngModel.$setValidity('mongoose', true);
14 | });
15 | }
16 | };
17 | });
--------------------------------------------------------------------------------
/client/components/auth/user.service.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('meetMeInTheMiddleApp')
4 | .factory('User', function ($resource) {
5 | return $resource('/api/users/:id/:controller', {
6 | id: '@_id'
7 | },
8 | {
9 | changePassword: {
10 | method: 'PUT',
11 | params: {
12 | controller:'password'
13 | }
14 | },
15 | get: {
16 | method: 'GET',
17 | params: {
18 | id:'me'
19 | }
20 | }
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/client/app/admin/admin.controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('meetMeInTheMiddleApp')
4 | .controller('AdminCtrl', function ($scope, $http, Auth, User) {
5 |
6 | // Use the User $resource to fetch all users
7 | $scope.users = User.query();
8 |
9 | $scope.delete = function(user) {
10 | User.remove({ id: user._id });
11 | angular.forEach($scope.users, function(u, i) {
12 | if (u === user) {
13 | $scope.users.splice(i, 1);
14 | }
15 | });
16 | };
17 | });
18 |
--------------------------------------------------------------------------------
/server/auth/twitter/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var passport = require('passport');
5 | var auth = require('../auth.service');
6 |
7 | var router = express.Router();
8 |
9 | router
10 | .get('/', passport.authenticate('twitter', {
11 | failureRedirect: '/signup',
12 | session: false
13 | }))
14 |
15 | .get('/callback', passport.authenticate('twitter', {
16 | failureRedirect: '/signup',
17 | session: false
18 | }), auth.setTokenCookie);
19 |
20 | module.exports = router;
--------------------------------------------------------------------------------
/dist/server/auth/twitter/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var passport = require('passport');
5 | var auth = require('../auth.service');
6 |
7 | var router = express.Router();
8 |
9 | router
10 | .get('/', passport.authenticate('twitter', {
11 | failureRedirect: '/signup',
12 | session: false
13 | }))
14 |
15 | .get('/callback', passport.authenticate('twitter', {
16 | failureRedirect: '/signup',
17 | session: false
18 | }), auth.setTokenCookie);
19 |
20 | module.exports = router;
--------------------------------------------------------------------------------
/client/app/admin/admin.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
The delete user and user index api routes are restricted to users with the 'admin' role.
5 |
6 | -
7 | {{user.name}}
8 | {{user.email}}
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/server/api/thing/thing.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var should = require('should');
4 | var app = require('../../app');
5 | var request = require('supertest');
6 |
7 | describe('GET /api/things', function() {
8 |
9 | it('should respond with JSON array', function(done) {
10 | request(app)
11 | .get('/api/things')
12 | .expect(200)
13 | .expect('Content-Type', /json/)
14 | .end(function(err, res) {
15 | if (err) return done(err);
16 | res.body.should.be.instanceof(Array);
17 | done();
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/dist/server/api/thing/thing.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var should = require('should');
4 | var app = require('../../app');
5 | var request = require('supertest');
6 |
7 | describe('GET /api/things', function() {
8 |
9 | it('should respond with JSON array', function(done) {
10 | request(app)
11 | .get('/api/things')
12 | .expect(200)
13 | .expect('Content-Type', /json/)
14 | .end(function(err, res) {
15 | if (err) return done(err);
16 | res.body.should.be.instanceof(Array);
17 | done();
18 | });
19 | });
20 | });
21 |
--------------------------------------------------------------------------------
/server/auth/facebook/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var passport = require('passport');
5 | var auth = require('../auth.service');
6 |
7 | var router = express.Router();
8 |
9 | router
10 | .get('/', passport.authenticate('facebook', {
11 | scope: ['email', 'user_about_me'],
12 | failureRedirect: '/signup',
13 | session: false
14 | }))
15 |
16 | .get('/callback', passport.authenticate('facebook', {
17 | failureRedirect: '/signup',
18 | session: false
19 | }), auth.setTokenCookie);
20 |
21 | module.exports = router;
--------------------------------------------------------------------------------
/dist/server/auth/facebook/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var passport = require('passport');
5 | var auth = require('../auth.service');
6 |
7 | var router = express.Router();
8 |
9 | router
10 | .get('/', passport.authenticate('facebook', {
11 | scope: ['email', 'user_about_me'],
12 | failureRedirect: '/signup',
13 | session: false
14 | }))
15 |
16 | .get('/callback', passport.authenticate('facebook', {
17 | failureRedirect: '/signup',
18 | session: false
19 | }), auth.setTokenCookie);
20 |
21 | module.exports = router;
--------------------------------------------------------------------------------
/server/api/thing/thing.socket.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Broadcast updates to client when the model changes
3 | */
4 |
5 | 'use strict';
6 |
7 | var thing = require('./thing.model');
8 |
9 | exports.register = function(socket) {
10 | thing.schema.post('save', function (doc) {
11 | onSave(socket, doc);
12 | });
13 | thing.schema.post('remove', function (doc) {
14 | onRemove(socket, doc);
15 | });
16 | }
17 |
18 | function onSave(socket, doc, cb) {
19 | socket.emit('thing:save', doc);
20 | }
21 |
22 | function onRemove(socket, doc, cb) {
23 | socket.emit('thing:remove', doc);
24 | }
--------------------------------------------------------------------------------
/dist/server/api/thing/thing.socket.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Broadcast updates to client when the model changes
3 | */
4 |
5 | 'use strict';
6 |
7 | var thing = require('./thing.model');
8 |
9 | exports.register = function(socket) {
10 | thing.schema.post('save', function (doc) {
11 | onSave(socket, doc);
12 | });
13 | thing.schema.post('remove', function (doc) {
14 | onRemove(socket, doc);
15 | });
16 | }
17 |
18 | function onSave(socket, doc, cb) {
19 | socket.emit('thing:save', doc);
20 | }
21 |
22 | function onRemove(socket, doc, cb) {
23 | socket.emit('thing:remove', doc);
24 | }
--------------------------------------------------------------------------------
/client/components/modal/modal.css:
--------------------------------------------------------------------------------
1 | /*.modal-primary .modal-header,
2 | .modal-info .modal-header,
3 | .modal-success .modal-header,
4 | .modal-warning .modal-header,
5 | .modal-danger .modal-header {
6 | color: #fff;
7 | border-radius: 5px 5px 0 0;
8 | }
9 | .modal-primary .modal-header {
10 | background: #428bca;
11 | }
12 | .modal-info .modal-header {
13 | background: #5bc0de;
14 | }
15 | .modal-success .modal-header {
16 | background: #5cb85c;
17 | }
18 | .modal-warning .modal-header {
19 | background: #f0ad4e;
20 | }
21 | .modal-danger .modal-header {
22 | background: #d9534f;
23 | }*/
--------------------------------------------------------------------------------
/client/components/modal/modal.html:
--------------------------------------------------------------------------------
1 |
5 |
9 |
--------------------------------------------------------------------------------
/server/auth/local/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var passport = require('passport');
5 | var auth = require('../auth.service');
6 |
7 | var router = express.Router();
8 |
9 | router.post('/', function(req, res, next) {
10 | passport.authenticate('local', function (err, user, info) {
11 | var error = err || info;
12 | if (error) return res.json(401, error);
13 | if (!user) return res.json(404, {message: 'Something went wrong, please try again.'});
14 |
15 | var token = auth.signToken(user._id, user.role);
16 | res.json({token: token});
17 | })(req, res, next)
18 | });
19 |
20 | module.exports = router;
--------------------------------------------------------------------------------
/dist/server/auth/local/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var passport = require('passport');
5 | var auth = require('../auth.service');
6 |
7 | var router = express.Router();
8 |
9 | router.post('/', function(req, res, next) {
10 | passport.authenticate('local', function (err, user, info) {
11 | var error = err || info;
12 | if (error) return res.json(401, error);
13 | if (!user) return res.json(404, {message: 'Something went wrong, please try again.'});
14 |
15 | var token = auth.signToken(user._id, user.role);
16 | res.json({token: token});
17 | })(req, res, next)
18 | });
19 |
20 | module.exports = router;
--------------------------------------------------------------------------------
/client/app/google-maps/google-maps.css:
--------------------------------------------------------------------------------
1 | .angular-google-map-container { height: 400px; }
2 |
3 | .controls {
4 | margin-top: 16px;
5 | border: 1px solid transparent;
6 | border-radius: 2px 0 0 2px;
7 | box-sizing: border-box;
8 | -moz-box-sizing: border-box;
9 | height: 32px;
10 | outline: none;
11 | box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
12 | }
13 |
14 | #pac-input {
15 | background-color: #fff;
16 | font-family: Roboto;
17 | font-size: 15px;
18 | font-weight: 300;
19 | margin-left: 12px;
20 | padding: 0 11px 0 13px;
21 | text-overflow: ellipsis;
22 | width: 400px;
23 | }
24 |
25 | #pac-input:focus {
26 | border-color: #4d90fe;
27 | }
--------------------------------------------------------------------------------
/client/components/navbar/navbar.controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('meetMeInTheMiddleApp')
4 | .controller('NavbarCtrl', function ($scope, $location, Auth) {
5 | $scope.menu = [{
6 | 'title': 'Home',
7 | 'link': '/'
8 | }];
9 |
10 | $scope.isCollapsed = true;
11 | $scope.isLoggedIn = Auth.isLoggedIn;
12 | $scope.isAdmin = Auth.isAdmin;
13 | $scope.getCurrentUser = Auth.getCurrentUser;
14 |
15 | $scope.logout = function() {
16 | Auth.logout();
17 | $location.path('/login');
18 | };
19 |
20 | $scope.isActive = function(route) {
21 | return route === $location.path();
22 | };
23 | });
--------------------------------------------------------------------------------
/server/auth/google/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var passport = require('passport');
5 | var auth = require('../auth.service');
6 |
7 | var router = express.Router();
8 |
9 | router
10 | .get('/', passport.authenticate('google', {
11 | failureRedirect: '/signup',
12 | scope: [
13 | 'https://www.googleapis.com/auth/userinfo.profile',
14 | 'https://www.googleapis.com/auth/userinfo.email'
15 | ],
16 | session: false
17 | }))
18 |
19 | .get('/callback', passport.authenticate('google', {
20 | failureRedirect: '/signup',
21 | session: false
22 | }), auth.setTokenCookie);
23 |
24 | module.exports = router;
--------------------------------------------------------------------------------
/dist/server/auth/google/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var passport = require('passport');
5 | var auth = require('../auth.service');
6 |
7 | var router = express.Router();
8 |
9 | router
10 | .get('/', passport.authenticate('google', {
11 | failureRedirect: '/signup',
12 | scope: [
13 | 'https://www.googleapis.com/auth/userinfo.profile',
14 | 'https://www.googleapis.com/auth/userinfo.email'
15 | ],
16 | session: false
17 | }))
18 |
19 | .get('/callback', passport.authenticate('google', {
20 | failureRedirect: '/signup',
21 | session: false
22 | }), auth.setTokenCookie);
23 |
24 | module.exports = router;
--------------------------------------------------------------------------------
/_.gitignore:
--------------------------------------------------------------------------------
1 | ### node etc ###
2 |
3 | # Logs
4 | logs
5 | *.log
6 |
7 | # Runtime data
8 | pids
9 | *.pid
10 | *.seed
11 |
12 | # Directory for instrumented libs generated by jscoverage/JSCover
13 | lib-cov
14 |
15 | # Coverage directory used by tools like istanbul
16 | coverage
17 |
18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
19 | .grunt
20 |
21 | # Compiled Dirs (http://nodejs.org/api/addons.html)
22 | build/
23 | dist/
24 |
25 | # Dependency directorys
26 | # Deployed apps should consider commenting these lines out:
27 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git
28 | node_modules/
29 | bower_components/
30 |
--------------------------------------------------------------------------------
/client/app/account/account.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('meetMeInTheMiddleApp')
4 | .config(function ($stateProvider) {
5 | $stateProvider
6 | .state('login', {
7 | url: '/login',
8 | templateUrl: 'app/account/login/login.html',
9 | controller: 'LoginCtrl'
10 | })
11 | .state('signup', {
12 | url: '/signup',
13 | templateUrl: 'app/account/signup/signup.html',
14 | controller: 'SignupCtrl'
15 | })
16 | .state('settings', {
17 | url: '/settings',
18 | templateUrl: 'app/account/settings/settings.html',
19 | controller: 'SettingsCtrl',
20 | authenticate: true
21 | });
22 | });
--------------------------------------------------------------------------------
/server/api/user/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var controller = require('./user.controller');
5 | var config = require('../../config/environment');
6 | var auth = require('../../auth/auth.service');
7 |
8 | var router = express.Router();
9 |
10 | router.get('/', auth.hasRole('admin'), controller.index);
11 | router.delete('/:id', auth.hasRole('admin'), controller.destroy);
12 | router.get('/me', auth.isAuthenticated(), controller.me);
13 | router.put('/:id/password', auth.isAuthenticated(), controller.changePassword);
14 | router.get('/:id', auth.isAuthenticated(), controller.show);
15 | router.post('/', controller.create);
16 |
17 | module.exports = router;
18 |
--------------------------------------------------------------------------------
/dist/server/api/user/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var controller = require('./user.controller');
5 | var config = require('../../config/environment');
6 | var auth = require('../../auth/auth.service');
7 |
8 | var router = express.Router();
9 |
10 | router.get('/', auth.hasRole('admin'), controller.index);
11 | router.delete('/:id', auth.hasRole('admin'), controller.destroy);
12 | router.get('/me', auth.isAuthenticated(), controller.me);
13 | router.put('/:id/password', auth.isAuthenticated(), controller.changePassword);
14 | router.get('/:id', auth.isAuthenticated(), controller.show);
15 | router.post('/', controller.create);
16 |
17 | module.exports = router;
18 |
--------------------------------------------------------------------------------
/dist/server/config/local.env.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Use local.env.js for environment variables that grunt will set when the server starts locally.
4 | // Use for your api keys, secrets, etc. This file should not be tracked by git.
5 | //
6 | // You will need to set these on the server you deploy to.
7 |
8 | module.exports = {
9 | DOMAIN: 'http://localhost:9000',
10 | SESSION_SECRET: "meetmeinthemiddle-secret",
11 |
12 | FACEBOOK_ID: 'app-id',
13 | FACEBOOK_SECRET: 'secret',
14 |
15 | TWITTER_ID: 'app-id',
16 | TWITTER_SECRET: 'secret',
17 |
18 | GOOGLE_ID: 'app-id',
19 | GOOGLE_SECRET: 'secret',
20 |
21 | // Control debug level for modules using visionmedia/debug
22 | DEBUG: ''
23 | };
24 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "meet-me-in-the-middle",
3 | "version": "0.0.0",
4 | "dependencies": {
5 | "angular": ">=1.2.*",
6 | "json3": "~3.3.1",
7 | "es5-shim": "~3.0.1",
8 | "jquery": "~1.11.0",
9 | "bootstrap": "~3.1.1",
10 | "angular-resource": ">=1.2.*",
11 | "angular-cookies": ">=1.2.*",
12 | "angular-sanitize": ">=1.2.*",
13 | "angular-bootstrap": "~0.11.0",
14 | "font-awesome": ">=4.1.0",
15 | "lodash": "~3.5.0",
16 | "angular-ui-router": "~0.2.10",
17 | "angular-google-maps": "~2.0.19",
18 | "angularjs-google-directions": "*"
19 | },
20 | "devDependencies": {
21 | "angular-mocks": ">=1.2.*",
22 | "angular-scenario": ">=1.2.*"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/client/app/landing/js/parallax.js:
--------------------------------------------------------------------------------
1 | ////////////////////////////////////////////////////// //
2 | // PARALLAX EFFECT DISABLED ON MOBILE //
3 | ////////////////////////////////////////////////////// //
4 | if (document.documentElement.clientWidth > 991) {
5 | (function(){
6 | "use strict";
7 |
8 | var parallax = document.querySelectorAll(".parallax"),
9 | speed = -0.15;
10 |
11 | window.onscroll = function(){
12 | [].slice.call(parallax).forEach(function(el,i){
13 |
14 | var windowYOffset = window.pageYOffset,
15 | elBackgrounPos = "0 " + (windowYOffset * speed) + "px";
16 |
17 | el.style.backgroundPosition = elBackgrounPos;
18 |
19 | });
20 | };
21 |
22 | })();
23 | }
--------------------------------------------------------------------------------
/server/config/environment/production.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Production specific configuration
4 | // =================================
5 | module.exports = {
6 | // Server IP
7 | ip: process.env.OPENSHIFT_NODEJS_IP ||
8 | process.env.IP ||
9 | undefined,
10 |
11 | // Server port
12 | port: process.env.OPENSHIFT_NODEJS_PORT ||
13 | process.env.PORT ||
14 | 8080,
15 |
16 | // MongoDB connection options
17 | mongo: {
18 | uri: process.env.MONGOLAB_URI ||
19 | process.env.MONGOHQ_URL ||
20 | process.env.OPENSHIFT_MONGODB_DB_URL+process.env.OPENSHIFT_APP_NAME ||
21 | 'mongodb://localhost/meetmeinthemiddle'
22 | }
23 | };
--------------------------------------------------------------------------------
/dist/server/config/environment/production.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Production specific configuration
4 | // =================================
5 | module.exports = {
6 | // Server IP
7 | ip: process.env.OPENSHIFT_NODEJS_IP ||
8 | process.env.IP ||
9 | undefined,
10 |
11 | // Server port
12 | port: process.env.OPENSHIFT_NODEJS_PORT ||
13 | process.env.PORT ||
14 | 8080,
15 |
16 | // MongoDB connection options
17 | mongo: {
18 | uri: process.env.MONGOLAB_URI ||
19 | process.env.MONGOHQ_URL ||
20 | process.env.OPENSHIFT_MONGODB_DB_URL+process.env.OPENSHIFT_APP_NAME ||
21 | 'mongodb://localhost/meetmeinthemiddle'
22 | }
23 | };
--------------------------------------------------------------------------------
/server/auth/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var passport = require('passport');
5 | var config = require('../config/environment');
6 | var User = require('../api/user/user.model');
7 |
8 | // Passport Configuration
9 | require('./local/passport').setup(User, config);
10 | require('./facebook/passport').setup(User, config);
11 | require('./google/passport').setup(User, config);
12 | require('./twitter/passport').setup(User, config);
13 |
14 | var router = express.Router();
15 |
16 | router.use('/local', require('./local'));
17 | router.use('/facebook', require('./facebook'));
18 | router.use('/twitter', require('./twitter'));
19 | router.use('/google', require('./google'));
20 |
21 | module.exports = router;
--------------------------------------------------------------------------------
/dist/server/auth/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var express = require('express');
4 | var passport = require('passport');
5 | var config = require('../config/environment');
6 | var User = require('../api/user/user.model');
7 |
8 | // Passport Configuration
9 | require('./local/passport').setup(User, config);
10 | require('./facebook/passport').setup(User, config);
11 | require('./google/passport').setup(User, config);
12 | require('./twitter/passport').setup(User, config);
13 |
14 | var router = express.Router();
15 |
16 | router.use('/local', require('./local'));
17 | router.use('/facebook', require('./facebook'));
18 | router.use('/twitter', require('./twitter'));
19 | router.use('/google', require('./google'));
20 |
21 | module.exports = router;
--------------------------------------------------------------------------------
/server/routes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Main application routes
3 | */
4 |
5 | 'use strict';
6 |
7 | var errors = require('./components/errors');
8 |
9 | module.exports = function(app) {
10 |
11 | // Insert routes below
12 | app.use('/api/things', require('./api/thing'));
13 | app.use('/api/users', require('./api/user'));
14 |
15 | app.use('/auth', require('./auth'));
16 |
17 | // All undefined asset or api routes should return a 404
18 | app.route('/:url(api|auth|components|app|bower_components|assets)/*')
19 | .get(errors[404]);
20 |
21 | // All other routes should redirect to the index.html
22 | app.route('/*')
23 | .get(function(req, res) {
24 | res.sendfile(app.get('appPath') + '/index.html');
25 | });
26 | };
27 |
--------------------------------------------------------------------------------
/dist/server/routes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Main application routes
3 | */
4 |
5 | 'use strict';
6 |
7 | var errors = require('./components/errors');
8 |
9 | module.exports = function(app) {
10 |
11 | // Insert routes below
12 | app.use('/api/things', require('./api/thing'));
13 | app.use('/api/users', require('./api/user'));
14 |
15 | app.use('/auth', require('./auth'));
16 |
17 | // All undefined asset or api routes should return a 404
18 | app.route('/:url(api|auth|components|app|bower_components|assets)/*')
19 | .get(errors[404]);
20 |
21 | // All other routes should redirect to the index.html
22 | app.route('/*')
23 | .get(function(req, res) {
24 | res.sendfile(app.get('appPath') + '/index.html');
25 | });
26 | };
27 |
--------------------------------------------------------------------------------
/server/config/local.env.sample.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Use local.env.js for environment variables that grunt will set when the server starts locally.
4 | // Use for your api keys, secrets, etc. This file should not be tracked by git.
5 | //
6 | // You will need to set these on the server you deploy to.
7 |
8 | module.exports = {
9 | DOMAIN: 'http://localhost:9000',
10 | SESSION_SECRET: 'meetmeinthemiddle-secret',
11 |
12 | FACEBOOK_ID: 'app-id',
13 | FACEBOOK_SECRET: 'secret',
14 |
15 | TWITTER_ID: 'app-id',
16 | TWITTER_SECRET: 'secret',
17 |
18 | GOOGLE_ID: 'app-id',
19 | GOOGLE_SECRET: 'secret',
20 |
21 | // Control debug level for modules using visionmedia/debug
22 | DEBUG: ''
23 | };
24 |
--------------------------------------------------------------------------------
/client/app/account/settings/settings.controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('meetMeInTheMiddleApp')
4 | .controller('SettingsCtrl', function ($scope, User, Auth) {
5 | $scope.errors = {};
6 |
7 | $scope.changePassword = function(form) {
8 | $scope.submitted = true;
9 | if(form.$valid) {
10 | Auth.changePassword( $scope.user.oldPassword, $scope.user.newPassword )
11 | .then( function() {
12 | $scope.message = 'Password successfully changed.';
13 | })
14 | .catch( function() {
15 | form.password.$setValidity('mongoose', false);
16 | $scope.errors.other = 'Incorrect password';
17 | $scope.message = '';
18 | });
19 | }
20 | };
21 | });
22 |
--------------------------------------------------------------------------------
/dist/server/config/local.env.sample.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Use local.env.js for environment variables that grunt will set when the server starts locally.
4 | // Use for your api keys, secrets, etc. This file should not be tracked by git.
5 | //
6 | // You will need to set these on the server you deploy to.
7 |
8 | module.exports = {
9 | DOMAIN: 'http://localhost:9000',
10 | SESSION_SECRET: 'meetmeinthemiddle-secret',
11 |
12 | FACEBOOK_ID: 'app-id',
13 | FACEBOOK_SECRET: 'secret',
14 |
15 | TWITTER_ID: 'app-id',
16 | TWITTER_SECRET: 'secret',
17 |
18 | GOOGLE_ID: 'app-id',
19 | GOOGLE_SECRET: 'secret',
20 |
21 | // Control debug level for modules using visionmedia/debug
22 | DEBUG: ''
23 | };
24 |
--------------------------------------------------------------------------------
/client/app/main/main.controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('meetMeInTheMiddleApp')
4 | .controller('MainCtrl', function ($scope, $http) { //Ko: socket.io was made injected at /client/app/app.js line 6
5 | $scope.awesomeThings = [];
6 |
7 | $http.get('/api/things').success(function(awesomeThings) {
8 | $scope.awesomeThings = awesomeThings;
9 | });
10 |
11 | $scope.addThing = function() {
12 | if($scope.newThing === '') {
13 | return;
14 | }
15 | $http.post('/api/things', { name: $scope.newThing });
16 | $scope.newThing = '';
17 | };
18 |
19 | $scope.deleteThing = function(thing) {
20 | $http.delete('/api/things/' + thing._id);
21 | };
22 |
23 | $scope.$on('$destroy', function () {
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/client/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": true,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": true,
18 | "strict": true,
19 | "trailing": true,
20 | "smarttabs": true,
21 | "globals": {
22 | "jQuery": true,
23 | "angular": true,
24 | "console": true,
25 | "$": true,
26 | "_": true,
27 | "moment": true,
28 | "describe": true,
29 | "beforeEach": true,
30 | "module": true,
31 | "inject": true,
32 | "it": true,
33 | "expect": true,
34 | "browser": true,
35 | "element": true,
36 | "by": true
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/client/app/account/login/login.controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('meetMeInTheMiddleApp')
4 | .controller('LoginCtrl', function ($scope, Auth, $location, $window) {
5 | $scope.user = {};
6 | $scope.errors = {};
7 |
8 | $scope.login = function(form) {
9 | $scope.submitted = true;
10 |
11 | if(form.$valid) {
12 | Auth.login({
13 | email: $scope.user.email,
14 | password: $scope.user.password
15 | })
16 | .then( function() {
17 | // Logged in, redirect to home
18 | $location.path('/');
19 | })
20 | .catch( function(err) {
21 | $scope.errors.other = err.message;
22 | });
23 | }
24 | };
25 |
26 | $scope.loginOauth = function(provider) {
27 | $window.location.href = '/auth/' + provider;
28 | };
29 | });
30 |
--------------------------------------------------------------------------------
/server/auth/local/passport.js:
--------------------------------------------------------------------------------
1 | var passport = require('passport');
2 | var LocalStrategy = require('passport-local').Strategy;
3 |
4 | exports.setup = function (User, config) {
5 | passport.use(new LocalStrategy({
6 | usernameField: 'email',
7 | passwordField: 'password' // this is the virtual field on the model
8 | },
9 | function(email, password, done) {
10 | User.findOne({
11 | email: email.toLowerCase()
12 | }, function(err, user) {
13 | if (err) return done(err);
14 |
15 | if (!user) {
16 | return done(null, false, { message: 'This email is not registered.' });
17 | }
18 | if (!user.authenticate(password)) {
19 | return done(null, false, { message: 'This password is not correct.' });
20 | }
21 | return done(null, user);
22 | });
23 | }
24 | ));
25 | };
--------------------------------------------------------------------------------
/dist/server/auth/local/passport.js:
--------------------------------------------------------------------------------
1 | var passport = require('passport');
2 | var LocalStrategy = require('passport-local').Strategy;
3 |
4 | exports.setup = function (User, config) {
5 | passport.use(new LocalStrategy({
6 | usernameField: 'email',
7 | passwordField: 'password' // this is the virtual field on the model
8 | },
9 | function(email, password, done) {
10 | User.findOne({
11 | email: email.toLowerCase()
12 | }, function(err, user) {
13 | if (err) return done(err);
14 |
15 | if (!user) {
16 | return done(null, false, { message: 'This email is not registered.' });
17 | }
18 | if (!user.authenticate(password)) {
19 | return done(null, false, { message: 'This password is not correct.' });
20 | }
21 | return done(null, user);
22 | });
23 | }
24 | ));
25 | };
--------------------------------------------------------------------------------
/client/app/main/main.css:
--------------------------------------------------------------------------------
1 | /*.thing-form {
2 | margin: 20px 0;
3 | }
4 |
5 | #banner {
6 | border-bottom: none;
7 | margin-top: -20px;
8 | }
9 |
10 | #banner h1 {
11 | font-size: 60px;
12 | line-height: 1;
13 | letter-spacing: -1px;
14 | }
15 |
16 | .hero-unit {
17 | position: relative;
18 | padding: 30px 15px;
19 | color: #F5F5F5;
20 | text-align: center;
21 | text-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);
22 | background: #4393B9;
23 | }
24 |
25 | .footer {
26 | text-align: center;
27 | padding: 30px 0;
28 | margin-top: 70px;
29 | border-top: 1px solid #E5E5E5;
30 | }*/
31 |
32 | /*
33 | Navbar style
34 | */
35 | .inButton {
36 | display: block;
37 | height: 100%;
38 | }
39 |
40 | .carousel-team {
41 | max-width: 800px;
42 | margin: 0 auto;
43 | }
44 |
45 | .owl-stage {
46 | margin: 0 auto;
47 |
48 | }
--------------------------------------------------------------------------------
/client/app/main/main.controller.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Controller: MainCtrl', function () {
4 |
5 | // load the controller's module
6 | beforeEach(module('meetMeInTheMiddleApp'));
7 | beforeEach(module('socketMock'));
8 |
9 | var MainCtrl,
10 | scope,
11 | $httpBackend;
12 |
13 | // Initialize the controller and a mock scope
14 | beforeEach(inject(function (_$httpBackend_, $controller, $rootScope) {
15 | $httpBackend = _$httpBackend_;
16 | $httpBackend.expectGET('/api/things')
17 | .respond(['HTML5 Boilerplate', 'AngularJS', 'Karma', 'Express']);
18 |
19 | scope = $rootScope.$new();
20 | MainCtrl = $controller('MainCtrl', {
21 | $scope: scope
22 | });
23 | }));
24 |
25 | it('should attach a list of things to the scope', function () {
26 | $httpBackend.flush();
27 | expect(scope.awesomeThings.length).toBe(4);
28 | });
29 | });
30 |
--------------------------------------------------------------------------------
/server/auth/twitter/passport.js:
--------------------------------------------------------------------------------
1 | exports.setup = function (User, config) {
2 | var passport = require('passport');
3 | var TwitterStrategy = require('passport-twitter').Strategy;
4 |
5 | passport.use(new TwitterStrategy({
6 | consumerKey: config.twitter.clientID,
7 | consumerSecret: config.twitter.clientSecret,
8 | callbackURL: config.twitter.callbackURL
9 | },
10 | function(token, tokenSecret, profile, done) {
11 | User.findOne({
12 | 'twitter.id_str': profile.id
13 | }, function(err, user) {
14 | if (err) {
15 | return done(err);
16 | }
17 | if (!user) {
18 | user = new User({
19 | name: profile.displayName,
20 | username: profile.username,
21 | role: 'user',
22 | provider: 'twitter',
23 | twitter: profile._json
24 | });
25 | user.save(function(err) {
26 | if (err) return done(err);
27 | return done(err, user);
28 | });
29 | } else {
30 | return done(err, user);
31 | }
32 | });
33 | }
34 | ));
35 | };
--------------------------------------------------------------------------------
/dist/server/auth/twitter/passport.js:
--------------------------------------------------------------------------------
1 | exports.setup = function (User, config) {
2 | var passport = require('passport');
3 | var TwitterStrategy = require('passport-twitter').Strategy;
4 |
5 | passport.use(new TwitterStrategy({
6 | consumerKey: config.twitter.clientID,
7 | consumerSecret: config.twitter.clientSecret,
8 | callbackURL: config.twitter.callbackURL
9 | },
10 | function(token, tokenSecret, profile, done) {
11 | User.findOne({
12 | 'twitter.id_str': profile.id
13 | }, function(err, user) {
14 | if (err) {
15 | return done(err);
16 | }
17 | if (!user) {
18 | user = new User({
19 | name: profile.displayName,
20 | username: profile.username,
21 | role: 'user',
22 | provider: 'twitter',
23 | twitter: profile._json
24 | });
25 | user.save(function(err) {
26 | if (err) return done(err);
27 | return done(err, user);
28 | });
29 | } else {
30 | return done(err, user);
31 | }
32 | });
33 | }
34 | ));
35 | };
--------------------------------------------------------------------------------
/server/auth/google/passport.js:
--------------------------------------------------------------------------------
1 | var passport = require('passport');
2 | var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
3 |
4 | exports.setup = function (User, config) {
5 | passport.use(new GoogleStrategy({
6 | clientID: config.google.clientID,
7 | clientSecret: config.google.clientSecret,
8 | callbackURL: config.google.callbackURL
9 | },
10 | function(accessToken, refreshToken, profile, done) {
11 | User.findOne({
12 | 'google.id': profile.id
13 | }, function(err, user) {
14 | if (!user) {
15 | user = new User({
16 | name: profile.displayName,
17 | email: profile.emails[0].value,
18 | role: 'user',
19 | username: profile.username,
20 | provider: 'google',
21 | google: profile._json
22 | });
23 | user.save(function(err) {
24 | if (err) done(err);
25 | return done(err, user);
26 | });
27 | } else {
28 | return done(err, user);
29 | }
30 | });
31 | }
32 | ));
33 | };
34 |
--------------------------------------------------------------------------------
/dist/server/auth/google/passport.js:
--------------------------------------------------------------------------------
1 | var passport = require('passport');
2 | var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
3 |
4 | exports.setup = function (User, config) {
5 | passport.use(new GoogleStrategy({
6 | clientID: config.google.clientID,
7 | clientSecret: config.google.clientSecret,
8 | callbackURL: config.google.callbackURL
9 | },
10 | function(accessToken, refreshToken, profile, done) {
11 | User.findOne({
12 | 'google.id': profile.id
13 | }, function(err, user) {
14 | if (!user) {
15 | user = new User({
16 | name: profile.displayName,
17 | email: profile.emails[0].value,
18 | role: 'user',
19 | username: profile.username,
20 | provider: 'google',
21 | google: profile._json
22 | });
23 | user.save(function(err) {
24 | if (err) done(err);
25 | return done(err, user);
26 | });
27 | } else {
28 | return done(err, user);
29 | }
30 | });
31 | }
32 | ));
33 | };
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Meet Me In The Middle
2 |
3 | > An application that locates the midpoint between two users using the Google Maps API.
4 |
5 | ## Team
6 |
7 | - __Product Owner__: Ko Seunghoon
8 | - __Scrum Master__: Jordan Genung
9 | - __Development Team Members__: Jonah Nisenson, Rioa Mattsson
10 |
11 | ## Table of Contents
12 |
13 | 1. [Usage](#Usage)
14 | 1. [Requirements](#requirements)
15 | 1. [Development](#development)
16 | 1. [Installing Dependencies](#installing-dependencies)
17 | 1. [Tasks](#tasks)
18 | 1. [Team](#team)
19 | 1. [Contributing](#contributing)
20 |
21 | ## Usage
22 |
23 | > Some usage instructions
24 |
25 | ## Requirements
26 |
27 | - Node 0.10.x
28 | - Redis 2.6.x
29 | - Postgresql 9.1.x
30 | - etc
31 | - etc
32 |
33 | ## Development
34 |
35 | ### Installing Dependencies
36 |
37 | From within the root directory:
38 |
39 | ```sh
40 | sudo npm install -g bower
41 | npm install
42 | bower install
43 | ```
44 |
45 | ### Roadmap
46 |
47 | View the project roadmap [here](LINK_TO_PROJECT_ISSUES)
48 |
49 |
50 | ## Contributing
51 |
52 | See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.
53 |
--------------------------------------------------------------------------------
/dist/server/app.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Main application file
3 | */
4 |
5 | 'use strict';
6 |
7 | // Set default node environment to development
8 | process.env.NODE_ENV = process.env.NODE_ENV || 'development';
9 |
10 | var express = require('express');
11 | var mongoose = require('mongoose');
12 | var config = require('./config/environment');
13 |
14 | // Connect to database
15 | mongoose.connect(config.mongo.uri, config.mongo.options);
16 |
17 | // Populate DB with sample data
18 | if(config.seedDB) { require('./config/seed'); }
19 |
20 | // Setup server
21 | var app = express();
22 | var server = require('http').createServer(app);
23 | var socketio = require('socket.io')(server, {
24 | serveClient: (config.env === 'production') ? false : true,
25 | path: '/socket.io-client'
26 | });
27 | require('./config/socketio')(socketio);
28 | require('./config/express')(app);
29 | require('./routes')(app);
30 |
31 | // Start server
32 | server.listen(config.port, config.ip, function () {
33 | console.log('Express server listening on %d, in %s mode', config.port, app.get('env'));
34 | });
35 |
36 | // Expose app
37 | //exports = module.exports = app;
38 | module.exports = app;
39 |
--------------------------------------------------------------------------------
/server/auth/facebook/passport.js:
--------------------------------------------------------------------------------
1 | var passport = require('passport');
2 | var FacebookStrategy = require('passport-facebook').Strategy;
3 |
4 | exports.setup = function (User, config) {
5 | passport.use(new FacebookStrategy({
6 | clientID: config.facebook.clientID,
7 | clientSecret: config.facebook.clientSecret,
8 | callbackURL: config.facebook.callbackURL
9 | },
10 | function(accessToken, refreshToken, profile, done) {
11 | User.findOne({
12 | 'facebook.id': profile.id
13 | },
14 | function(err, user) {
15 | if (err) {
16 | return done(err);
17 | }
18 | if (!user) {
19 | user = new User({
20 | name: profile.displayName,
21 | email: profile.emails[0].value,
22 | role: 'user',
23 | username: profile.username,
24 | provider: 'facebook',
25 | facebook: profile._json
26 | });
27 | user.save(function(err) {
28 | if (err) done(err);
29 | return done(err, user);
30 | });
31 | } else {
32 | return done(err, user);
33 | }
34 | })
35 | }
36 | ));
37 | };
--------------------------------------------------------------------------------
/dist/server/auth/facebook/passport.js:
--------------------------------------------------------------------------------
1 | var passport = require('passport');
2 | var FacebookStrategy = require('passport-facebook').Strategy;
3 |
4 | exports.setup = function (User, config) {
5 | passport.use(new FacebookStrategy({
6 | clientID: config.facebook.clientID,
7 | clientSecret: config.facebook.clientSecret,
8 | callbackURL: config.facebook.callbackURL
9 | },
10 | function(accessToken, refreshToken, profile, done) {
11 | User.findOne({
12 | 'facebook.id': profile.id
13 | },
14 | function(err, user) {
15 | if (err) {
16 | return done(err);
17 | }
18 | if (!user) {
19 | user = new User({
20 | name: profile.displayName,
21 | email: profile.emails[0].value,
22 | role: 'user',
23 | username: profile.username,
24 | provider: 'facebook',
25 | facebook: profile._json
26 | });
27 | user.save(function(err) {
28 | if (err) done(err);
29 | return done(err, user);
30 | });
31 | } else {
32 | return done(err, user);
33 | }
34 | })
35 | }
36 | ));
37 | };
--------------------------------------------------------------------------------
/client/app/account/signup/signup.controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('meetMeInTheMiddleApp')
4 | .controller('SignupCtrl', function ($scope, Auth, $location, $window) {
5 | $scope.user = {};
6 | $scope.errors = {};
7 |
8 | $scope.register = function(form) {
9 | $scope.submitted = true;
10 |
11 | if(form.$valid) {
12 | Auth.createUser({
13 | name: $scope.user.name,
14 | email: $scope.user.email,
15 | password: $scope.user.password
16 | })
17 | .then( function() {
18 | // Account created, redirect to home
19 | $location.path('/');
20 | })
21 | .catch( function(err) {
22 | err = err.data;
23 | $scope.errors = {};
24 |
25 | // Update validity of form fields that match the mongoose errors
26 | angular.forEach(err.errors, function(error, field) {
27 | form[field].$setValidity('mongoose', false);
28 | $scope.errors[field] = error.message;
29 | });
30 | });
31 | }
32 | };
33 |
34 | $scope.loginOauth = function(provider) {
35 | $window.location.href = '/auth/' + provider;
36 | };
37 | });
38 |
--------------------------------------------------------------------------------
/client/app/landing/js/jQuery.headroom.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 |
3 | if(!$) {
4 | return;
5 | }
6 |
7 | ////////////
8 | // Plugin //
9 | ////////////
10 |
11 | $.fn.headroom = function(option) {
12 | return this.each(function() {
13 | var $this = $(this),
14 | data = $this.data('headroom'),
15 | options = typeof option === 'object' && option;
16 |
17 | options = $.extend(true, {}, Headroom.options, options);
18 |
19 | if (!data) {
20 | data = new Headroom(this, options);
21 | data.init();
22 | $this.data('headroom', data);
23 | }
24 | if (typeof option === 'string') {
25 | data[option]();
26 | }
27 | });
28 | };
29 |
30 | //////////////
31 | // Data API //
32 | //////////////
33 |
34 | $('[data-headroom]').each(function() {
35 | var $this = $(this);
36 | $this.headroom($this.data());
37 | });
38 |
39 | }(window.Zepto || window.jQuery));
40 |
41 |
42 | //navbar configuration below
43 | $(".navbar-fixed-top").headroom({
44 | "offset": 205,
45 | "tolerance": 5,
46 | "classes": {
47 | "pinned": "swingInX",
48 | "unpinned": "swingOutX"
49 | }
50 | });
51 | // to destroy
52 | $(".navbar-fixed-top").headroom("destroy");
--------------------------------------------------------------------------------
/client/app/landing/js/jquery.inview.min.js:
--------------------------------------------------------------------------------
1 | !function(t){function e(){"use strict";var e,i,n={height:a.innerHeight,width:a.innerWidth};return n.height||(e=r.compatMode,(e||!t.support.boxModel)&&(i="CSS1Compat"===e?f:r.body,n={height:i.clientHeight,width:i.clientWidth})),n}function i(){return{top:a.pageYOffset||f.scrollTop||r.body.scrollTop,left:a.pageXOffset||f.scrollLeft||r.body.scrollLeft}}function n(){var n,l=t(),r=0;if(t.each(d,function(t,e){var i=e.data.selector,n=e.$element;l=l.add(i?n.find(i):n)}),n=l.length)for(o=o||e(),h=h||i();n>r;r++)if(t.contains(f,l[r])){var a,c,p,s=t(l[r]),u={height:s.height(),width:s.width()},g=s.offset(),v=s.data("inview");if(!h||!o)return;g.top+u.height>h.top&&g.toph.left&&g.leftg.left?"right":h.left+o.widthg.top?"bottom":h.top+o.height
2 |
3 |
4 |
5 |
6 |
Change Password
7 |
8 |
38 |
39 |
--------------------------------------------------------------------------------
/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {
2 | "generator-angular-fullstack": {
3 | "insertRoutes": true,
4 | "registerRoutesFile": "server/routes.js",
5 | "routesNeedle": "// Insert routes below",
6 | "routesBase": "/api/",
7 | "pluralizeRoutes": true,
8 | "insertSockets": true,
9 | "registerSocketsFile": "server/config/socketio.js",
10 | "socketsNeedle": "// Insert sockets below",
11 | "filters": {
12 | "js": true,
13 | "html": true,
14 | "css": true,
15 | "uirouter": true,
16 | "bootstrap": true,
17 | "uibootstrap": true,
18 | "socketio": true,
19 | "mongoose": true,
20 | "auth": true,
21 | "oauth": true,
22 | "googleAuth": true,
23 | "facebookAuth": true,
24 | "twitterAuth": true
25 | }
26 | },
27 | "generator-ng-component": {
28 | "routeDirectory": "client/app/",
29 | "directiveDirectory": "client/app/",
30 | "filterDirectory": "client/app/",
31 | "serviceDirectory": "client/app/",
32 | "basePath": "client",
33 | "moduleName": "",
34 | "filters": [
35 | "uirouter"
36 | ],
37 | "extensions": [
38 | "js",
39 | "html",
40 | "css"
41 | ],
42 | "directiveSimpleTemplates": "",
43 | "directiveComplexTemplates": "",
44 | "filterTemplates": "",
45 | "serviceTemplates": "",
46 | "factoryTemplates": "",
47 | "controllerTemplates": "",
48 | "decoratorTemplates": "",
49 | "providerTemplates": "",
50 | "routeTemplates": ""
51 | }
52 | }
--------------------------------------------------------------------------------
/client/app/app.css:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * Bootstrap Fonts
4 | */
5 |
6 | /*@font-face {
7 | font-family: 'Glyphicons Halflings';
8 | src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot');
9 | src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
10 | url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.woff') format('woff'),
11 | url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.ttf') format('truetype'),
12 | url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
13 | }
14 | */
15 | /**
16 | *Font Awesome Fonts
17 | */
18 |
19 | /*@font-face {
20 | font-family: 'FontAwesome';
21 | src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?v=4.1.0');
22 | src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'),
23 | url('../bower_components/font-awesome/fonts/fontawesome-webfont.woff?v=4.1.0') format('woff'),
24 | url('../bower_components/font-awesome/fonts/fontawesome-webfont.ttf?v=4.1.0') format('truetype'),
25 | url('../bower_components/font-awesome/fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg');
26 | font-weight: normal;
27 | font-style: normal;
28 | }
29 | */
30 | /**
31 | * App-wide Styles
32 | */
33 |
34 | /*.browsehappy {
35 | margin: 0.2em 0;
36 | background: #ccc;
37 | color: #000;
38 | padding: 0.2em 0;
39 | }
40 | */
--------------------------------------------------------------------------------
/server/api/user/user.model.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var should = require('should');
4 | var app = require('../../app');
5 | var User = require('./user.model');
6 |
7 | var user = new User({
8 | provider: 'local',
9 | name: 'Fake User',
10 | email: 'test@test.com',
11 | password: 'password'
12 | });
13 |
14 | describe('User Model', function() {
15 | before(function(done) {
16 | // Clear users before testing
17 | User.remove().exec().then(function() {
18 | done();
19 | });
20 | });
21 |
22 | afterEach(function(done) {
23 | User.remove().exec().then(function() {
24 | done();
25 | });
26 | });
27 |
28 | it('should begin with no users', function(done) {
29 | User.find({}, function(err, users) {
30 | users.should.have.length(0);
31 | done();
32 | });
33 | });
34 |
35 | it('should fail when saving a duplicate user', function(done) {
36 | user.save(function() {
37 | var userDup = new User(user);
38 | userDup.save(function(err) {
39 | should.exist(err);
40 | done();
41 | });
42 | });
43 | });
44 |
45 | it('should fail when saving without an email', function(done) {
46 | user.email = '';
47 | user.save(function(err) {
48 | should.exist(err);
49 | done();
50 | });
51 | });
52 |
53 | it("should authenticate user if password is valid", function() {
54 | return user.authenticate('password').should.be.true;
55 | });
56 |
57 | it("should not authenticate user if password is invalid", function() {
58 | return user.authenticate('blah').should.not.be.true;
59 | });
60 | });
61 |
--------------------------------------------------------------------------------
/dist/server/api/user/user.model.spec.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var should = require('should');
4 | var app = require('../../app');
5 | var User = require('./user.model');
6 |
7 | var user = new User({
8 | provider: 'local',
9 | name: 'Fake User',
10 | email: 'test@test.com',
11 | password: 'password'
12 | });
13 |
14 | describe('User Model', function() {
15 | before(function(done) {
16 | // Clear users before testing
17 | User.remove().exec().then(function() {
18 | done();
19 | });
20 | });
21 |
22 | afterEach(function(done) {
23 | User.remove().exec().then(function() {
24 | done();
25 | });
26 | });
27 |
28 | it('should begin with no users', function(done) {
29 | User.find({}, function(err, users) {
30 | users.should.have.length(0);
31 | done();
32 | });
33 | });
34 |
35 | it('should fail when saving a duplicate user', function(done) {
36 | user.save(function() {
37 | var userDup = new User(user);
38 | userDup.save(function(err) {
39 | should.exist(err);
40 | done();
41 | });
42 | });
43 | });
44 |
45 | it('should fail when saving without an email', function(done) {
46 | user.email = '';
47 | user.save(function(err) {
48 | should.exist(err);
49 | done();
50 | });
51 | });
52 |
53 | it("should authenticate user if password is valid", function() {
54 | return user.authenticate('password').should.be.true;
55 | });
56 |
57 | it("should not authenticate user if password is invalid", function() {
58 | return user.authenticate('blah').should.not.be.true;
59 | });
60 | });
61 |
--------------------------------------------------------------------------------
/client/app/app.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('meetMeInTheMiddleApp', [
4 | 'ngCookies',
5 | 'ngResource',
6 | 'ngSanitize',
7 | 'ui.router',
8 | 'ui.bootstrap',
9 | 'uiGmapgoogle-maps'
10 | ])
11 | .config(function ($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider) {
12 | $urlRouterProvider
13 | .otherwise('/');
14 |
15 | $locationProvider.html5Mode(true);
16 | $httpProvider.interceptors.push('authInterceptor');
17 | })
18 |
19 | .factory('authInterceptor', function ($rootScope, $q, $cookieStore, $location) {
20 | return {
21 | // Add authorization token to headers
22 | request: function (config) {
23 | config.headers = config.headers || {};
24 | if ($cookieStore.get('token')) {
25 | config.headers.Authorization = 'Bearer ' + $cookieStore.get('token');
26 | }
27 | return config;
28 | },
29 |
30 | // Intercept 401s and redirect you to login
31 | responseError: function(response) {
32 | if(response.status === 401) {
33 | $location.path('/login');
34 | // remove any stale tokens
35 | $cookieStore.remove('token');
36 | return $q.reject(response);
37 | }
38 | else {
39 | return $q.reject(response);
40 | }
41 | }
42 | };
43 | })
44 | .run(function ($rootScope, $location, Auth) {
45 | // Redirect to login if route requires auth and you're not logged in
46 | $rootScope.$on('$stateChangeStart', function (event, next) {
47 | Auth.isLoggedInAsync(function(loggedIn) {
48 | if (next.authenticate && !loggedIn) {
49 | $location.path('/login');
50 | }
51 | });
52 | });
53 | });
--------------------------------------------------------------------------------
/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration
2 | // https://github.com/angular/protractor/blob/master/referenceConf.js
3 |
4 | 'use strict';
5 |
6 | exports.config = {
7 | // The timeout for each script run on the browser. This should be longer
8 | // than the maximum time your application needs to stabilize between tasks.
9 | allScriptsTimeout: 110000,
10 |
11 | // A base URL for your application under test. Calls to protractor.get()
12 | // with relative paths will be prepended with this.
13 | baseUrl: 'http://localhost:' + (process.env.PORT || '9000'),
14 |
15 | // If true, only chromedriver will be started, not a standalone selenium.
16 | // Tests for browsers other than chrome will not run.
17 | chromeOnly: true,
18 |
19 | // list of files / patterns to load in the browser
20 | specs: [
21 | 'e2e/**/*.spec.js'
22 | ],
23 |
24 | // Patterns to exclude.
25 | exclude: [],
26 |
27 | // ----- Capabilities to be passed to the webdriver instance ----
28 | //
29 | // For a full list of available capabilities, see
30 | // https://code.google.com/p/selenium/wiki/DesiredCapabilities
31 | // and
32 | // https://code.google.com/p/selenium/source/browse/javascript/webdriver/capabilities.js
33 | capabilities: {
34 | 'browserName': 'chrome'
35 | },
36 |
37 | // ----- The test framework -----
38 | //
39 | // Jasmine and Cucumber are fully supported as a test and assertion framework.
40 | // Mocha has limited beta support. You will need to include your own
41 | // assertion framework if working with mocha.
42 | framework: 'jasmine',
43 |
44 | // ----- Options to be passed to minijasminenode -----
45 | //
46 | // See the full list at https://github.com/juliemr/minijasminenode
47 | jasmineNodeOpts: {
48 | defaultTimeoutInterval: 30000
49 | }
50 | };
51 |
--------------------------------------------------------------------------------
/server/config/seed.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Populate DB with sample data on server start
3 | * to disable, edit config/environment/index.js, and set `seedDB: false`
4 | */
5 |
6 | 'use strict';
7 |
8 | var Thing = require('../api/thing/thing.model');
9 | var User = require('../api/user/user.model');
10 |
11 | Thing.find({}).remove(function() {
12 | Thing.create({
13 | name : 'Development Tools',
14 | info : 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.'
15 | }, {
16 | name : 'Server and Client integration',
17 | info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.'
18 | }, {
19 | name : 'Smart Build System',
20 | info : 'Build system ignores `spec` files, allowing you to keep tests alongside code. Automatic injection of scripts and styles into your index.html'
21 | }, {
22 | name : 'Modular Structure',
23 | info : 'Best practice client and server structures allow for more code reusability and maximum scalability'
24 | }, {
25 | name : 'Optimized Build',
26 | info : 'Build process packs up your templates as a single JavaScript payload, minifies your scripts/css/images, and rewrites asset names for caching.'
27 | },{
28 | name : 'Deployment Ready',
29 | info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators'
30 | });
31 | });
32 |
33 | User.find({}).remove(function() {
34 | User.create({
35 | provider: 'local',
36 | name: 'Test User',
37 | email: 'test@test.com',
38 | password: 'test'
39 | }, {
40 | provider: 'local',
41 | role: 'admin',
42 | name: 'Admin',
43 | email: 'admin@admin.com',
44 | password: 'admin'
45 | }, function() {
46 | console.log('finished populating users');
47 | }
48 | );
49 | });
--------------------------------------------------------------------------------
/dist/server/config/seed.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Populate DB with sample data on server start
3 | * to disable, edit config/environment/index.js, and set `seedDB: false`
4 | */
5 |
6 | 'use strict';
7 |
8 | var Thing = require('../api/thing/thing.model');
9 | var User = require('../api/user/user.model');
10 |
11 | Thing.find({}).remove(function() {
12 | Thing.create({
13 | name : 'Development Tools',
14 | info : 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.'
15 | }, {
16 | name : 'Server and Client integration',
17 | info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.'
18 | }, {
19 | name : 'Smart Build System',
20 | info : 'Build system ignores `spec` files, allowing you to keep tests alongside code. Automatic injection of scripts and styles into your index.html'
21 | }, {
22 | name : 'Modular Structure',
23 | info : 'Best practice client and server structures allow for more code reusability and maximum scalability'
24 | }, {
25 | name : 'Optimized Build',
26 | info : 'Build process packs up your templates as a single JavaScript payload, minifies your scripts/css/images, and rewrites asset names for caching.'
27 | },{
28 | name : 'Deployment Ready',
29 | info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators'
30 | });
31 | });
32 |
33 | User.find({}).remove(function() {
34 | User.create({
35 | provider: 'local',
36 | name: 'Test User',
37 | email: 'test@test.com',
38 | password: 'test'
39 | }, {
40 | provider: 'local',
41 | role: 'admin',
42 | name: 'Admin',
43 | email: 'admin@admin.com',
44 | password: 'admin'
45 | }, function() {
46 | console.log('finished populating users');
47 | }
48 | );
49 | });
--------------------------------------------------------------------------------
/dist/server/config/socketio.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Socket.io configuration
3 | */
4 |
5 | 'use strict';
6 |
7 | var config = require('./environment');
8 |
9 | // When the user disconnects.. perform this
10 | function onDisconnect(socket) {
11 | }
12 |
13 | // When the user connects.. perform this
14 | function onConnect(socket) {
15 | // When the client emits 'info', this listens and executes
16 | socket.on('info', function (data) {
17 | console.info('[%s] %s', socket.address, JSON.stringify(data, null, 2));
18 | });
19 |
20 | // Insert sockets below
21 | require('../api/thing/thing.socket').register(socket);
22 | }
23 |
24 | module.exports = function (socketio) {
25 | // socket.io (v1.x.x) is powered by debug.
26 | // In order to see all the debug output, set DEBUG (in server/config/local.env.js) to including the desired scope.
27 | //
28 | // ex: DEBUG: "http*,socket.io:socket"
29 |
30 | // We can authenticate socket.io users and access their token through socket.handshake.decoded_token
31 | //
32 | // 1. You will need to send the token in `client/components/socket/socket.service.js`
33 | //
34 | // 2. Require authentication here:
35 | // socketio.use(require('socketio-jwt').authorize({
36 | // secret: config.secrets.session,
37 | // handshake: true
38 | // }));
39 |
40 | socketio.on('connection', function (socket) {
41 | socket.address = socket.handshake.address !== null ?
42 | socket.handshake.address.address + ':' + socket.handshake.address.port :
43 | process.env.DOMAIN;
44 |
45 | socket.connectedAt = new Date();
46 |
47 | // Call onDisconnect.
48 | socket.on('disconnect', function () {
49 | onDisconnect(socket);
50 | console.info('[%s] DISCONNECTED', socket.address);
51 | });
52 |
53 | // Call onConnect.
54 | onConnect(socket);
55 | console.info('[%s] CONNECTED', socket.address);
56 | });
57 | };
--------------------------------------------------------------------------------
/server/config/environment/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var path = require('path');
4 | var _ = require('lodash');
5 |
6 | function requiredProcessEnv(name) {
7 | if(!process.env[name]) {
8 | throw new Error('You must set the ' + name + ' environment variable');
9 | }
10 | return process.env[name];
11 | }
12 |
13 | // All configurations will extend these options
14 | // ============================================
15 | var all = {
16 | env: process.env.NODE_ENV,
17 |
18 | // Root path of server
19 | root: path.normalize(__dirname + '/../../..'),
20 |
21 | // Server port
22 | port: process.env.PORT || 9000,
23 |
24 | // Should we populate the DB with sample data?
25 | seedDB: false,
26 |
27 | // Secret for session, you will want to change this and make it an environment variable
28 | secrets: {
29 | session: 'meet-me-in-the-middle-secret'
30 | },
31 |
32 | // List of user roles
33 | userRoles: ['guest', 'user', 'admin'],
34 |
35 | // MongoDB connection options
36 | mongo: {
37 | options: {
38 | db: {
39 | safe: true
40 | }
41 | }
42 | },
43 |
44 | facebook: {
45 | clientID: process.env.FACEBOOK_ID || 'id',
46 | clientSecret: process.env.FACEBOOK_SECRET || 'secret',
47 | callbackURL: (process.env.DOMAIN || '') + '/auth/facebook/callback'
48 | },
49 |
50 | twitter: {
51 | clientID: process.env.TWITTER_ID || 'id',
52 | clientSecret: process.env.TWITTER_SECRET || 'secret',
53 | callbackURL: (process.env.DOMAIN || '') + '/auth/twitter/callback'
54 | },
55 |
56 | google: {
57 | clientID: process.env.GOOGLE_ID || 'id',
58 | clientSecret: process.env.GOOGLE_SECRET || 'secret',
59 | callbackURL: (process.env.DOMAIN || '') + '/auth/google/callback'
60 | }
61 | };
62 |
63 | // Export the config object based on the NODE_ENV
64 | // ==============================================
65 | module.exports = _.merge(
66 | all,
67 | require('./' + process.env.NODE_ENV + '.js') || {});
--------------------------------------------------------------------------------
/dist/server/config/environment/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var path = require('path');
4 | var _ = require('lodash');
5 |
6 | function requiredProcessEnv(name) {
7 | if(!process.env[name]) {
8 | throw new Error('You must set the ' + name + ' environment variable');
9 | }
10 | return process.env[name];
11 | }
12 |
13 | // All configurations will extend these options
14 | // ============================================
15 | var all = {
16 | env: process.env.NODE_ENV,
17 |
18 | // Root path of server
19 | root: path.normalize(__dirname + '/../../..'),
20 |
21 | // Server port
22 | port: process.env.PORT || 9000,
23 |
24 | // Should we populate the DB with sample data?
25 | seedDB: false,
26 |
27 | // Secret for session, you will want to change this and make it an environment variable
28 | secrets: {
29 | session: 'meet-me-in-the-middle-secret'
30 | },
31 |
32 | // List of user roles
33 | userRoles: ['guest', 'user', 'admin'],
34 |
35 | // MongoDB connection options
36 | mongo: {
37 | options: {
38 | db: {
39 | safe: true
40 | }
41 | }
42 | },
43 |
44 | facebook: {
45 | clientID: process.env.FACEBOOK_ID || 'id',
46 | clientSecret: process.env.FACEBOOK_SECRET || 'secret',
47 | callbackURL: (process.env.DOMAIN || '') + '/auth/facebook/callback'
48 | },
49 |
50 | twitter: {
51 | clientID: process.env.TWITTER_ID || 'id',
52 | clientSecret: process.env.TWITTER_SECRET || 'secret',
53 | callbackURL: (process.env.DOMAIN || '') + '/auth/twitter/callback'
54 | },
55 |
56 | google: {
57 | clientID: process.env.GOOGLE_ID || 'id',
58 | clientSecret: process.env.GOOGLE_SECRET || 'secret',
59 | callbackURL: (process.env.DOMAIN || '') + '/auth/google/callback'
60 | }
61 | };
62 |
63 | // Export the config object based on the NODE_ENV
64 | // ==============================================
65 | module.exports = _.merge(
66 | all,
67 | require('./' + process.env.NODE_ENV + '.js') || {});
--------------------------------------------------------------------------------
/server/config/socketio.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Socket.io configuration
3 | */
4 |
5 | 'use strict';
6 |
7 | var config = require('./environment');
8 |
9 | // When the user disconnects.. perform this
10 | function onDisconnect(socket) {
11 | }
12 |
13 | // When the user connects.. perform this
14 | function onConnect(socket) {
15 | // When the client emits 'info', this listens and executes
16 | socket.on('info', function (data) {
17 | console.info('[%s] %s', socket.address, JSON.stringify(data, null, 2));
18 | });
19 |
20 | // Insert sockets below
21 | require('../api/thing/thing.socket').register(socket);
22 | }
23 |
24 | module.exports = function (socketio) {
25 | // socket.io (v1.x.x) is powered by debug.
26 | // In order to see all the debug output, set DEBUG (in server/config/local.env.js) to including the desired scope.
27 | //
28 | // ex: DEBUG: "http*,socket.io:socket"
29 |
30 | // We can authenticate socket.io users and access their token through socket.handshake.decoded_token
31 | //
32 | // 1. You will need to send the token in `client/components/socket/socket.service.js`
33 | //
34 | // 2. Require authentication here:
35 | // socketio.use(require('socketio-jwt').authorize({
36 | // secret: config.secrets.session,
37 | // handshake: true
38 | // }));
39 |
40 | //Ko: all server-side socket.io logics go here.
41 | socketio.on('connection', function (socket) {
42 | socket.address = socket.handshake.address !== null ?
43 | socket.handshake.address.address + ':' + socket.handshake.address.port :
44 | process.env.DOMAIN;
45 |
46 | socket.connectedAt = new Date();
47 |
48 | // Call onDisconnect.
49 | socket.on('disconnect', function () {
50 | onDisconnect(socket);
51 | console.info('[%s] DISCONNECTED', socket.address);
52 | });
53 |
54 | // Call onConnect.
55 | onConnect(socket);
56 | console.info('[%s] CONNECTED', socket.address);
57 |
58 | //Ko: call on place pin
59 | socket.on('test', function(){
60 | console.log("testtest");
61 | })
62 | });
63 |
64 | };
--------------------------------------------------------------------------------
/server/api/thing/thing.controller.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Using Rails-like standard naming convention for endpoints.
3 | * GET /things -> index
4 | * POST /things -> create
5 | * GET /things/:id -> show
6 | * PUT /things/:id -> update
7 | * DELETE /things/:id -> destroy
8 | */
9 |
10 | 'use strict';
11 |
12 | var _ = require('lodash');
13 | var Thing = require('./thing.model');
14 |
15 | // Get list of things
16 | exports.index = function(req, res) {
17 | Thing.find(function (err, things) {
18 | if(err) { return handleError(res, err); }
19 | return res.json(200, things);
20 | });
21 | };
22 |
23 | // Get a single thing
24 | exports.show = function(req, res) {
25 | Thing.findById(req.params.id, function (err, thing) {
26 | if(err) { return handleError(res, err); }
27 | if(!thing) { return res.send(404); }
28 | return res.json(thing);
29 | });
30 | };
31 |
32 | // Creates a new thing in the DB.
33 | exports.create = function(req, res) {
34 | Thing.create(req.body, function(err, thing) {
35 | if(err) { return handleError(res, err); }
36 | return res.json(201, thing);
37 | });
38 | };
39 |
40 | // Updates an existing thing in the DB.
41 | exports.update = function(req, res) {
42 | if(req.body._id) { delete req.body._id; }
43 | Thing.findById(req.params.id, function (err, thing) {
44 | if (err) { return handleError(res, err); }
45 | if(!thing) { return res.send(404); }
46 | var updated = _.merge(thing, req.body);
47 | updated.save(function (err) {
48 | if (err) { return handleError(res, err); }
49 | return res.json(200, thing);
50 | });
51 | });
52 | };
53 |
54 | // Deletes a thing from the DB.
55 | exports.destroy = function(req, res) {
56 | Thing.findById(req.params.id, function (err, thing) {
57 | if(err) { return handleError(res, err); }
58 | if(!thing) { return res.send(404); }
59 | thing.remove(function(err) {
60 | if(err) { return handleError(res, err); }
61 | return res.send(204);
62 | });
63 | });
64 | };
65 |
66 | function handleError(res, err) {
67 | return res.send(500, err);
68 | }
--------------------------------------------------------------------------------
/dist/server/api/thing/thing.controller.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Using Rails-like standard naming convention for endpoints.
3 | * GET /things -> index
4 | * POST /things -> create
5 | * GET /things/:id -> show
6 | * PUT /things/:id -> update
7 | * DELETE /things/:id -> destroy
8 | */
9 |
10 | 'use strict';
11 |
12 | var _ = require('lodash');
13 | var Thing = require('./thing.model');
14 |
15 | // Get list of things
16 | exports.index = function(req, res) {
17 | Thing.find(function (err, things) {
18 | if(err) { return handleError(res, err); }
19 | return res.json(200, things);
20 | });
21 | };
22 |
23 | // Get a single thing
24 | exports.show = function(req, res) {
25 | Thing.findById(req.params.id, function (err, thing) {
26 | if(err) { return handleError(res, err); }
27 | if(!thing) { return res.send(404); }
28 | return res.json(thing);
29 | });
30 | };
31 |
32 | // Creates a new thing in the DB.
33 | exports.create = function(req, res) {
34 | Thing.create(req.body, function(err, thing) {
35 | if(err) { return handleError(res, err); }
36 | return res.json(201, thing);
37 | });
38 | };
39 |
40 | // Updates an existing thing in the DB.
41 | exports.update = function(req, res) {
42 | if(req.body._id) { delete req.body._id; }
43 | Thing.findById(req.params.id, function (err, thing) {
44 | if (err) { return handleError(res, err); }
45 | if(!thing) { return res.send(404); }
46 | var updated = _.merge(thing, req.body);
47 | updated.save(function (err) {
48 | if (err) { return handleError(res, err); }
49 | return res.json(200, thing);
50 | });
51 | });
52 | };
53 |
54 | // Deletes a thing from the DB.
55 | exports.destroy = function(req, res) {
56 | Thing.findById(req.params.id, function (err, thing) {
57 | if(err) { return handleError(res, err); }
58 | if(!thing) { return res.send(404); }
59 | thing.remove(function(err) {
60 | if(err) { return handleError(res, err); }
61 | return res.send(204);
62 | });
63 | });
64 | };
65 |
66 | function handleError(res, err) {
67 | return res.send(500, err);
68 | }
--------------------------------------------------------------------------------
/server/config/express.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Express configuration
3 | */
4 |
5 | 'use strict';
6 |
7 | var express = require('express');
8 | var favicon = require('serve-favicon');
9 | var morgan = require('morgan');
10 | var compression = require('compression');
11 | var bodyParser = require('body-parser');
12 | var methodOverride = require('method-override');
13 | var cookieParser = require('cookie-parser');
14 | var errorHandler = require('errorhandler');
15 | var path = require('path');
16 | var config = require('./environment');
17 | var passport = require('passport');
18 | var session = require('express-session');
19 | var mongoStore = require('connect-mongo')(session);
20 | var mongoose = require('mongoose');
21 |
22 | module.exports = function(app) {
23 | var env = app.get('env');
24 |
25 | app.set('views', config.root + '/server/views');
26 | app.engine('html', require('ejs').renderFile);
27 | app.set('view engine', 'html');
28 | app.use(compression());
29 | app.use(bodyParser.urlencoded({ extended: false }));
30 | app.use(bodyParser.json());
31 | app.use(methodOverride());
32 | app.use(cookieParser());
33 | app.use(passport.initialize());
34 |
35 | // Persist sessions with mongoStore
36 | // We need to enable sessions for passport twitter because its an oauth 1.0 strategy
37 | app.use(session({
38 | secret: config.secrets.session,
39 | resave: true,
40 | saveUninitialized: true,
41 | store: new mongoStore({ mongoose_connection: mongoose.connection })
42 | }));
43 |
44 | if ('production' === env) {
45 | app.use(favicon(path.join(config.root, 'public', 'favicon.ico')));
46 | app.use(express.static(path.join(config.root, 'public')));
47 | app.set('appPath', config.root + '/public');
48 | app.use(morgan('dev'));
49 | }
50 |
51 | if ('development' === env || 'test' === env) {
52 | app.use(require('connect-livereload')());
53 | app.use(express.static(path.join(config.root, '.tmp')));
54 | app.use(express.static(path.join(config.root, 'client')));
55 | app.set('appPath', 'client');
56 | app.use(morgan('dev'));
57 | app.use(errorHandler()); // Error handler - has to be last
58 | }
59 | };
--------------------------------------------------------------------------------
/dist/server/config/express.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Express configuration
3 | */
4 |
5 | 'use strict';
6 |
7 | var express = require('express');
8 | var favicon = require('serve-favicon');
9 | var morgan = require('morgan');
10 | var compression = require('compression');
11 | var bodyParser = require('body-parser');
12 | var methodOverride = require('method-override');
13 | var cookieParser = require('cookie-parser');
14 | var errorHandler = require('errorhandler');
15 | var path = require('path');
16 | var config = require('./environment');
17 | var passport = require('passport');
18 | var session = require('express-session');
19 | var mongoStore = require('connect-mongo')(session);
20 | var mongoose = require('mongoose');
21 |
22 | module.exports = function(app) {
23 | var env = app.get('env');
24 |
25 | app.set('views', config.root + '/server/views');
26 | app.engine('html', require('ejs').renderFile);
27 | app.set('view engine', 'html');
28 | app.use(compression());
29 | app.use(bodyParser.urlencoded({ extended: false }));
30 | app.use(bodyParser.json());
31 | app.use(methodOverride());
32 | app.use(cookieParser());
33 | app.use(passport.initialize());
34 |
35 | // Persist sessions with mongoStore
36 | // We need to enable sessions for passport twitter because its an oauth 1.0 strategy
37 | app.use(session({
38 | secret: config.secrets.session,
39 | resave: true,
40 | saveUninitialized: true,
41 | store: new mongoStore({ mongoose_connection: mongoose.connection })
42 | }));
43 |
44 | if ('production' === env) {
45 | app.use(favicon(path.join(config.root, 'public', 'favicon.ico')));
46 | app.use(express.static(path.join(config.root, 'public')));
47 | app.set('appPath', config.root + '/public');
48 | app.use(morgan('dev'));
49 | }
50 |
51 | if ('development' === env || 'test' === env) {
52 | app.use(require('connect-livereload')());
53 | app.use(express.static(path.join(config.root, '.tmp')));
54 | app.use(express.static(path.join(config.root, 'client')));
55 | app.set('appPath', 'client');
56 | app.use(morgan('dev'));
57 | app.use(errorHandler()); // Error handler - has to be last
58 | }
59 | };
--------------------------------------------------------------------------------
/server/app.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Main application file
3 | */
4 |
5 | 'use strict';
6 |
7 | // Set default node environment to development
8 | process.env.NODE_ENV = process.env.NODE_ENV || 'development';
9 |
10 | var express = require('express');
11 | var mongoose = require('mongoose');
12 | var config = require('./config/environment');
13 |
14 | // Connect to database
15 | mongoose.connect(config.mongo.uri, config.mongo.options);
16 |
17 | // Populate DB with sample data
18 | if(config.seedDB) { require('./config/seed'); }
19 |
20 | // Setup server
21 | var app = express();
22 | var server = require('http').createServer(app);
23 | //Ko: Socket is hooked here
24 | // var socketio = require('socket.io')(server, {
25 | // serveClient: (config.env === 'production') ? false : true,
26 | // path: '/socket.io-client'
27 | // });
28 | // //Ko: Server-side socket logic is defined here in ./config/socketio.js
29 | // require('./config/socketio')(socketio);
30 | var socket = require('socket.io');
31 | var io = socket(server);
32 |
33 | var dataCollection = {};
34 | io.on('connection', function(socket){
35 |
36 | // data = {id:c, coors: { latitude: num, longitude: num}}
37 | socket.on('move-pin', function(data){
38 | // If it's new socket.id
39 | dataCollection[socket.id] = data;
40 |
41 | // Sendback all the data
42 | //dataCollection = {socket.id1:{longitude:num, latitude: num, roomNumber: num}, ..., socket.idN:{longitude:num, latitude:num, roomNumber: num}}
43 | io.emit('move-pin', dataCollection)
44 |
45 |
46 | // Testing
47 | console.log('TESTING SOCKET.IO' + socket.id)
48 |
49 | console.dir(dataCollection);
50 |
51 | });
52 |
53 | // Delete the data after disconnecting.
54 | socket.on('disconnect', function(data){
55 | delete dataCollection[socket.id];
56 | io.emit('move-pin', dataCollection);
57 | })
58 | })
59 |
60 |
61 | require('./config/express')(app);
62 | require('./routes')(app);
63 |
64 | // Start server
65 | server.listen(config.port, config.ip, function () {
66 | console.log('Express server listening on %d, in %s mode', config.port, app.get('env'));
67 | });
68 |
69 | // Expose app
70 | exports = module.exports = app;
--------------------------------------------------------------------------------
/client/app/account/login/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Login
7 |
Accounts are reset on server restart from server/config/seed.js. Default account is test@test.com / test
8 |
Admin account is admin@admin.com / admin
9 |
10 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/server/auth/auth.service.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var mongoose = require('mongoose');
4 | var passport = require('passport');
5 | var config = require('../config/environment');
6 | var jwt = require('jsonwebtoken');
7 | var expressJwt = require('express-jwt');
8 | var compose = require('composable-middleware');
9 | var User = require('../api/user/user.model');
10 | var validateJwt = expressJwt({ secret: config.secrets.session });
11 |
12 | /**
13 | * Attaches the user object to the request if authenticated
14 | * Otherwise returns 403
15 | */
16 | function isAuthenticated() {
17 | return compose()
18 | // Validate jwt
19 | .use(function(req, res, next) {
20 | // allow access_token to be passed through query parameter as well
21 | if(req.query && req.query.hasOwnProperty('access_token')) {
22 | req.headers.authorization = 'Bearer ' + req.query.access_token;
23 | }
24 | validateJwt(req, res, next);
25 | })
26 | // Attach user to request
27 | .use(function(req, res, next) {
28 | User.findById(req.user._id, function (err, user) {
29 | if (err) return next(err);
30 | if (!user) return res.send(401);
31 |
32 | req.user = user;
33 | next();
34 | });
35 | });
36 | }
37 |
38 | /**
39 | * Checks if the user role meets the minimum requirements of the route
40 | */
41 | function hasRole(roleRequired) {
42 | if (!roleRequired) throw new Error('Required role needs to be set');
43 |
44 | return compose()
45 | .use(isAuthenticated())
46 | .use(function meetsRequirements(req, res, next) {
47 | if (config.userRoles.indexOf(req.user.role) >= config.userRoles.indexOf(roleRequired)) {
48 | next();
49 | }
50 | else {
51 | res.send(403);
52 | }
53 | });
54 | }
55 |
56 | /**
57 | * Returns a jwt token signed by the app secret
58 | */
59 | function signToken(id) {
60 | return jwt.sign({ _id: id }, config.secrets.session, { expiresInMinutes: 60*5 });
61 | }
62 |
63 | /**
64 | * Set token cookie directly for oAuth strategies
65 | */
66 | function setTokenCookie(req, res) {
67 | if (!req.user) return res.json(404, { message: 'Something went wrong, please try again.'});
68 | var token = signToken(req.user._id, req.user.role);
69 | res.cookie('token', JSON.stringify(token));
70 | res.redirect('/');
71 | }
72 |
73 | exports.isAuthenticated = isAuthenticated;
74 | exports.hasRole = hasRole;
75 | exports.signToken = signToken;
76 | exports.setTokenCookie = setTokenCookie;
--------------------------------------------------------------------------------
/dist/server/auth/auth.service.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var mongoose = require('mongoose');
4 | var passport = require('passport');
5 | var config = require('../config/environment');
6 | var jwt = require('jsonwebtoken');
7 | var expressJwt = require('express-jwt');
8 | var compose = require('composable-middleware');
9 | var User = require('../api/user/user.model');
10 | var validateJwt = expressJwt({ secret: config.secrets.session });
11 |
12 | /**
13 | * Attaches the user object to the request if authenticated
14 | * Otherwise returns 403
15 | */
16 | function isAuthenticated() {
17 | return compose()
18 | // Validate jwt
19 | .use(function(req, res, next) {
20 | // allow access_token to be passed through query parameter as well
21 | if(req.query && req.query.hasOwnProperty('access_token')) {
22 | req.headers.authorization = 'Bearer ' + req.query.access_token;
23 | }
24 | validateJwt(req, res, next);
25 | })
26 | // Attach user to request
27 | .use(function(req, res, next) {
28 | User.findById(req.user._id, function (err, user) {
29 | if (err) return next(err);
30 | if (!user) return res.send(401);
31 |
32 | req.user = user;
33 | next();
34 | });
35 | });
36 | }
37 |
38 | /**
39 | * Checks if the user role meets the minimum requirements of the route
40 | */
41 | function hasRole(roleRequired) {
42 | if (!roleRequired) throw new Error('Required role needs to be set');
43 |
44 | return compose()
45 | .use(isAuthenticated())
46 | .use(function meetsRequirements(req, res, next) {
47 | if (config.userRoles.indexOf(req.user.role) >= config.userRoles.indexOf(roleRequired)) {
48 | next();
49 | }
50 | else {
51 | res.send(403);
52 | }
53 | });
54 | }
55 |
56 | /**
57 | * Returns a jwt token signed by the app secret
58 | */
59 | function signToken(id) {
60 | return jwt.sign({ _id: id }, config.secrets.session, { expiresInMinutes: 60*5 });
61 | }
62 |
63 | /**
64 | * Set token cookie directly for oAuth strategies
65 | */
66 | function setTokenCookie(req, res) {
67 | if (!req.user) return res.json(404, { message: 'Something went wrong, please try again.'});
68 | var token = signToken(req.user._id, req.user.role);
69 | res.cookie('token', JSON.stringify(token));
70 | res.redirect('/');
71 | }
72 |
73 | exports.isAuthenticated = isAuthenticated;
74 | exports.hasRole = hasRole;
75 | exports.signToken = signToken;
76 | exports.setTokenCookie = setTokenCookie;
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // http://karma-runner.github.io/0.10/config/configuration-file.html
3 |
4 | module.exports = function(config) {
5 | config.set({
6 | // base path, that will be used to resolve files and exclude
7 | basePath: '',
8 |
9 | // testing framework to use (jasmine/mocha/qunit/...)
10 | frameworks: ['jasmine'],
11 |
12 | // list of files / patterns to load in the browser
13 | files: [
14 | 'client/bower_components/jquery/dist/jquery.js',
15 | 'client/bower_components/angular/angular.js',
16 | 'client/bower_components/angular-mocks/angular-mocks.js',
17 | 'client/bower_components/angular-resource/angular-resource.js',
18 | 'client/bower_components/angular-cookies/angular-cookies.js',
19 | 'client/bower_components/angular-sanitize/angular-sanitize.js',
20 | 'client/bower_components/angular-route/angular-route.js',
21 | 'client/bower_components/angular-bootstrap/ui-bootstrap-tpls.js',
22 | 'client/bower_components/lodash/dist/lodash.compat.js',
23 | 'client/bower_components/angular-socket-io/socket.js',
24 | 'client/bower_components/angular-ui-router/release/angular-ui-router.js',
25 | 'client/app/app.js',
26 | 'client/app/app.coffee',
27 | 'client/app/**/*.js',
28 | 'client/app/**/*.coffee',
29 | 'client/components/**/*.js',
30 | 'client/components/**/*.coffee',
31 | 'client/app/**/*.jade',
32 | 'client/components/**/*.jade',
33 | 'client/app/**/*.html',
34 | 'client/components/**/*.html'
35 | ],
36 |
37 | preprocessors: {
38 | '**/*.jade': 'ng-jade2js',
39 | '**/*.html': 'html2js',
40 | '**/*.coffee': 'coffee',
41 | },
42 |
43 | ngHtml2JsPreprocessor: {
44 | stripPrefix: 'client/'
45 | },
46 |
47 | ngJade2JsPreprocessor: {
48 | stripPrefix: 'client/'
49 | },
50 |
51 | // list of files / patterns to exclude
52 | exclude: [],
53 |
54 | // web server port
55 | port: 8080,
56 |
57 | // level of logging
58 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
59 | logLevel: config.LOG_INFO,
60 |
61 |
62 | // enable / disable watching file and executing tests whenever any file changes
63 | autoWatch: false,
64 |
65 |
66 | // Start these browsers, currently available:
67 | // - Chrome
68 | // - ChromeCanary
69 | // - Firefox
70 | // - Opera
71 | // - Safari (only Mac)
72 | // - PhantomJS
73 | // - IE (only Windows)
74 | browsers: ['PhantomJS'],
75 |
76 |
77 | // Continuous Integration mode
78 | // if true, it capture browsers, run tests and exit
79 | singleRun: false
80 | });
81 | };
82 |
--------------------------------------------------------------------------------
/client/components/modal/modal.service.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('meetMeInTheMiddleApp')
4 | .factory('Modal', function ($rootScope, $modal) {
5 | /**
6 | * Opens a modal
7 | * @param {Object} scope - an object to be merged with modal's scope
8 | * @param {String} modalClass - (optional) class(es) to be applied to the modal
9 | * @return {Object} - the instance $modal.open() returns
10 | */
11 | function openModal(scope, modalClass) {
12 | var modalScope = $rootScope.$new();
13 | scope = scope || {};
14 | modalClass = modalClass || 'modal-default';
15 |
16 | angular.extend(modalScope, scope);
17 |
18 | return $modal.open({
19 | templateUrl: 'components/modal/modal.html',
20 | windowClass: modalClass,
21 | scope: modalScope
22 | });
23 | }
24 |
25 | // Public API here
26 | return {
27 |
28 | /* Confirmation modals */
29 | confirm: {
30 |
31 | /**
32 | * Create a function to open a delete confirmation modal (ex. ng-click='myModalFn(name, arg1, arg2...)')
33 | * @param {Function} del - callback, ran when delete is confirmed
34 | * @return {Function} - the function to open the modal (ex. myModalFn)
35 | */
36 | delete: function(del) {
37 | del = del || angular.noop;
38 |
39 | /**
40 | * Open a delete confirmation modal
41 | * @param {String} name - name or info to show on modal
42 | * @param {All} - any additional args are passed staight to del callback
43 | */
44 | return function() {
45 | var args = Array.prototype.slice.call(arguments),
46 | name = args.shift(),
47 | deleteModal;
48 |
49 | deleteModal = openModal({
50 | modal: {
51 | dismissable: true,
52 | title: 'Confirm Delete',
53 | html: 'Are you sure you want to delete ' + name + ' ?
',
54 | buttons: [{
55 | classes: 'btn-danger',
56 | text: 'Delete',
57 | click: function(e) {
58 | deleteModal.close(e);
59 | }
60 | }, {
61 | classes: 'btn-default',
62 | text: 'Cancel',
63 | click: function(e) {
64 | deleteModal.dismiss(e);
65 | }
66 | }]
67 | }
68 | }, 'modal-danger');
69 |
70 | deleteModal.result.then(function(event) {
71 | del.apply(event, args);
72 | });
73 | };
74 | }
75 | }
76 | };
77 | });
78 |
--------------------------------------------------------------------------------
/server/api/user/user.controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var User = require('./user.model');
4 | var passport = require('passport');
5 | var config = require('../../config/environment');
6 | var jwt = require('jsonwebtoken');
7 |
8 | var validationError = function(res, err) {
9 | return res.json(422, err);
10 | };
11 |
12 | /**
13 | * Get list of users
14 | * restriction: 'admin'
15 | */
16 | exports.index = function(req, res) {
17 | User.find({}, '-salt -hashedPassword', function (err, users) {
18 | if(err) return res.send(500, err);
19 | res.json(200, users);
20 | });
21 | };
22 |
23 | /**
24 | * Creates a new user
25 | */
26 | exports.create = function (req, res, next) {
27 | var newUser = new User(req.body);
28 | newUser.provider = 'local';
29 | newUser.role = 'user';
30 | newUser.save(function(err, user) {
31 | if (err) return validationError(res, err);
32 | var token = jwt.sign({_id: user._id }, config.secrets.session, { expiresInMinutes: 60*5 });
33 | res.json({ token: token });
34 | });
35 | };
36 |
37 | /**
38 | * Get a single user
39 | */
40 | exports.show = function (req, res, next) {
41 | var userId = req.params.id;
42 |
43 | User.findById(userId, function (err, user) {
44 | if (err) return next(err);
45 | if (!user) return res.send(401);
46 | res.json(user.profile);
47 | });
48 | };
49 |
50 | /**
51 | * Deletes a user
52 | * restriction: 'admin'
53 | */
54 | exports.destroy = function(req, res) {
55 | User.findByIdAndRemove(req.params.id, function(err, user) {
56 | if(err) return res.send(500, err);
57 | return res.send(204);
58 | });
59 | };
60 |
61 | /**
62 | * Change a users password
63 | */
64 | exports.changePassword = function(req, res, next) {
65 | var userId = req.user._id;
66 | var oldPass = String(req.body.oldPassword);
67 | var newPass = String(req.body.newPassword);
68 |
69 | User.findById(userId, function (err, user) {
70 | if(user.authenticate(oldPass)) {
71 | user.password = newPass;
72 | user.save(function(err) {
73 | if (err) return validationError(res, err);
74 | res.send(200);
75 | });
76 | } else {
77 | res.send(403);
78 | }
79 | });
80 | };
81 |
82 | /**
83 | * Get my info
84 | */
85 | exports.me = function(req, res, next) {
86 | var userId = req.user._id;
87 | User.findOne({
88 | _id: userId
89 | }, '-salt -hashedPassword', function(err, user) { // don't ever give out the password or salt
90 | if (err) return next(err);
91 | if (!user) return res.json(401);
92 | res.json(user);
93 | });
94 | };
95 |
96 | /**
97 | * Authentication callback
98 | */
99 | exports.authCallback = function(req, res, next) {
100 | res.redirect('/');
101 | };
102 |
--------------------------------------------------------------------------------
/dist/server/api/user/user.controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var User = require('./user.model');
4 | var passport = require('passport');
5 | var config = require('../../config/environment');
6 | var jwt = require('jsonwebtoken');
7 |
8 | var validationError = function(res, err) {
9 | return res.json(422, err);
10 | };
11 |
12 | /**
13 | * Get list of users
14 | * restriction: 'admin'
15 | */
16 | exports.index = function(req, res) {
17 | User.find({}, '-salt -hashedPassword', function (err, users) {
18 | if(err) return res.send(500, err);
19 | res.json(200, users);
20 | });
21 | };
22 |
23 | /**
24 | * Creates a new user
25 | */
26 | exports.create = function (req, res, next) {
27 | var newUser = new User(req.body);
28 | newUser.provider = 'local';
29 | newUser.role = 'user';
30 | newUser.save(function(err, user) {
31 | if (err) return validationError(res, err);
32 | var token = jwt.sign({_id: user._id }, config.secrets.session, { expiresInMinutes: 60*5 });
33 | res.json({ token: token });
34 | });
35 | };
36 |
37 | /**
38 | * Get a single user
39 | */
40 | exports.show = function (req, res, next) {
41 | var userId = req.params.id;
42 |
43 | User.findById(userId, function (err, user) {
44 | if (err) return next(err);
45 | if (!user) return res.send(401);
46 | res.json(user.profile);
47 | });
48 | };
49 |
50 | /**
51 | * Deletes a user
52 | * restriction: 'admin'
53 | */
54 | exports.destroy = function(req, res) {
55 | User.findByIdAndRemove(req.params.id, function(err, user) {
56 | if(err) return res.send(500, err);
57 | return res.send(204);
58 | });
59 | };
60 |
61 | /**
62 | * Change a users password
63 | */
64 | exports.changePassword = function(req, res, next) {
65 | var userId = req.user._id;
66 | var oldPass = String(req.body.oldPassword);
67 | var newPass = String(req.body.newPassword);
68 |
69 | User.findById(userId, function (err, user) {
70 | if(user.authenticate(oldPass)) {
71 | user.password = newPass;
72 | user.save(function(err) {
73 | if (err) return validationError(res, err);
74 | res.send(200);
75 | });
76 | } else {
77 | res.send(403);
78 | }
79 | });
80 | };
81 |
82 | /**
83 | * Get my info
84 | */
85 | exports.me = function(req, res, next) {
86 | var userId = req.user._id;
87 | User.findOne({
88 | _id: userId
89 | }, '-salt -hashedPassword', function(err, user) { // don't ever give out the password or salt
90 | if (err) return next(err);
91 | if (!user) return res.json(401);
92 | res.json(user);
93 | });
94 | };
95 |
96 | /**
97 | * Authentication callback
98 | */
99 | exports.authCallback = function(req, res, next) {
100 | res.redirect('/');
101 | };
102 |
--------------------------------------------------------------------------------
/client/app/landing/js/countUp.min.js:
--------------------------------------------------------------------------------
1 | //function countUp(a,b,c,d,e,f){"use strict";for(var g=0,h=["webkit","moz","ms","o"],i=0;ithis.endVal?!0:!1,this.startTime=null,this.timestamp=null,this.remaining=null,this.frameVal=this.startVal,this.rAF=null,this.decimals=Math.max(0,d||0),this.dec=Math.pow(10,this.decimals),this.duration=1e3*e||2e3,this.version=function(){return"1.3.2"},this.printValue=function(a){var b=isNaN(a)?"--":j.formatNumber(a);"INPUT"==j.d.tagName?this.d.value=b:"text"==j.d.tagName?this.d.textContent=b:this.d.innerHTML=b},this.easeOutExpo=function(a,b,c,d){return 1024*c*(-Math.pow(2,-10*a/d)+1)/1023+b},this.count=function(a){null===j.startTime&&(j.startTime=a),j.timestamp=a;var b=a-j.startTime;if(j.remaining=j.duration-b,j.options.useEasing)if(j.countDown){var c=j.easeOutExpo(b,0,j.startVal-j.endVal,j.duration);j.frameVal=j.startVal-c}else j.frameVal=j.easeOutExpo(b,j.startVal,j.endVal-j.startVal,j.duration);else if(j.countDown){var c=(j.startVal-j.endVal)*(b/j.duration);j.frameVal=j.startVal-c}else j.frameVal=j.startVal+(j.endVal-j.startVal)*(b/j.duration);j.frameVal=j.countDown?j.frameValj.endVal?j.endVal:j.frameVal,j.frameVal=Math.round(j.frameVal*j.dec)/j.dec,j.printValue(j.frameVal),b1?j.options.decimal+b[1]:"",e=/(\d+)(\d{3})/,j.options.useGrouping)for(;e.test(c);)c=c.replace(e,"$1"+j.options.separator+"$2");return j.options.prefix+c+d+j.options.suffix},j.printValue(j.startVal)}
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "meet-me-in-the-middle",
3 | "version": "0.0.0",
4 | "main": "server/app.js",
5 | "dependencies": {
6 | "angular-google-maps": "^2.0.19",
7 | "body-parser": "~1.5.0",
8 | "composable-middleware": "^0.3.0",
9 | "compression": "~1.0.1",
10 | "connect-mongo": "^0.4.1",
11 | "cookie-parser": "~1.0.1",
12 | "ejs": "~0.8.4",
13 | "errorhandler": "~1.0.0",
14 | "express": "~4.0.0",
15 | "express-jwt": "^0.1.3",
16 | "express-session": "~1.0.2",
17 | "jsonwebtoken": "^0.3.0",
18 | "lodash": "~2.4.1",
19 | "method-override": "~1.0.0",
20 | "mongoose": "~3.8.8",
21 | "morgan": "~1.0.0",
22 | "passport": "~0.2.0",
23 | "passport-facebook": "latest",
24 | "passport-google-oauth": "latest",
25 | "passport-local": "~0.1.6",
26 | "passport-twitter": "latest",
27 | "serve-favicon": "~2.0.1",
28 | "socket.io": "^1.3.5"
29 | },
30 | "devDependencies": {
31 | "grunt": "~0.4.4",
32 | "grunt-autoprefixer": "~0.7.2",
33 | "grunt-wiredep": "~1.8.0",
34 | "grunt-concurrent": "~0.5.0",
35 | "grunt-contrib-clean": "~0.5.0",
36 | "grunt-contrib-concat": "~0.4.0",
37 | "grunt-contrib-copy": "~0.5.0",
38 | "grunt-contrib-cssmin": "~0.9.0",
39 | "grunt-contrib-htmlmin": "~0.2.0",
40 | "grunt-contrib-imagemin": "~0.7.1",
41 | "grunt-contrib-jshint": "~0.10.0",
42 | "grunt-contrib-uglify": "~0.4.0",
43 | "grunt-contrib-watch": "~0.6.1",
44 | "grunt-google-cdn": "~0.4.0",
45 | "grunt-newer": "~0.7.0",
46 | "grunt-ng-annotate": "^0.2.3",
47 | "grunt-rev": "~0.1.0",
48 | "grunt-svgmin": "~0.4.0",
49 | "grunt-usemin": "~2.1.1",
50 | "grunt-env": "~0.4.1",
51 | "grunt-node-inspector": "~0.1.5",
52 | "grunt-nodemon": "~0.2.0",
53 | "grunt-angular-templates": "^0.5.4",
54 | "grunt-dom-munger": "^3.4.0",
55 | "grunt-protractor-runner": "^1.1.0",
56 | "grunt-asset-injector": "^0.1.0",
57 | "grunt-karma": "~0.8.2",
58 | "grunt-build-control": "DaftMonk/grunt-build-control",
59 | "grunt-mocha-test": "~0.10.2",
60 | "jit-grunt": "^0.5.0",
61 | "time-grunt": "~0.3.1",
62 | "grunt-express-server": "~0.4.17",
63 | "grunt-open": "~0.2.3",
64 | "open": "~0.0.4",
65 | "jshint-stylish": "~0.1.5",
66 | "connect-livereload": "~0.4.0",
67 | "karma-ng-scenario": "~0.1.0",
68 | "karma-firefox-launcher": "~0.1.3",
69 | "karma-script-launcher": "~0.1.0",
70 | "karma-html2js-preprocessor": "~0.1.0",
71 | "karma-ng-jade2js-preprocessor": "^0.1.2",
72 | "karma-jasmine": "~0.1.5",
73 | "karma-chrome-launcher": "~0.1.3",
74 | "requirejs": "~2.1.11",
75 | "karma-requirejs": "~0.2.1",
76 | "karma-coffee-preprocessor": "~0.2.1",
77 | "karma-jade-preprocessor": "0.0.11",
78 | "karma-phantomjs-launcher": "~0.1.4",
79 | "karma": "~0.12.9",
80 | "karma-ng-html2js-preprocessor": "~0.1.0",
81 | "supertest": "~0.11.0",
82 | "should": "~3.3.1"
83 | },
84 | "engines": {
85 | "node": ">=0.10.0"
86 | },
87 | "scripts": {
88 | "start": "node server/app.js",
89 | "test": "grunt test",
90 | "update-webdriver": "node node_modules/grunt-protractor-runner/node_modules/protractor/bin/webdriver-manager update"
91 | },
92 | "private": true
93 | }
94 |
--------------------------------------------------------------------------------
/dist/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "meet-me-in-the-middle",
3 | "version": "0.0.0",
4 | "main": "server/app.js",
5 | "dependencies": {
6 | "express": "~4.0.0",
7 | "morgan": "~1.0.0",
8 | "body-parser": "~1.5.0",
9 | "method-override": "~1.0.0",
10 | "serve-favicon": "~2.0.1",
11 | "cookie-parser": "~1.0.1",
12 | "express-session": "~1.0.2",
13 | "errorhandler": "~1.0.0",
14 | "compression": "~1.0.1",
15 | "lodash": "~2.4.1",
16 | "ejs": "~0.8.4",
17 | "mongoose": "~3.8.8",
18 | "jsonwebtoken": "^0.3.0",
19 | "express-jwt": "^0.1.3",
20 | "passport": "~0.2.0",
21 | "passport-local": "~0.1.6",
22 | "passport-facebook": "latest",
23 | "passport-twitter": "latest",
24 | "passport-google-oauth": "latest",
25 | "composable-middleware": "^0.3.0",
26 | "connect-mongo": "^0.4.1",
27 | "socket.io": "^1.0.6",
28 | "socket.io-client": "^1.0.6",
29 | "socketio-jwt": "^2.0.2"
30 | },
31 | "devDependencies": {
32 | "grunt": "~0.4.4",
33 | "grunt-autoprefixer": "~0.7.2",
34 | "grunt-wiredep": "~1.8.0",
35 | "grunt-concurrent": "~0.5.0",
36 | "grunt-contrib-clean": "~0.5.0",
37 | "grunt-contrib-concat": "~0.4.0",
38 | "grunt-contrib-copy": "~0.5.0",
39 | "grunt-contrib-cssmin": "~0.9.0",
40 | "grunt-contrib-htmlmin": "~0.2.0",
41 | "grunt-contrib-imagemin": "~0.7.1",
42 | "grunt-contrib-jshint": "~0.10.0",
43 | "grunt-contrib-uglify": "~0.4.0",
44 | "grunt-contrib-watch": "~0.6.1",
45 | "grunt-google-cdn": "~0.4.0",
46 | "grunt-newer": "~0.7.0",
47 | "grunt-ng-annotate": "^0.2.3",
48 | "grunt-rev": "~0.1.0",
49 | "grunt-svgmin": "~0.4.0",
50 | "grunt-usemin": "~2.1.1",
51 | "grunt-env": "~0.4.1",
52 | "grunt-node-inspector": "~0.1.5",
53 | "grunt-nodemon": "~0.2.0",
54 | "grunt-angular-templates": "^0.5.4",
55 | "grunt-dom-munger": "^3.4.0",
56 | "grunt-protractor-runner": "^1.1.0",
57 | "grunt-asset-injector": "^0.1.0",
58 | "grunt-karma": "~0.8.2",
59 | "grunt-build-control": "DaftMonk/grunt-build-control",
60 | "grunt-mocha-test": "~0.10.2",
61 | "jit-grunt": "^0.5.0",
62 | "time-grunt": "~0.3.1",
63 | "grunt-express-server": "~0.4.17",
64 | "grunt-open": "~0.2.3",
65 | "open": "~0.0.4",
66 | "jshint-stylish": "~0.1.5",
67 | "connect-livereload": "~0.4.0",
68 | "karma-ng-scenario": "~0.1.0",
69 | "karma-firefox-launcher": "~0.1.3",
70 | "karma-script-launcher": "~0.1.0",
71 | "karma-html2js-preprocessor": "~0.1.0",
72 | "karma-ng-jade2js-preprocessor": "^0.1.2",
73 | "karma-jasmine": "~0.1.5",
74 | "karma-chrome-launcher": "~0.1.3",
75 | "requirejs": "~2.1.11",
76 | "karma-requirejs": "~0.2.1",
77 | "karma-coffee-preprocessor": "~0.2.1",
78 | "karma-jade-preprocessor": "0.0.11",
79 | "karma-phantomjs-launcher": "~0.1.4",
80 | "karma": "~0.12.9",
81 | "karma-ng-html2js-preprocessor": "~0.1.0",
82 | "supertest": "~0.11.0",
83 | "should": "~3.3.1"
84 | },
85 | "engines": {
86 | "node": ">=0.10.0"
87 | },
88 | "scripts": {
89 | "start": "node server/app.js",
90 | "test": "grunt test",
91 | "update-webdriver": "node node_modules/grunt-protractor-runner/node_modules/protractor/bin/webdriver-manager update"
92 | },
93 | "private": true
94 | }
95 |
--------------------------------------------------------------------------------
/client/app/account/signup/signup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Sign up
7 |
8 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/_PRESS-RELEASE.md:
--------------------------------------------------------------------------------
1 | # Project Name #
2 |
3 |
18 |
19 | ## Heading ##
20 | > Name the product in a way the reader (i.e. your target customers) will understand.
21 |
22 | ## Sub-Heading ##
23 | > Describe who the market for the product is and what benefit they get. One sentence only underneath the title.
24 |
25 | ## Summary ##
26 | > Give a summary of the product and the benefit. Assume the reader will not read anything else so make this paragraph good.
27 |
28 | ## Problem ##
29 | > Describe the problem your product solves.
30 |
31 | ## Solution ##
32 | > Describe how your product elegantly solves the problem.
33 |
34 | ## Quote from You ##
35 | > A quote from a spokesperson in your company.
36 |
37 | ## How to Get Started ##
38 | > Describe how easy it is to get started.
39 |
40 | ## Customer Quote ##
41 | > Provide a quote from a hypothetical customer that describes how they experienced the benefit.
42 |
43 | ## Closing and Call to Action ##
44 | > Wrap it up and give pointers where the reader should go next.
45 |
--------------------------------------------------------------------------------
/server/api/user/user.model.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var mongoose = require('mongoose');
4 | var Schema = mongoose.Schema;
5 | var crypto = require('crypto');
6 | var authTypes = ['github', 'twitter', 'facebook', 'google'];
7 |
8 | var UserSchema = new Schema({
9 | name: String,
10 | email: { type: String, lowercase: true },
11 | role: {
12 | type: String,
13 | default: 'user'
14 | },
15 | hashedPassword: String,
16 | provider: String,
17 | salt: String,
18 | facebook: {},
19 | twitter: {},
20 | google: {},
21 | github: {}
22 | });
23 |
24 | /**
25 | * Virtuals
26 | */
27 | UserSchema
28 | .virtual('password')
29 | .set(function(password) {
30 | this._password = password;
31 | this.salt = this.makeSalt();
32 | this.hashedPassword = this.encryptPassword(password);
33 | })
34 | .get(function() {
35 | return this._password;
36 | });
37 |
38 | // Public profile information
39 | UserSchema
40 | .virtual('profile')
41 | .get(function() {
42 | return {
43 | 'name': this.name,
44 | 'role': this.role
45 | };
46 | });
47 |
48 | // Non-sensitive info we'll be putting in the token
49 | UserSchema
50 | .virtual('token')
51 | .get(function() {
52 | return {
53 | '_id': this._id,
54 | 'role': this.role
55 | };
56 | });
57 |
58 | /**
59 | * Validations
60 | */
61 |
62 | // Validate empty email
63 | UserSchema
64 | .path('email')
65 | .validate(function(email) {
66 | if (authTypes.indexOf(this.provider) !== -1) return true;
67 | return email.length;
68 | }, 'Email cannot be blank');
69 |
70 | // Validate empty password
71 | UserSchema
72 | .path('hashedPassword')
73 | .validate(function(hashedPassword) {
74 | if (authTypes.indexOf(this.provider) !== -1) return true;
75 | return hashedPassword.length;
76 | }, 'Password cannot be blank');
77 |
78 | // Validate email is not taken
79 | UserSchema
80 | .path('email')
81 | .validate(function(value, respond) {
82 | var self = this;
83 | this.constructor.findOne({email: value}, function(err, user) {
84 | if(err) throw err;
85 | if(user) {
86 | if(self.id === user.id) return respond(true);
87 | return respond(false);
88 | }
89 | respond(true);
90 | });
91 | }, 'The specified email address is already in use.');
92 |
93 | var validatePresenceOf = function(value) {
94 | return value && value.length;
95 | };
96 |
97 | /**
98 | * Pre-save hook
99 | */
100 | UserSchema
101 | .pre('save', function(next) {
102 | if (!this.isNew) return next();
103 |
104 | if (!validatePresenceOf(this.hashedPassword) && authTypes.indexOf(this.provider) === -1)
105 | next(new Error('Invalid password'));
106 | else
107 | next();
108 | });
109 |
110 | /**
111 | * Methods
112 | */
113 | UserSchema.methods = {
114 | /**
115 | * Authenticate - check if the passwords are the same
116 | *
117 | * @param {String} plainText
118 | * @return {Boolean}
119 | * @api public
120 | */
121 | authenticate: function(plainText) {
122 | return this.encryptPassword(plainText) === this.hashedPassword;
123 | },
124 |
125 | /**
126 | * Make salt
127 | *
128 | * @return {String}
129 | * @api public
130 | */
131 | makeSalt: function() {
132 | return crypto.randomBytes(16).toString('base64');
133 | },
134 |
135 | /**
136 | * Encrypt password
137 | *
138 | * @param {String} password
139 | * @return {String}
140 | * @api public
141 | */
142 | encryptPassword: function(password) {
143 | if (!password || !this.salt) return '';
144 | var salt = new Buffer(this.salt, 'base64');
145 | return crypto.pbkdf2Sync(password, salt, 10000, 64).toString('base64');
146 | }
147 | };
148 |
149 | module.exports = mongoose.model('User', UserSchema);
150 |
--------------------------------------------------------------------------------
/dist/server/api/user/user.model.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var mongoose = require('mongoose');
4 | var Schema = mongoose.Schema;
5 | var crypto = require('crypto');
6 | var authTypes = ['github', 'twitter', 'facebook', 'google'];
7 |
8 | var UserSchema = new Schema({
9 | name: String,
10 | email: { type: String, lowercase: true },
11 | role: {
12 | type: String,
13 | default: 'user'
14 | },
15 | hashedPassword: String,
16 | provider: String,
17 | salt: String,
18 | facebook: {},
19 | twitter: {},
20 | google: {},
21 | github: {}
22 | });
23 |
24 | /**
25 | * Virtuals
26 | */
27 | UserSchema
28 | .virtual('password')
29 | .set(function(password) {
30 | this._password = password;
31 | this.salt = this.makeSalt();
32 | this.hashedPassword = this.encryptPassword(password);
33 | })
34 | .get(function() {
35 | return this._password;
36 | });
37 |
38 | // Public profile information
39 | UserSchema
40 | .virtual('profile')
41 | .get(function() {
42 | return {
43 | 'name': this.name,
44 | 'role': this.role
45 | };
46 | });
47 |
48 | // Non-sensitive info we'll be putting in the token
49 | UserSchema
50 | .virtual('token')
51 | .get(function() {
52 | return {
53 | '_id': this._id,
54 | 'role': this.role
55 | };
56 | });
57 |
58 | /**
59 | * Validations
60 | */
61 |
62 | // Validate empty email
63 | UserSchema
64 | .path('email')
65 | .validate(function(email) {
66 | if (authTypes.indexOf(this.provider) !== -1) return true;
67 | return email.length;
68 | }, 'Email cannot be blank');
69 |
70 | // Validate empty password
71 | UserSchema
72 | .path('hashedPassword')
73 | .validate(function(hashedPassword) {
74 | if (authTypes.indexOf(this.provider) !== -1) return true;
75 | return hashedPassword.length;
76 | }, 'Password cannot be blank');
77 |
78 | // Validate email is not taken
79 | UserSchema
80 | .path('email')
81 | .validate(function(value, respond) {
82 | var self = this;
83 | this.constructor.findOne({email: value}, function(err, user) {
84 | if(err) throw err;
85 | if(user) {
86 | if(self.id === user.id) return respond(true);
87 | return respond(false);
88 | }
89 | respond(true);
90 | });
91 | }, 'The specified email address is already in use.');
92 |
93 | var validatePresenceOf = function(value) {
94 | return value && value.length;
95 | };
96 |
97 | /**
98 | * Pre-save hook
99 | */
100 | UserSchema
101 | .pre('save', function(next) {
102 | if (!this.isNew) return next();
103 |
104 | if (!validatePresenceOf(this.hashedPassword) && authTypes.indexOf(this.provider) === -1)
105 | next(new Error('Invalid password'));
106 | else
107 | next();
108 | });
109 |
110 | /**
111 | * Methods
112 | */
113 | UserSchema.methods = {
114 | /**
115 | * Authenticate - check if the passwords are the same
116 | *
117 | * @param {String} plainText
118 | * @return {Boolean}
119 | * @api public
120 | */
121 | authenticate: function(plainText) {
122 | return this.encryptPassword(plainText) === this.hashedPassword;
123 | },
124 |
125 | /**
126 | * Make salt
127 | *
128 | * @return {String}
129 | * @api public
130 | */
131 | makeSalt: function() {
132 | return crypto.randomBytes(16).toString('base64');
133 | },
134 |
135 | /**
136 | * Encrypt password
137 | *
138 | * @param {String} password
139 | * @return {String}
140 | * @api public
141 | */
142 | encryptPassword: function(password) {
143 | if (!password || !this.salt) return '';
144 | var salt = new Buffer(this.salt, 'base64');
145 | return crypto.pbkdf2Sync(password, salt, 10000, 64).toString('base64');
146 | }
147 | };
148 |
149 | module.exports = mongoose.model('User', UserSchema);
150 |
--------------------------------------------------------------------------------
/client/favicon.ico:
--------------------------------------------------------------------------------
1 | � ( @ -2Op"=p�Jt��Jt��b���������������������������������������������������b���Jt��Jt��"=p�Op-2 O`O�O�O�O�O�O�O�$\�Jt��������������v���v���������������Jt��$\�O�O�O�O�O�O�O�O` O�O�O�O�O�O�O�O�O�O� ;n�s���>���>���>���>���s��� ;n�O�O�O�O�O�O�O�O�O�O� O`O�O�O�O�O�O�O�O�O�O�$\�]���^n��^n��]���$\�O�O�O�O�O�O�O�O�O�O�O` O�O�O�O�O�O�O�O�O�O�O�n�* ��* ��n�O�O�O�O�O�O�O�O�O�O�O� O�O�O�O�O�O�O�O�O�O�O�5>Y�5>Y�O�O�O�O�O�O�O�O�O�O�O� -2O�O�O�O�O�O�O�O�O�O�&6e�&6e�O�O�O�O�O�O�O�O�O�O�-2 5r�4���E���$\�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O�$\�E���4���5r� 5r�E���M���M���v���0\��O�O�O�O�O�O�O�$\�$\�O�O�O�O�O�O�O�0\��v���M���M���E���5r� )��p&��p��&��������������b���Jt��Jt��Jt��0\��#i��.r��.r��#i��0\��Jt��Jt��Jt��b���������������&��p��&��)��p 4���&��-���_������������������]���]�������7���p�����������p���7�������]���]�������������������_��-���-���4��� qֈp��p��p����������������������p���7���#i��p�����������p���#i��7���p�����������������������p��&��-���qֈ 8��(p��p��I���v���v���]���7���n���v���p���#i��]���v���v���]���#i��p���v���n���7���]���v���v���I���-���-���8��( ;��`-���M���7���7���7���.r��R��E��R��E��7���7���7���7���E��R��E��R��.r��7���7���7���M���M���;��` ���������������������������z ���������������������������
2 | � ���
3 | �
9�
9�
9�
9�
9�
9�
9�
9�
4 | �n�n�
5 | �
9�
9�
9�
9�
9�
9�
9�
9�
6 | ���� * �x* ��* ��* ��* ��* ��* ��* ��n�&