├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── MILESTONES.md ├── PRESS-RELEASE.md ├── README.md ├── STYLE-GUIDE.md ├── generators ├── app.js ├── appBackbone.js ├── crudApp.js ├── reactActions.js ├── reactComponent.js ├── reactConstants.js ├── reactDispatcher.js └── reactStore.js ├── package.json ├── slushfile.js └── templates ├── app-backbone ├── README.md ├── client │ ├── app │ │ ├── index.html │ │ ├── src │ │ │ ├── app.jsx │ │ │ ├── collections │ │ │ │ └── Todos.js │ │ │ ├── components │ │ │ │ ├── Banner.jsx │ │ │ │ ├── Footer.jsx │ │ │ │ ├── Home.jsx │ │ │ │ ├── Item.jsx │ │ │ │ ├── Login.jsx │ │ │ │ ├── NavBar.jsx │ │ │ │ ├── Signup.jsx │ │ │ │ └── ToDo.jsx │ │ │ ├── models │ │ │ │ └── Todo.js │ │ │ ├── preprocessor.js │ │ │ └── tests │ │ │ │ ├── app-items-test.js │ │ │ │ └── app-test.js │ │ └── styles │ │ │ └── main.css │ └── index.html ├── gulpfile.js ├── package.json └── server │ ├── .jshintrc │ ├── .jshintrc-spec │ ├── api │ ├── thing │ │ ├── index.js │ │ ├── thing.controller.js │ │ ├── thing.model.js │ │ └── thing.spec.js │ └── user │ │ ├── index.js │ │ ├── user.controller.js │ │ ├── user.model.js │ │ └── user.model.spec.js │ ├── app.js │ ├── auth │ ├── auth.service.js │ ├── facebook │ │ ├── index.js │ │ └── passport.js │ ├── google │ │ ├── index.js │ │ └── passport.js │ ├── index.js │ ├── local │ │ ├── index.js │ │ └── passport.js │ └── twitter │ │ ├── index.js │ │ └── passport.js │ ├── components │ └── errors │ │ └── index.js │ ├── config │ ├── environment │ │ ├── development.js │ │ ├── index.js │ │ ├── production.js │ │ └── test.js │ ├── express.js │ ├── local.env.js │ ├── local.env.sample.js │ └── seed.js │ ├── routes.js │ └── views │ └── 404.html ├── app-koa ├── README.md ├── client │ ├── app │ │ ├── index.html │ │ ├── src │ │ │ ├── actions │ │ │ │ └── AppActions.js │ │ │ ├── app.jsx │ │ │ ├── components │ │ │ │ ├── Banner.jsx │ │ │ │ ├── Footer.jsx │ │ │ │ ├── Home.jsx │ │ │ │ ├── Item.jsx │ │ │ │ ├── Login.jsx │ │ │ │ ├── NavBar.jsx │ │ │ │ ├── Signup.jsx │ │ │ │ └── ToDo.jsx │ │ │ ├── constants │ │ │ │ └── AppConstants.js │ │ │ ├── dispatchers │ │ │ │ └── AppDispatcher.js │ │ │ ├── preprocessor.js │ │ │ ├── stores │ │ │ │ └── AppStore.js │ │ │ └── tests │ │ │ │ ├── app-items-test.js │ │ │ │ └── app-test.js │ │ └── styles │ │ │ └── main.css │ └── index.html ├── gulpfile.js ├── package.json └── server │ ├── .jshintrc │ ├── .jshintrc-spec │ ├── api │ ├── thing │ │ ├── index.js │ │ ├── thing.controller.js │ │ ├── thing.model.js │ │ └── thing.spec.js │ └── user │ │ ├── index.js │ │ ├── user.controller.js │ │ ├── user.model.js │ │ └── user.model.spec.js │ ├── app.js │ ├── auth │ ├── auth.service.js │ ├── facebook │ │ ├── index.js │ │ └── passport.js │ ├── google │ │ ├── index.js │ │ └── passport.js │ ├── index.js │ ├── local │ │ ├── index.js │ │ └── passport.js │ └── twitter │ │ ├── index.js │ │ └── passport.js │ ├── components │ └── errors │ │ └── index.js │ ├── config │ ├── environment │ │ ├── development.js │ │ ├── index.js │ │ ├── production.js │ │ └── test.js │ ├── express.js │ ├── local.env.js │ ├── local.env.sample.js │ └── seed.js │ ├── routes.js │ └── views │ └── 404.html ├── app ├── README.md ├── client │ ├── app │ │ ├── index.html │ │ ├── src │ │ │ ├── actions │ │ │ │ └── AppActions.js │ │ │ ├── app.jsx │ │ │ ├── components │ │ │ │ ├── Banner.jsx │ │ │ │ ├── Footer.jsx │ │ │ │ ├── Home.jsx │ │ │ │ ├── Item.jsx │ │ │ │ ├── Login.jsx │ │ │ │ ├── NavBar.jsx │ │ │ │ ├── Signup.jsx │ │ │ │ └── ToDo.jsx │ │ │ ├── constants │ │ │ │ └── AppConstants.js │ │ │ ├── dispatchers │ │ │ │ └── AppDispatcher.js │ │ │ ├── preprocessor.js │ │ │ ├── stores │ │ │ │ └── AppStore.js │ │ │ └── tests │ │ │ │ ├── app-items-test.js │ │ │ │ └── app-test.js │ │ └── styles │ │ │ └── main.css │ └── index.html ├── gulpfile.js ├── package.json └── server │ ├── .jshintrc │ ├── .jshintrc-spec │ ├── api │ ├── thing │ │ ├── index.js │ │ ├── thing.controller.js │ │ ├── thing.model.js │ │ └── thing.spec.js │ └── user │ │ ├── index.js │ │ ├── user.controller.js │ │ ├── user.model.js │ │ └── user.model.spec.js │ ├── app.js │ ├── auth │ ├── auth.service.js │ ├── facebook │ │ ├── index.js │ │ └── passport.js │ ├── google │ │ ├── index.js │ │ └── passport.js │ ├── index.js │ ├── local │ │ ├── index.js │ │ └── passport.js │ └── twitter │ │ ├── index.js │ │ └── passport.js │ ├── components │ └── errors │ │ └── index.js │ ├── config │ ├── environment │ │ ├── development.js │ │ ├── index.js │ │ ├── production.js │ │ └── test.js │ ├── express.js │ ├── local.env.js │ ├── local.env.sample.js │ └── seed.js │ ├── routes.js │ └── views │ └── 404.html ├── crud-app ├── client │ └── crudApp │ │ └── src │ │ ├── actions │ │ └── CrudAppActions.js │ │ ├── components │ │ └── CrudApp.jsx │ │ ├── constants │ │ └── CrudAppConstants.js │ │ ├── dispatchers │ │ └── CrudAppDispatcher.js │ │ └── stores │ │ └── CrudAppStore.js └── server │ ├── .jshintrc │ ├── .jshintrc-spec │ ├── api │ ├── thing │ │ ├── index.js │ │ ├── thing.controller.js │ │ ├── thing.model.js │ │ └── thing.spec.js │ └── user │ │ ├── index.js │ │ ├── user.controller.js │ │ ├── user.model.js │ │ └── user.model.spec.js │ ├── app.js │ ├── auth │ ├── auth.service.js │ ├── facebook │ │ ├── index.js │ │ └── passport.js │ ├── google │ │ ├── index.js │ │ └── passport.js │ ├── index.js │ ├── local │ │ ├── index.js │ │ └── passport.js │ └── twitter │ │ ├── index.js │ │ └── passport.js │ ├── components │ └── errors │ │ └── index.js │ ├── config │ ├── environment │ │ ├── development.js │ │ ├── index.js │ │ ├── production.js │ │ └── test.js │ ├── express.js │ ├── local.env.js │ ├── local.env.sample.js │ └── seed.js │ ├── routes.js │ └── views │ └── 404.html ├── react-actions └── react-actions.js ├── react-component └── react-component.jsx ├── react-constants └── react-constants.js ├── react-dispatcher └── react-dispatcher.js └── react-store └── react-store.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Node gitignore 2 | # Logs 3 | logs 4 | *.log 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | # Commenting this out is preferred by some people, see 28 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 29 | node_modules 30 | 31 | # OSX Gitignore 32 | 33 | .DS_Store 34 | .AppleDouble 35 | .LSOverride 36 | 37 | # Icon must end with two \r 38 | Icon 39 | 40 | 41 | # Thumbnails 42 | ._* 43 | 44 | # Files that might appear on external disk 45 | .Spotlight-V100 46 | .Trashes 47 | 48 | # Directories potentially created on remote AFP share 49 | .AppleDB 50 | .AppleDesktop 51 | Network Trash Folder 52 | Temporary Items 53 | .apdisk 54 | 55 | # Windows Gitignore 56 | 57 | # Windows image file caches 58 | Thumbs.db 59 | ehthumbs.db 60 | 61 | # Folder config file 62 | Desktop.ini 63 | 64 | # Recycle Bin used on file shares 65 | $RECYCLE.BIN/ 66 | 67 | # Windows Installer files 68 | *.cab 69 | *.msi 70 | *.msm 71 | *.msp 72 | 73 | # Windows shortcuts 74 | *.lnk 75 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 urban-boa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /MILESTONES.md: -------------------------------------------------------------------------------- 1 | ### New Features Groups Could Add ### 2 | - Option to replace flux model system with Angular! 3 | - Option to replace flux model system with Backbone! 4 | - Option to replace flux model system with [insert less popular framework here]! 5 | - For resources to help with this block check out http://www.funnyant.com/reactjs-what-is-it/ 6 | 7 | - Option to replace Crypto back end with BCrypt. 8 | 9 | - React UI - Like Angular UI, but build out a React UI Library 10 | include as an option with the generator. 11 | 12 | - Port from Slush to Yeoman. 13 | - This shouldn't be too hard - Use these templates but learn grunt 14 | 15 | - Custom Injection into existing files upon generation of subroutes. 16 | - Improve Gulp File - Minification etc. 17 | 18 | - BLOG! Tutorial or even a 'how good is this'. 19 | - Use it in your next project 20 | - BugFix Auth - Server Operational, hook up client 21 | - Show to React Facebook / Publicise. -------------------------------------------------------------------------------- /PRESS-RELEASE.md: -------------------------------------------------------------------------------- 1 | # ReactJS-Fullstack Slush Generator# 2 | 3 | ## Summary ## 4 | >Slush Generator for React.js lets you quickly set up a fullstack project with an express server, useful options for database, front end framework, build-tool and testing integration. 5 | 6 | ### Client ### 7 | 8 | * Scripts: JavaScript, JSX, (CoffeeScript) 9 | * Markup: HTML, (Jade) 10 | * Stylesheets: CSS, (Stylus), (Sass), (Less) 11 | 12 | ### Server ### 13 | 14 | * Database: None, MongoDB 15 | * Authentication boilerplate: Yes, No 16 | * PasswordHashing: BCrypt, Crypto 17 | * oAuth integrations: Facebook Twitter Google 18 | * Socket.io integration: Yes, No 19 | 20 | 21 | ##Generators## 22 | * react-fullstack (aka react-fullstack:app) 23 | * react-fullstack:endpoint 24 | * react-fullstack:component 25 | * react-fullstack:crud 26 | * react-fullstack:action 27 | * react-fullstack:dispatcher 28 | * react-fullstack:store 29 | 30 | ## Problem ## 31 | > There is no fullstack ReactJS Generator. Existing react generators offer few features/options. React is gaining popularity and this issue needs to be addressed. 32 | 33 | ## Solution ## 34 | > Elegantly drafts scaffolds for faster implementation of React applications. Drastically reduces the learning curve and time spent writing boilerplate code. Integrates perfectly with Flux and has a complete backend using Express and Mongo. 35 | 36 | ## Quote from You ## 37 | >"I don't even know React but I could make a fullstack app using this. Move over Facebook!" - Teresa 38 | "I have ten million users rendered concurrently on my app. Angular couldn't handle it, but it was no problem for React Fullstack" - Richard 39 | "React can render every single note in the musical spectrum" - Marc 40 | "I just built this for the Github Stars" - James 41 | 42 | ## Usage 43 | Install `slush generator` 44 | ``` 45 | npm install -g slush 46 | ``` 47 | Install `gulp` 48 | ``` 49 | npm install -g gulp 50 | ``` 51 | Make a new directory, and `cd` into it 52 | ``` 53 | mkdir my-new-project && cd $_ 54 | ``` 55 | Install `react-fullstack generator` 56 | ``` 57 | npm install -g slush-react-fullstack 58 | ``` 59 | Generate your full app template 60 | ``` 61 | slush react-fullstack 62 | ``` 63 | 64 | Run `gulp` to preview on local host, don't forget to spin up an instance of mongo when doing so! 65 | 66 | ## Customer Quote ## 67 | > "I owe my entire company to this scaffold" - Tony Phillips 68 | 69 | ## Closing and Call to Action ## 70 | > Use React in your next web app, and use react-fullstack to save time. 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #ReactJS-Fullstack Generator 2 | >Slush generator for ReactJS - lets you quickly set up a project with sensible defaults and best practices. 3 | 4 | ##Getting Started 5 | Install `slush generator` 6 | ``` 7 | npm install -g slush 8 | ``` 9 | Install `gulp` 10 | ``` 11 | npm install -g gulp 12 | ``` 13 | Make a new directory, and `cd` into it 14 | ``` 15 | mkdir my-new-project && cd my-new-project 16 | ``` 17 | Install `react-fullstack generator` 18 | ``` 19 | npm install -g slush-react-fullstack 20 | ``` 21 | Generate your full app template 22 | ``` 23 | slush react-fullstack 24 | ``` 25 | 26 | Run `gulp` to preview on local host, don't forget to spin up an instance of mongo when doing so! 27 | 28 | ##Sub-generators## 29 | 30 | * react-fullstack:crud-app 31 | * react-fullstack:react-component 32 | * react-fullstack:react-constants 33 | * react-fullstack:react-actions 34 | * react-fullstack:react-dispatcher 35 | * react-fullstack:react-store 36 | 37 | To install a sub-generator, pick one from the above list: 38 | ``` 39 | slush react-fullstack:generator_of_your_choice 40 | ``` 41 | 42 | **Note: Generators are to be run from the root directory of your app.** 43 | 44 | ##Testing 45 | Running `gulp jest` will run the unit tests with jest. 46 | 47 | ##Contributing 48 | See the contributing docs: CONTRIBUTING.md for instructions. 49 | 50 | When submitting a bugfix, write a test that exposes the bug and fails before applying your fix. Submit the test alongside the fix. 51 | When submitting a new feature, add tests that cover the feature. 52 | 53 | ## Changelog 54 | 55 | Recent changes can be viewed on Github. 56 | 57 | ## License 58 | [BSD license](http://opensource.org/licenses/bsd-license.php) 59 | -------------------------------------------------------------------------------- /generators/app.js: -------------------------------------------------------------------------------- 1 | module.exports = function(_, conflict, gulp, inquirer, install, mkdirp, rename, template){ 2 | gulp.task('default', function (done) { 3 | var prompts = [{ 4 | name: 'stack', 5 | type: 'list', 6 | message: 'What stack would you like to use?', 7 | choices: ['Flux', 'Backbone', 'Angular', 'Flux w/ Koa'] 8 | }, { 9 | name: 'appName', 10 | message: 'What would you like to call your application?', 11 | default: 'React-Fullstack' 12 | }, { 13 | name: 'appDescription', 14 | message: 'How would you describe your application?', 15 | default: 'Full-Stack JavaScript with MongoDB, Express, React, Flux and Node.js' 16 | }, { 17 | name: 'appKeywords', 18 | message: 'How would you describe your application in comma seperated key words?', 19 | default: 'MongoDB, Express, React, Flux, Node.js' 20 | }, { 21 | name: 'appAuthor', 22 | message: 'What is your company/author name?' 23 | }]; 24 | //Ask 25 | inquirer.prompt(prompts, function (answers) { 26 | if (!answers.appName) { 27 | return done(); 28 | } 29 | answers.slugifiedAppName = _.slugify(answers.appName); 30 | answers.humanizedAppName = _.humanize(answers.appName); 31 | answers.capitalizedAppAuthor = _.capitalize(answers.appAuthor); 32 | var path; 33 | 34 | switch (answers.stack) { 35 | case 'Flux': 36 | path = __dirname + '/../templates/app/**/*'; 37 | break; 38 | case 'Backbone': 39 | path = __dirname + '/../templates/app-backbone/**/*'; 40 | break; 41 | case 'Angular': 42 | path = __dirname + '/../templates/app-angular/**/*'; 43 | break; 44 | case 'Flux w/ Koa': 45 | path = __dirname + '/../templates/app-koa/**/*'; 46 | break; 47 | default: 48 | path = __dirname + '/../templates/app/**/*'; 49 | break; 50 | } 51 | gulp.src(path) 52 | .pipe(template(answers, {interpolate: /<\?\?(.+?)\?>/g})) 53 | .pipe(conflict('./')) 54 | .pipe(gulp.dest('./')) 55 | .pipe(install()) 56 | .on('end', function() { 57 | done(); 58 | }); 59 | }); 60 | }); 61 | return gulp; 62 | } 63 | -------------------------------------------------------------------------------- /generators/appBackbone.js: -------------------------------------------------------------------------------- 1 | module.exports = function(_, conflict, gulp, inquirer, install, mkdirp, rename, template){ 2 | gulp.task('backbone', function (done) { 3 | var prompts = [{ 4 | name: 'appName', 5 | message: 'What would you like to call your application?', 6 | default: 'React-Flux-Fullstack-Backbone' 7 | }, { 8 | name: 'appDescription', 9 | message: 'How would you describe your application?', 10 | default: 'Full-Stack JavaScript with MongoDB, Express, React, Backbone and Node.js' 11 | }, { 12 | name: 'appKeywords', 13 | message: 'How would you describe your application in comma seperated key words?', 14 | default: 'MongoDB, Express, React, Backbone, Node.js' 15 | }, { 16 | name: 'appAuthor', 17 | message: 'What is your company/author name?' 18 | }]; 19 | //Ask 20 | inquirer.prompt(prompts, function (answers) { 21 | if (!answers.appName) { 22 | return done(); 23 | } 24 | answers.slugifiedAppName = _.slugify(answers.appName); 25 | answers.humanizedAppName = _.humanize(answers.appName); 26 | answers.capitalizedAppAuthor = _.capitalize(answers.appAuthor); 27 | 28 | gulp.src(__dirname + '/../templates/app-backbone/**') 29 | .pipe(template(answers, {interpolate: /<\?\?(.+?)\?>/g})) 30 | .pipe(conflict('./')) 31 | .pipe(gulp.dest('./')) 32 | .pipe(install()) 33 | .on('end', function() { 34 | done(); 35 | }); 36 | }); 37 | }); 38 | return gulp; 39 | }; 40 | -------------------------------------------------------------------------------- /generators/crudApp.js: -------------------------------------------------------------------------------- 1 | module.exports = function (_, conflict, gulp, inquirer, install, mkdirp, rename, template) { 2 | gulp.task('crud-app', function (done) { 3 | var prompts = [{ 4 | name: 'crudName', 5 | message: 'What would you like to call your CRUD application?' 6 | }]; 7 | //Ask 8 | inquirer.prompt(prompts, function (answers) { 9 | if (!answers.appName) { 10 | return done(); 11 | } 12 | answers.slugifiedCrudName = _.slugify(answers.crudName); 13 | answers.underscoredCrudName = _.underscored(answers.slugifiedCrudName); 14 | answers.classifiedCrudName = _.classify(answers.underscoredCrudName); 15 | 16 | gulp.src(__dirname + '/../templates/crud-app/client/crudApp/**') 17 | .pipe(template(answers, {interpolate: /<\?\?(.+?)\?>/g})) 18 | .pipe(rename(function(file) { 19 | if (file.basename.slice(0, 8) === 'CrudApp') { 20 | file.basename = file.basename.replace('CrudApp', answers.classifiedCrudName); 21 | } 22 | })) 23 | .pipe(conflict('client/' + answers.classfiedCrudName + '/')) 24 | .pipe(gulp.dest('client/' + answers.classfiedCrudName + '/')) 25 | .pipe(install()) 26 | .on('end', function() { 27 | done(); 28 | }); 29 | }); 30 | }); 31 | return gulp; 32 | } -------------------------------------------------------------------------------- /generators/reactActions.js: -------------------------------------------------------------------------------- 1 | module.exports = function (_, conflict, gulp, inquirer, install, mkdirp, rename, template) { 2 | gulp.task('react-actions', function (done) { 3 | var prompts = [{ 4 | name: 'actionName', 5 | message: 'What is the name of the new action?' 6 | }]; 7 | //Ask 8 | inquirer.prompt(prompts, function (answers) { 9 | if (!answers) { 10 | return done(); 11 | } 12 | answers.slugifiedActionName = _.slugify(answers.actionName); 13 | answers.underscoredActionName = _.underscored(answers.slugifiedActionName); 14 | answers.classifiedActionName = _.classify(answers.underscoredActionName); 15 | 16 | gulp.src(__dirname + '/../templates/react-actions/react-actions.js') 17 | .pipe(template(answers, {interpolate: /<\?\?(.+?)\?>/g})) 18 | .pipe(rename(answers.classifiedActionName + '.js')) 19 | .pipe(conflict('client/app/src/actions')) 20 | .pipe(gulp.dest('client/app/src/actions')) 21 | .on('end', function () { 22 | done(); 23 | }); 24 | }); 25 | }); 26 | return gulp; 27 | }; -------------------------------------------------------------------------------- /generators/reactComponent.js: -------------------------------------------------------------------------------- 1 | module.exports = function (_, conflict, gulp, inquirer, install, mkdirp, rename, template) { 2 | gulp.task('react-component', function (done) { 3 | var prompts = [{ 4 | name: 'componentName', 5 | message: 'What is the name of the new component?' 6 | }]; 7 | //Ask 8 | inquirer.prompt(prompts, function (answers) { 9 | if (!answers) { 10 | return done(); 11 | } 12 | answers.slugifiedComponentName = _.slugify(answers.componentName); 13 | answers.underscoredComponentName = _.underscored(answers.slugifiedComponentName); 14 | answers.classifiedComponentName = _.classify(answers.underscoredComponentName); 15 | 16 | gulp.src(__dirname + '/../templates/react-component/react-component.jsx') 17 | .pipe(template(answers, {interpolate: /<\?\?(.+?)\?>/g})) 18 | .pipe(rename(answers.classifiedComponentName + '.jsx')) 19 | .pipe(conflict('client/app/src/components')) 20 | .pipe(gulp.dest('client/app/src/components')) 21 | .on('end', function () { 22 | done(); 23 | }); 24 | }); 25 | }); 26 | return gulp; 27 | }; -------------------------------------------------------------------------------- /generators/reactConstants.js: -------------------------------------------------------------------------------- 1 | module.exports = function (_, conflict, gulp, inquirer, install, mkdirp, rename, template) { 2 | gulp.task('react-constants', function (done) { 3 | var prompts = [{ 4 | name: 'constantsName', 5 | message: 'What is the name of the new component?' 6 | }]; 7 | //Ask 8 | inquirer.prompt(prompts, function (answers) { 9 | if (!answers) { 10 | return done(); 11 | } 12 | answers.slugifiedConstantsName = _.slugify(answers.constantsName); 13 | answers.underscoredConstantsName = _.underscored(answers.slugifiedConstantsName); 14 | answers.classifiedConstantsName = _.classify(answers.underscoredConstantsName); 15 | 16 | gulp.src(__dirname + '/../templates/react-constants/react-constants.js') 17 | .pipe(template(answers, {interpolate: /<\?\?(.+?)\?>/g})) 18 | .pipe(rename(answers.classifiedConstantsName + '.js')) 19 | .pipe(conflict('client/app/src/constants')) 20 | .pipe(gulp.dest('client/app/src/constants')) 21 | .on('end', function () { 22 | done(); 23 | }); 24 | }); 25 | }); 26 | return gulp; 27 | }; -------------------------------------------------------------------------------- /generators/reactDispatcher.js: -------------------------------------------------------------------------------- 1 | module.exports = function (_, conflict, gulp, inquirer, install, mkdirp, rename, template) { 2 | gulp.task('react-dispatcher', function (done) { 3 | var prompts = [{ 4 | name: 'dispatcherName', 5 | message: 'What is the name of the new dispatcher?' 6 | }]; 7 | //Ask 8 | inquirer.prompt(prompts, function (answers) { 9 | if (!answers) { 10 | return done(); 11 | } 12 | answers.slugifiedDispatcherName = _.slugify(answers.dispatcherName); 13 | answers.underscoredDispatcherName = _.underscored(answers.slugifiedDispatcherName); 14 | answers.classifiedDispatcherName = _.classify(answers.underscoredDispatcherName); 15 | 16 | gulp.src(__dirname + '/../templates/react-dispatcher/react-dispatcher.js') 17 | .pipe(template(answers, {interpolate: /<\?\?(.+?)\?>/g})) 18 | .pipe(rename(answers.classifiedDispatcherName + '.js')) 19 | .pipe(conflict('client/app/src/dispatchers')) 20 | .pipe(gulp.dest('client/app/src/dispatchers')) 21 | .on('end', function () { 22 | done(); 23 | }); 24 | }); 25 | }); 26 | return gulp; 27 | }; -------------------------------------------------------------------------------- /generators/reactStore.js: -------------------------------------------------------------------------------- 1 | module.exports = function (_, conflict, gulp, inquirer, install, mkdirp, rename, template) { 2 | gulp.task('react-store', function (done) { 3 | var prompts = [{ 4 | name: 'storeName', 5 | message: 'What is the name of the new store?' 6 | }]; 7 | //Ask 8 | inquirer.prompt(prompts, function (answers) { 9 | if (!answers) { 10 | return done(); 11 | } 12 | answers.slugifiedStoreName = _.slugify(answers.storeName); 13 | answers.underscoredStoreName = _.underscored(answers.slugifiedStoreName); 14 | answers.classifiedStoreName = _.classify(answers.underscoredStoreName); 15 | 16 | gulp.src(__dirname + '/../templates/react-store/react-store.js') 17 | .pipe(template(answers, {interpolate: /<\?\?(.+?)\?>/g})) 18 | .pipe(rename(answers.classifiedStoreName + '.js')) 19 | .pipe(conflict('client/app/src/stores')) 20 | .pipe(gulp.dest('client/app/src/stores')) 21 | .on('end', function () { 22 | done(); 23 | }); 24 | }); 25 | }); 26 | return gulp; 27 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "slush-react-fullstack", 3 | "version": "0.1.1", 4 | "homepage": "https://github.com/urban-boa/SlushDraft", 5 | "description": "slush generator", 6 | "main": "slushfile.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/react-fullstack/slush-react-fullstack.git" 10 | }, 11 | "keywords": [ 12 | "slushgenerator", 13 | "generator", 14 | "slush", 15 | "React", 16 | "Flux", 17 | "Expressjs", 18 | "MongoDB", 19 | "Node" 20 | ], 21 | "author": "urban-boa", 22 | "license": "ISC", 23 | "bugs": { 24 | "url": "https://github.com/react-fullstack/slush-react-fullstack/issues" 25 | }, 26 | "scripts": { 27 | "test": "echo \"Error: no test specified\" && exit 1" 28 | }, 29 | "dependencies": { 30 | "gulp": "^3.6.2", 31 | "gulp-conflict": "^0.1.1", 32 | "gulp-install": "^0.1.4", 33 | "gulp-rename": "^1.2.0", 34 | "gulp-template": "^0.1.1", 35 | "inflection": "^1.3.5", 36 | "inquirer": "^0.4.1", 37 | "mkdirp": "^0.5.0", 38 | "underscore.string": "^2.3.3" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /slushfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var _ = require('underscore.string'); 4 | var conflict = require('gulp-conflict'); 5 | var gulp = require('gulp'); 6 | var inquirer = require('inquirer'); 7 | var install = require('gulp-install'); 8 | var mkdirp = require('mkdirp'); 9 | var rename = require('gulp-rename'); 10 | var template = require('gulp-template'); 11 | 12 | // load generators 13 | gulp = require('./generators/app')(_, conflict, gulp, inquirer, install, mkdirp, rename, template); 14 | gulp = require('./generators/crudApp')(_, conflict, gulp, inquirer, install, mkdirp, rename, template); 15 | gulp = require('./generators/appBackbone')(_, conflict, gulp, inquirer, install, mkdirp, rename, template); 16 | gulp = require('./generators/reactActions')(_, conflict, gulp, inquirer, install, mkdirp, rename, template); 17 | gulp = require('./generators/reactComponent')(_, conflict, gulp, inquirer, install, mkdirp, rename, template); 18 | gulp = require('./generators/reactConstants')(_, conflict, gulp, inquirer, install, mkdirp, rename, template); 19 | gulp = require('./generators/reactDispatcher')(_, conflict, gulp, inquirer, install, mkdirp, rename, template); 20 | gulp = require('./generators/reactStore')(_, conflict, gulp, inquirer, install, mkdirp, rename, template); 21 | -------------------------------------------------------------------------------- /templates/app-backbone/README.md: -------------------------------------------------------------------------------- 1 | ## ## 2 | 3 | 4 | 5 | ### by ### 6 | 7 | 8 | The MEAN stack just got MEANER. Mongo, Express, Angular, Node et React. 9 | 10 | A Full Stack Application built with the React-Fullstack Slush Generator! 11 | 12 | -------------------------------------------------------------------------------- /templates/app-backbone/client/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <?? humanizedAppName ?> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /templates/app-backbone/client/app/src/app.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | /*ReadMe: 4 | Components are where you will be designing your view in the 5 | render section. This code is written in jsx; 6 | is used below for changing views for routing. 7 | 8 | To add components, you can run react-fullstack:component, and it will create a new component file in the folder. 9 | */ 10 | 11 | 12 | 'use strict'; 13 | 14 | var Backbone = require('backbone'); 15 | // Have to point Backbone's jQuery at jQuery 16 | Backbone.$ = $; 17 | //console.log(Backbone); 18 | //console.log($); 19 | 20 | var React = require('react'); 21 | 22 | var LOGIN = require('./components/Login.jsx'); 23 | var HOME = require('./components/Home.jsx'); 24 | var SIGNUP = require('./components/Signup.jsx'); 25 | var NAV = require('./components/NavBar.jsx'); 26 | var BANNER = require('./components/Banner.jsx'); 27 | var FOOTER = require('./components/Footer.jsx'); 28 | 29 | var Router = require('react-router'); 30 | var Route = Router.Route; 31 | var Routes = Router.Routes; 32 | var NotFoundRoute = Router.NotFoundRoute; 33 | var DefaultRoute = Router.DefaultRoute; 34 | var Link = Router.Link; 35 | 36 | 37 | var APP = React.createClass({ 38 | render: function(){ 39 | return ( 40 |
41 |
46 | ) 47 | } 48 | }) 49 | 50 | var routes = ( 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | ); 60 | 61 | module.exports = React.renderComponent(routes, document.body); 62 | -------------------------------------------------------------------------------- /templates/app-backbone/client/app/src/collections/Todos.js: -------------------------------------------------------------------------------- 1 | var Backbone = require('backbone'); 2 | var Todo = require('../models/Todo'); 3 | 4 | var Todos = module.exports = Backbone.Collection.extend({ 5 | model: Todo, 6 | url: '/api/things' 7 | }); 8 | -------------------------------------------------------------------------------- /templates/app-backbone/client/app/src/components/Banner.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | var React = require('react'); 5 | 6 | var BANNER = React.createClass({ 7 | 8 | 9 | render: function(){ 10 | return ( 11 |
12 | 13 |
14 | ) 15 | } 16 | }) 17 | 18 | module.exports = BANNER; -------------------------------------------------------------------------------- /templates/app-backbone/client/app/src/components/Footer.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | var React = require('react'); 5 | 6 | var FOOTER = React.createClass({ 7 | 8 | 9 | render: function(){ 10 | return ( 11 |
12 |
13 |

14 | Fork us on Github! 15 |

16 |
17 |
18 | ) 19 | } 20 | }) 21 | 22 | module.exports = FOOTER; 23 | -------------------------------------------------------------------------------- /templates/app-backbone/client/app/src/components/Home.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | 'use strict'; 5 | 6 | var Backbone = require('backbone'); 7 | 8 | var Todos = require('../collections/todos'); 9 | 10 | var React = require('react'); 11 | 12 | var NAV = require('./NavBar.jsx'); 13 | var BANNER = require('./Banner.jsx'); 14 | var TODO = require('./ToDo.jsx'); 15 | 16 | var Q = require('q'); 17 | 18 | var BackboneMixin = { 19 | componentDidMount: function () { 20 | // Whenever there may be a change in the Backbone data, trigger a 21 | // reconcile. 22 | console.log('collections', this.getBackboneCollections()); 23 | this.getBackboneCollections().forEach(function (collection) { 24 | console.log('binding'); 25 | 26 | // Add event listener to the collection to 27 | // force update on change. 28 | collection.on('add remove change', function() { 29 | //console.log('update!'); 30 | this.forceUpdate(); 31 | }.bind(this)); 32 | 33 | // Initial fetch of the collection. 34 | collection.fetch(); 35 | }, this); 36 | }, 37 | 38 | componentWillUnmount: function () { 39 | // Ensure that we clean up any dangling references when the component is 40 | // destroyed. 41 | this.getBackboneCollections().forEach(function (collection) { 42 | collection.off(null, null, this); 43 | }, this); 44 | } 45 | }; 46 | 47 | var APP = React.createClass({ 48 | mixins: [BackboneMixin], 49 | 50 | getBackboneCollections: function() { 51 | return [this.state.todos]; 52 | }, 53 | 54 | getInitialState: function(){ 55 | // Initialize Backbone Todos model 56 | var todos = new Todos(); 57 | console.log("todos:", todos); 58 | return {todos: todos}; 59 | }, 60 | 61 | _onChange: function(){ 62 | // this.setState(getAppState()); 63 | var that = this; 64 | Q(getAppState()).then(function(promise){ 65 | console.log('change') 66 | console.log(that.state) 67 | that.setState({todos: that.state.todos.models}) 68 | that.forceUpdate(); 69 | }) 70 | 71 | }, 72 | 73 | componentDidMount: function(){ 74 | var that = this; 75 | console.log("mounted"); 76 | }, 77 | 78 | componentWillUnmount: function(){ 79 | 80 | }, 81 | 82 | handleClick: function(){ 83 | 84 | }, 85 | 86 | render: function(){ 87 | return ( 88 |
89 | 90 |
91 | ) 92 | } 93 | }) 94 | 95 | module.exports = APP; 96 | -------------------------------------------------------------------------------- /templates/app-backbone/client/app/src/components/Item.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | 'use strict'; 5 | 6 | 7 | var React = require('react'); 8 | 9 | var ITEM = React.createClass({ 10 | 11 | handleClick: function(e) { 12 | e.preventDefault(); 13 | console.log(this.props.item.url()); 14 | this.props.item.destroy({ 15 | success: function() { 16 | console.log('destroyed!'); 17 | }, 18 | error: function() { 19 | console.log('failed to destroy!'); 20 | }, 21 | wait:true 22 | }); 23 | 24 | }, 25 | 26 | render: function(){ 27 | return ( 28 |
  • 29 | {this.props.item.get('item')} 30 | x 31 |
  • 32 | ) 33 | } 34 | }) 35 | 36 | module.exports = ITEM; 37 | -------------------------------------------------------------------------------- /templates/app-backbone/client/app/src/components/Login.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | 'use strict'; 5 | 6 | var React = require('react'); 7 | 8 | var LOGIN = React.createClass({ 9 | 10 | handleClick: function(){ 11 | console.log("at handleClick in Signup") 12 | }, 13 | 14 | handleInput: function(e) { 15 | console.log("at handleInput in Login") 16 | }, 17 | 18 | render: function(){ 19 | return ( 20 |
    21 |
    22 |

    Login

    23 |
    24 |
    25 | 26 |
    27 | 28 |
    29 |
    30 |
    31 | 32 |
    33 | 34 |
    35 |
    36 |
    37 |
    38 |
    39 | 42 |
    43 |
    44 |
    45 |
    46 |
    47 | 48 |
    49 |
    50 |
    51 |
    52 |
    53 | ) 54 | } 55 | }) 56 | 57 | module.exports = LOGIN; 58 | -------------------------------------------------------------------------------- /templates/app-backbone/client/app/src/components/NavBar.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | 'use strict'; 5 | 6 | var React = require('react'); 7 | 8 | var Router = require('react-router'); 9 | var Link = Router.Link; 10 | 11 | function getAppState(){ 12 | return AppStore.getData(); 13 | }; 14 | 15 | var NAV = React.createClass({ 16 | 17 | render: function(){ 18 | return ( 19 | 26 | ); 27 | } 28 | }) 29 | 30 | module.exports = NAV; 31 | -------------------------------------------------------------------------------- /templates/app-backbone/client/app/src/components/Signup.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | 'use strict'; 5 | 6 | var React = require('react'); 7 | 8 | var NAV = require('./NavBar.jsx'); 9 | var BANNER = require('./Banner.jsx') 10 | 11 | 12 | var SIGNUP = React.createClass({ 13 | 14 | handleClick: function(){ 15 | console.log("at handleClick in Signup") 16 | }, 17 | 18 | handleInput: function(e) { 19 | console.log("at handleInput in Signup") 20 | }, 21 | 22 | render: function(){ 23 | return ( 24 |
    25 |
    26 |

    Signup

    27 |
    28 |
    29 | 30 |
    31 | 32 |
    33 |
    34 |
    35 | 36 |
    37 | 38 |
    39 |
    40 |
    41 | 42 |
    43 | 44 |
    45 |
    46 | 47 |
    48 |
    49 | 50 |
    51 |
    52 |
    53 |
    54 |
    55 | 56 | ) 57 | } 58 | }) 59 | 60 | module.exports = SIGNUP; 61 | -------------------------------------------------------------------------------- /templates/app-backbone/client/app/src/components/ToDo.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 'use strict'; 4 | 5 | 6 | var React = require('react'); 7 | 8 | var ITEM = require('./Item.jsx'); 9 | 10 | 11 | var TODO = React.createClass({ 12 | 13 | handleClick: function() { 14 | 15 | var task = this.refs.todo.getDOMNode().value.trim(); 16 | if (task != '') { 17 | this.props.allTodos.add({item: task}).save(); 18 | } 19 | this.refs.todo.getDOMNode().value = ''; 20 | }, 21 | 22 | handleInput: function(e) { 23 | if (e.nativeEvent.charCode === 13) { 24 | this.handleClick(); 25 | } 26 | }, 27 | 28 | render: function(){ 29 | //console.log('RENDER:', this.props); 30 | var items = this.props.allTodos.models.map(function(todo, i) { 31 | //console.log(todo); 32 | //console.log(todo.get('item')); 33 | 34 | // pass todo model to item component 35 | return 36 | }); 37 | return ( 38 |
    39 |
    40 |

    Welcome To The React-Flux-Fullstack Slush Generator

    41 |
    42 | 43 | 44 | 45 | 46 |
    47 |

    Features:

    48 |
      49 | {items} 50 |
    51 |
    52 |
    53 | ); 54 | } 55 | }) 56 | 57 | module.exports = TODO; 58 | -------------------------------------------------------------------------------- /templates/app-backbone/client/app/src/models/Todo.js: -------------------------------------------------------------------------------- 1 | var Backbone = require('backbone'); 2 | 3 | var Todo = module.exports = Backbone.Model.extend({ 4 | idAttribute: '_id', 5 | defaults: { 6 | title: '', 7 | completed: false 8 | }, 9 | 10 | }); 11 | -------------------------------------------------------------------------------- /templates/app-backbone/client/app/src/preprocessor.js: -------------------------------------------------------------------------------- 1 | // preprocessor.js 2 | var ReactTools = require('react-tools'); 3 | module.exports = { 4 | process: function(src) { 5 | return ReactTools.transform(src); 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /templates/app-backbone/client/app/src/tests/app-items-test.js: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | jest.dontMock('../components/Home.jsx'); 4 | jest.dontMock('../components/ToDo.jsx'); 5 | jest.dontMock('../components/Item.jsx'); 6 | 7 | jest.dontMock('../stores/AppStore'); 8 | jest.dontMock('../actions/AppActions'); 9 | jest.dontMock('../dispatchers/AppDispatcher'); 10 | jest.dontMock('../constants/AppConstants'); 11 | jest.dontMock('react/lib/merge'); 12 | 13 | 14 | describe('Main App Items', function() { 15 | 16 | var React = require('react/addons'); 17 | var App = require('../components/Home.jsx'); 18 | var TestUtils = React.addons.TestUtils; 19 | 20 | it('can add new item', function() { 21 | 22 | var testApp = TestUtils.renderIntoDocument(); 23 | var input = TestUtils.findRenderedDOMComponentWithClass(testApp, "form-control"); 24 | var button = TestUtils.findRenderedDOMComponentWithClass(testApp, "btn"); 25 | 26 | input.getDOMNode().value = "Test"; 27 | TestUtils.Simulate.click(button.getDOMNode()); 28 | expect(input.getDOMNode().value).toEqual(''); 29 | 30 | }); 31 | }); -------------------------------------------------------------------------------- /templates/app-backbone/client/app/src/tests/app-test.js: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | jest.dontMock('../components/Home.jsx'); 4 | jest.dontMock('../components/ToDo.jsx'); 5 | jest.dontMock('../components/Item.jsx'); 6 | jest.dontMock('../stores/AppStore.js'); 7 | 8 | describe('Main App', function() { 9 | it('has render method', function() { 10 | 11 | var React = require('react/addons'); 12 | var App = require('../components/Home.jsx'); 13 | var TestUtils = React.addons.TestUtils; 14 | 15 | var testApp = TestUtils.renderIntoDocument(); 16 | 17 | expect(TestUtils.isCompositeComponent(testApp)).toEqual(true); 18 | expect(testApp.render).toBeDefined(); 19 | 20 | // var items = TestUtils.scryRenderedDOMComponentsWithClass(testApp, "list-group-item"); 21 | 22 | // for (var i = 0; i < items.length; i++) { 23 | // console.log(items[i].getDOMNode().textContent); 24 | // } 25 | }); 26 | }); -------------------------------------------------------------------------------- /templates/app-backbone/client/app/styles/main.css: -------------------------------------------------------------------------------- 1 | .navbar { 2 | margin-bottom: 0; 3 | } 4 | 5 | .list-group { 6 | margin-top: 25px; 7 | margin-bottom: 150px; 8 | } 9 | 10 | .banner { 11 | text-align: center; 12 | background-color: #D184B7; 13 | padding-top: 30px; 14 | padding-bottom: 30px; 15 | } 16 | 17 | .login { 18 | margin-right: 25px; 19 | } 20 | 21 | .input-group { 22 | margin-top: 25px; 23 | } 24 | 25 | .footer { 26 | position: fixed; 27 | bottom: 0; 28 | width: 100%; 29 | /* Set the fixed height of the footer here */ 30 | height: 60px; 31 | background-color: #f5f5f5; 32 | } 33 | 34 | .container .text-muted { 35 | margin: 20px 0; 36 | } 37 | 38 | .control-label { 39 | margin-left: 20px; 40 | } 41 | 42 | .control-button { 43 | margin-left: 20px; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /templates/app-backbone/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <?? humanizedAppName ?> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /templates/app-backbone/gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | 5 | var browserify = require('browserify'); 6 | var reactify = require('reactify'); 7 | var source = require('vinyl-source-stream'); 8 | var connect = require('gulp-connect'); 9 | var nodemon = require('gulp-nodemon'); 10 | var jest = require('gulp-jest'); 11 | 12 | var paths = { 13 | app: ['./client/app/src/app.jsx'], 14 | js: ['./client/app/src/**/*.*'], 15 | }; 16 | 17 | gulp.task('browserify', function() { 18 | // Browserify/bundle the JS. 19 | browserify(paths.app) 20 | .transform(reactify) 21 | .bundle() 22 | .pipe(source('bundle.js')) 23 | .pipe(gulp.dest('./client/app/build/')); 24 | // .pipe(connect.reload()); 25 | }); 26 | 27 | // gulp.task('connect', function(){ 28 | // connect.server({ 29 | // root: 'templates/app', 30 | // livereload: true, 31 | // port: 8080 32 | // }); 33 | // }); 34 | 35 | gulp.task('nodemon', function(done){ 36 | nodemon({ script: './server/app.js', env: { 'NODE_ENV': 'development'}}) 37 | .on('restart'); 38 | }); 39 | 40 | // Rerun the task when a file changes 41 | gulp.task('watch', function() { 42 | gulp.watch(paths.js, ['browserify']); 43 | }); 44 | 45 | // The default task (called when you run `gulp` from cli) 46 | gulp.task('default', ['serve']); 47 | 48 | gulp.task('serve', ['browserify', 'nodemon']); 49 | 50 | // Update Express is serving the right files 51 | // Browserify/Reactify/Serve 52 | 53 | gulp.task('jest', function () { 54 | return gulp.src('./client/app/src/').pipe(jest({ 55 | scriptPreprocessor: "./preprocessor.js", 56 | unmockedModulePathPatterns: [ 57 | "node_modules/react" 58 | ], 59 | testDirectoryName: "tests", 60 | testPathIgnorePatterns: [ 61 | "node_modules", 62 | "spec/support" 63 | ], 64 | moduleFileExtensions: [ 65 | "js", 66 | "json", 67 | "react" 68 | ] 69 | })); 70 | }); 71 | -------------------------------------------------------------------------------- /templates/app-backbone/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "version": "0.0.1", 4 | "description": "A template for fullstack react applications with gulp and browserify", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node server/app.js", 8 | "test": "gulp test" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/urban-boa/react-fullstack.git" 13 | }, 14 | "dependencies": { 15 | "body-parser": "~1.5.0", 16 | "composable-middleware": "^0.3.0", 17 | "compression": "~1.0.1", 18 | "connect-mongo": "^0.4.1", 19 | "cookie-parser": "~1.0.1", 20 | "ejs": "~0.8.4", 21 | "errorhandler": "~1.0.0", 22 | "express": "~4.0.0", 23 | "express-jwt": "^0.1.3", 24 | "express-session": "~1.0.2", 25 | "flux": "^2.0.1", 26 | "gulp-nodemon": "^1.0.4", 27 | "jquery-browserify": "^1.8.1", 28 | "jsonwebtoken": "^0.3.0", 29 | "lodash": "~2.4.1", 30 | "method-override": "~1.0.0", 31 | "mongoose": "~3.8.8", 32 | "morgan": "~1.0.0", 33 | "passport": "~0.2.0", 34 | "passport-facebook": "latest", 35 | "passport-google-oauth": "latest", 36 | "passport-local": "~0.1.6", 37 | "passport-twitter": "latest", 38 | "q": "^1.0.1", 39 | "react": "^0.11.2", 40 | "react-router": "^0.9.4", 41 | "serve-favicon": "~2.0.1", 42 | "gulp-jest": "*", 43 | "react-tools": "^0.11.2" 44 | }, 45 | "author": "Urban Boa", 46 | "license": "ISC", 47 | "bugs": { 48 | "url": "https://github.com/urban-boa/react-fullstack/issues" 49 | }, 50 | "engines": { 51 | "node": ">=0.10.0" 52 | }, 53 | "homepage": "https://github.com/urban-boa/react-fullstack/", 54 | "devDependencies": { 55 | "browserify": "^5.12.0", 56 | "gulp": "^3.8.8", 57 | "gulp-connect": "^2.0.6", 58 | "jest-cli": "^0.1.18", 59 | "reactify": "^0.14.0", 60 | "vinyl-source-stream": "^1.0.0" 61 | }, 62 | "jest": { 63 | "scriptPreprocessor": "/preprocessor.js", 64 | "unmockedModulePathPatterns": ["/node_modules/react"] 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /templates/app-backbone/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 | -------------------------------------------------------------------------------- /templates/app-backbone/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 | -------------------------------------------------------------------------------- /templates/app-backbone/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; -------------------------------------------------------------------------------- /templates/app-backbone/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 | } -------------------------------------------------------------------------------- /templates/app-backbone/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 | item: String, 8 | info: String, 9 | active: Boolean 10 | }); 11 | 12 | module.exports = mongoose.model('Thing', ThingSchema); -------------------------------------------------------------------------------- /templates/app-backbone/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 | -------------------------------------------------------------------------------- /templates/app-backbone/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 | -------------------------------------------------------------------------------- /templates/app-backbone/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 | -------------------------------------------------------------------------------- /templates/app-backbone/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 | require('./config/express')(app); 24 | require('./routes')(app); 25 | 26 | // Start server 27 | server.listen(config.port, config.ip, function () { 28 | console.log('Express server listening on %d, in %s mode', config.port, app.get('env')); 29 | }); 30 | 31 | // Expose app 32 | exports = module.exports = app; -------------------------------------------------------------------------------- /templates/app-backbone/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; -------------------------------------------------------------------------------- /templates/app-backbone/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; -------------------------------------------------------------------------------- /templates/app-backbone/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 | }; -------------------------------------------------------------------------------- /templates/app-backbone/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; -------------------------------------------------------------------------------- /templates/app-backbone/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 | -------------------------------------------------------------------------------- /templates/app-backbone/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; -------------------------------------------------------------------------------- /templates/app-backbone/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; -------------------------------------------------------------------------------- /templates/app-backbone/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 | }; -------------------------------------------------------------------------------- /templates/app-backbone/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; -------------------------------------------------------------------------------- /templates/app-backbone/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 | }; -------------------------------------------------------------------------------- /templates/app-backbone/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 | -------------------------------------------------------------------------------- /templates/app-backbone/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/slushy-dev' 9 | }, 10 | 11 | seedDB: true 12 | }; 13 | -------------------------------------------------------------------------------- /templates/app-backbone/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: 'slushy-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') || {}); -------------------------------------------------------------------------------- /templates/app-backbone/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/slushy' 22 | } 23 | }; -------------------------------------------------------------------------------- /templates/app-backbone/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/slushy-test' 9 | } 10 | }; -------------------------------------------------------------------------------- /templates/app-backbone/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 | }; 60 | -------------------------------------------------------------------------------- /templates/app-backbone/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: "slushy-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 | -------------------------------------------------------------------------------- /templates/app-backbone/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: 'slushy-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 | -------------------------------------------------------------------------------- /templates/app-backbone/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 | item : '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 | item : 'Server and Client integration', 17 | info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' 18 | }, { 19 | item : '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 | item : 'Modular Structure', 23 | info : 'Best practice client and server structures allow for more code reusability and maximum scalability' 24 | }, { 25 | item : '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 | item : '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 | }); 50 | -------------------------------------------------------------------------------- /templates/app-backbone/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 | -------------------------------------------------------------------------------- /templates/app-koa/README.md: -------------------------------------------------------------------------------- 1 | ## ## 2 | 3 | 4 | 5 | ### by ### 6 | 7 | 8 | The MEAN stack just got MEANER. Mongo, Express, Angular, Node et React. 9 | 10 | A Full Stack Application built with the React-Fullstack Slush Generator! 11 | 12 | -------------------------------------------------------------------------------- /templates/app-koa/client/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <?? humanizedAppName ?> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /templates/app-koa/client/app/src/actions/AppActions.js: -------------------------------------------------------------------------------- 1 | /*ReadMe: 2 | Actions are data packaged from user interactions or web APIs. 3 | It is an object literal containing the new fields of data and 4 | specific action types. Actions are sent to the dispatcher before 5 | the store. 6 | */ 7 | 8 | 'use strict'; 9 | var AppDispatcher = require('../dispatchers/AppDispatcher'); 10 | var AppConstants = require('../constants/AppConstants'); 11 | 12 | var AppActions = { 13 | populateAction: function() { 14 | return AppDispatcher.handleViewAction({ 15 | actionType: AppConstants.POPULATE 16 | }); 17 | }, 18 | addItemAction: function(text) { 19 | AppDispatcher.handleViewAction({ 20 | actionType: AppConstants.ADD, 21 | text: text 22 | }); 23 | }, 24 | removeItemAction: function(id) { 25 | AppDispatcher.handleViewAction({ 26 | actionType: AppConstants.REMOVE, 27 | id: id 28 | }); 29 | } 30 | }; 31 | 32 | module.exports = AppActions; 33 | -------------------------------------------------------------------------------- /templates/app-koa/client/app/src/app.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | /*ReadMe: 4 | Components are where you will be designing your view in the 5 | render section. This code is written in jsx; 6 | is used below for changing views for routing. 7 | 8 | To add components, you can run react-fullstack:component, and it will create a new component file in the folder. 9 | */ 10 | 11 | 12 | 'use strict'; 13 | 14 | var React = require('react'); 15 | 16 | var LOGIN = require('./components/Login.jsx'); 17 | var HOME = require('./components/Home.jsx'); 18 | var SIGNUP = require('./components/Signup.jsx'); 19 | var NAV = require('./components/NavBar.jsx'); 20 | var BANNER = require('./components/Banner.jsx'); 21 | var FOOTER = require('./components/Footer.jsx'); 22 | 23 | var Router = require('react-router'); 24 | var Route = Router.Route; 25 | var Routes = Router.Routes; 26 | var NotFoundRoute = Router.NotFoundRoute; 27 | var DefaultRoute = Router.DefaultRoute; 28 | var Link = Router.Link; 29 | 30 | 31 | var APP = React.createClass({ 32 | render: function(){ 33 | return ( 34 |
    35 |
    40 | ) 41 | } 42 | }) 43 | 44 | var routes = ( 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | ); 54 | 55 | module.exports = React.renderComponent(routes, document.body); 56 | -------------------------------------------------------------------------------- /templates/app-koa/client/app/src/components/Banner.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | var React = require('react'); 5 | 6 | var BANNER = React.createClass({ 7 | 8 | 9 | render: function(){ 10 | return ( 11 |
    12 | 13 |
    14 | ) 15 | } 16 | }) 17 | 18 | module.exports = BANNER; -------------------------------------------------------------------------------- /templates/app-koa/client/app/src/components/Footer.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | var React = require('react'); 5 | 6 | var FOOTER = React.createClass({ 7 | 8 | 9 | render: function(){ 10 | return ( 11 |
    12 |
    13 |

    Fork us on Github!

    14 |
    15 |
    16 | ) 17 | } 18 | }) 19 | 20 | module.exports = FOOTER; -------------------------------------------------------------------------------- /templates/app-koa/client/app/src/components/Home.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | 'use strict'; 5 | 6 | var React = require('react'); 7 | 8 | var AppStore = require('../stores/AppStore'); 9 | var AppActions = require('../actions/AppActions'); 10 | 11 | var NAV = require('./NavBar.jsx'); 12 | var BANNER = require('./Banner.jsx'); 13 | var TODO = require('./ToDo.jsx'); 14 | 15 | var Q = require('q'); 16 | 17 | function getAppState(){ 18 | return AppStore.getData(); 19 | }; 20 | 21 | function getInitialAppState(){ 22 | return AppStore.getInitialData(); 23 | } 24 | 25 | var APP = React.createClass({ 26 | getInitialState: function(){ 27 | return getInitialAppState(); 28 | // return getAppState(); 29 | // return null; 30 | }, 31 | 32 | _onChange: function(){ 33 | // this.setState(getAppState()); 34 | var that = this; 35 | Q(getAppState()).then(function(promise){ 36 | console.log('change') 37 | console.log(promise) 38 | that.setState({todos: promise}) 39 | }) 40 | 41 | }, 42 | 43 | componentDidMount: function(){ 44 | var that = this; 45 | console.log(1) 46 | AppStore.addChangeListener(this._onChange); 47 | // Q(AppActions.populateAction()).then(function(promisedData){ 48 | // console.log(promisedData); 49 | // this.setState(promisedData); 50 | // }); 51 | Q(getAppState()).then(function(promise){ 52 | console.log(2) 53 | console.log("this:",that) 54 | console.log(promise) 55 | // var data = {todos: promise} 56 | that.setState({todos: promise}) 57 | }) 58 | }, 59 | 60 | componentWillUnmount: function(){ 61 | AppStore.removeChangeListener(this._onChange); 62 | }, 63 | 64 | handleClick: function(){ 65 | AppActions.exampleAction('Data from View'); 66 | }, 67 | 68 | render: function(){ 69 | return ( 70 |
    71 | 72 |
    73 | ) 74 | } 75 | }) 76 | 77 | module.exports = APP; 78 | -------------------------------------------------------------------------------- /templates/app-koa/client/app/src/components/Item.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | 'use strict'; 5 | 6 | 7 | var React = require('react'); 8 | 9 | var AppStore = require('../stores/AppStore'); 10 | var AppActions = require('../actions/AppActions'); 11 | 12 | 13 | var ITEM = React.createClass({ 14 | 15 | handleClick: function(e) { 16 | e.preventDefault(); 17 | AppActions.removeItemAction(this.props.item.id); 18 | }, 19 | 20 | render: function(){ 21 | return ( 22 |
  • 23 | {this.props.item.item} 24 | x 25 |
  • 26 | ) 27 | } 28 | }) 29 | 30 | module.exports = ITEM; -------------------------------------------------------------------------------- /templates/app-koa/client/app/src/components/Login.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | 'use strict'; 5 | 6 | var React = require('react'); 7 | 8 | var AppStore = require('../stores/AppStore'); 9 | var AppActions = require('../actions/AppActions'); 10 | 11 | var LOGIN = React.createClass({ 12 | 13 | handleSubmit: function(e){ 14 | e.preventDefault(); 15 | 16 | var email = this.refs.email.getDOMNode().value.trim(); 17 | var password = this.refs.password.getDOMNode().value.trim(); 18 | 19 | var userData = { 20 | email: email, 21 | password: password 22 | }; 23 | 24 | $.ajax({ 25 | type: 'POST', 26 | data: JSON.stringify(userData), 27 | contentType: 'application/json', 28 | url: '/auth/local/', 29 | success: function(item) { 30 | console.log('Successfully added: ', item); 31 | }, 32 | failure: function(item) { 33 | console.log('Failed! OH NOOOO!'); 34 | } 35 | }); 36 | }, 37 | 38 | handleInput: function(e) { 39 | console.log("at handleInput in Login") 40 | }, 41 | 42 | render: function(){ 43 | return ( 44 |
    45 |
    46 |

    Login

    47 |
    48 |
    49 | 50 |
    51 | 52 |
    53 |
    54 |
    55 | 56 |
    57 | 58 |
    59 |
    60 |
    61 |
    62 |
    63 | 66 |
    67 |
    68 |
    69 |
    70 |
    71 | 72 |
    73 |
    74 |
    75 |
    76 |
    77 | ) 78 | } 79 | }) 80 | 81 | module.exports = LOGIN; 82 | -------------------------------------------------------------------------------- /templates/app-koa/client/app/src/components/NavBar.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | 'use strict'; 5 | 6 | var React = require('react'); 7 | 8 | var AppStore = require('../stores/AppStore'); 9 | var AppActions = require('../actions/AppActions'); 10 | 11 | var Router = require('react-router'); 12 | var Link = Router.Link; 13 | 14 | function getAppState(){ 15 | return AppStore.getData(); 16 | }; 17 | 18 | var NAV = React.createClass({ 19 | 20 | render: function(){ 21 | return ( 22 | 29 | ); 30 | } 31 | }) 32 | 33 | module.exports = NAV; -------------------------------------------------------------------------------- /templates/app-koa/client/app/src/components/ToDo.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 'use strict'; 4 | 5 | 6 | var React = require('react'); 7 | 8 | var AppStore = require('../stores/AppStore'); 9 | var AppActions = require('../actions/AppActions'); 10 | var ITEM = require('./Item.jsx'); 11 | 12 | 13 | var TODO = React.createClass({ 14 | 15 | handleClick: function() { 16 | 17 | var task = this.refs.todo.getDOMNode().value.trim(); 18 | if (task != '') { 19 | AppActions.addItemAction(task); 20 | } 21 | this.refs.todo.getDOMNode().value = ''; 22 | }, 23 | 24 | handleInput: function(e) { 25 | if (e.nativeEvent.charCode === 13) { 26 | this.handleClick(); 27 | } 28 | }, 29 | 30 | render: function(){ 31 | var items = this.props.allTodos.map(function(item, i) { 32 | return 33 | }); 34 | return ( 35 |
    36 |
    37 |

    Welcome To The React-Flux-Fullstack Slush Generator

    38 |
    39 | 40 | 41 | 42 | 43 |
    44 |

    Features:

    45 |
      46 | {items} 47 |
    48 |
    49 |
    50 | ); 51 | } 52 | }) 53 | 54 | module.exports = TODO; -------------------------------------------------------------------------------- /templates/app-koa/client/app/src/constants/AppConstants.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var keyMirror = require('react/lib/keyMirror'); 4 | 5 | module.exports = keyMirror({ 6 | POPULATE: null, 7 | ADD: null, 8 | REMOVE: null 9 | }); 10 | -------------------------------------------------------------------------------- /templates/app-koa/client/app/src/dispatchers/AppDispatcher.js: -------------------------------------------------------------------------------- 1 | /*ReadMe: 2 | Dispatchers operates as the central hub of data flow in a Flux application. 3 | They organize events into a single flow and ensure dependencies are taken care 4 | of at the right time before events continue to process. Dispatchers act as a 5 | bridge function between user events/ web APIs and the stores so that the correct 6 | changes can be made. 7 | */ 8 | 'use strict'; 9 | var Dispatcher = require('flux').Dispatcher; 10 | var copyProperties = require('react/lib/copyProperties'); 11 | var AppDispatcher = copyProperties(new Dispatcher(), { 12 | 13 | /** 14 | * A bridge function between the views and the dispatcher, marking the action 15 | * as a view action. Another variant here could be handleServerAction. 16 | * @param {object} action The data coming from the view. 17 | */ 18 | handleViewAction: function(action) { 19 | return this.dispatch({ 20 | source: 'VIEW_ACTION', 21 | action: action 22 | }); 23 | } 24 | 25 | }); 26 | 27 | module.exports = AppDispatcher; 28 | -------------------------------------------------------------------------------- /templates/app-koa/client/app/src/preprocessor.js: -------------------------------------------------------------------------------- 1 | // preprocessor.js 2 | var ReactTools = require('react-tools'); 3 | module.exports = { 4 | process: function(src) { 5 | return ReactTools.transform(src); 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /templates/app-koa/client/app/src/tests/app-items-test.js: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | jest.dontMock('../components/Home.jsx'); 4 | jest.dontMock('../components/ToDo.jsx'); 5 | jest.dontMock('../components/Item.jsx'); 6 | 7 | jest.dontMock('../stores/AppStore'); 8 | jest.dontMock('../actions/AppActions'); 9 | jest.dontMock('../dispatchers/AppDispatcher'); 10 | jest.dontMock('../constants/AppConstants'); 11 | jest.dontMock('react/lib/merge'); 12 | 13 | 14 | describe('Main App Items', function() { 15 | 16 | var React = require('react/addons'); 17 | var App = require('../components/Home.jsx'); 18 | var TestUtils = React.addons.TestUtils; 19 | 20 | it('can add new item', function() { 21 | 22 | var testApp = TestUtils.renderIntoDocument(); 23 | var input = TestUtils.findRenderedDOMComponentWithClass(testApp, "form-control"); 24 | var button = TestUtils.findRenderedDOMComponentWithClass(testApp, "btn"); 25 | 26 | input.getDOMNode().value = "Test"; 27 | TestUtils.Simulate.click(button.getDOMNode()); 28 | expect(input.getDOMNode().value).toEqual(''); 29 | 30 | }); 31 | }); -------------------------------------------------------------------------------- /templates/app-koa/client/app/src/tests/app-test.js: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | jest.dontMock('../components/Home.jsx'); 4 | jest.dontMock('../components/ToDo.jsx'); 5 | jest.dontMock('../components/Item.jsx'); 6 | jest.dontMock('../stores/AppStore.js'); 7 | 8 | describe('Main App', function() { 9 | it('has render method', function() { 10 | 11 | var React = require('react/addons'); 12 | var App = require('../components/Home.jsx'); 13 | var TestUtils = React.addons.TestUtils; 14 | 15 | var testApp = TestUtils.renderIntoDocument(); 16 | 17 | expect(TestUtils.isCompositeComponent(testApp)).toEqual(true); 18 | expect(testApp.render).toBeDefined(); 19 | 20 | // var items = TestUtils.scryRenderedDOMComponentsWithClass(testApp, "list-group-item"); 21 | 22 | // for (var i = 0; i < items.length; i++) { 23 | // console.log(items[i].getDOMNode().textContent); 24 | // } 25 | }); 26 | }); -------------------------------------------------------------------------------- /templates/app-koa/client/app/styles/main.css: -------------------------------------------------------------------------------- 1 | .navbar { 2 | margin-bottom: 0; 3 | } 4 | 5 | .list-group { 6 | margin-top: 25px; 7 | margin-bottom: 150px; 8 | } 9 | 10 | .banner { 11 | text-align: center; 12 | background-color: #D184B7; 13 | padding-top: 30px; 14 | padding-bottom: 30px; 15 | } 16 | 17 | .login { 18 | margin-right: 25px; 19 | } 20 | 21 | .input-group { 22 | margin-top: 25px; 23 | } 24 | 25 | .footer { 26 | position: fixed; 27 | bottom: 0; 28 | width: 100%; 29 | /* Set the fixed height of the footer here */ 30 | height: 60px; 31 | background-color: #f5f5f5; 32 | } 33 | 34 | .container .text-muted { 35 | margin: 20px 0; 36 | } 37 | 38 | .control-label { 39 | margin-left: 20px; 40 | } 41 | 42 | .control-button { 43 | margin-left: 20px; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /templates/app-koa/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <?? humanizedAppName ?> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /templates/app-koa/gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | 5 | var browserify = require('browserify'); 6 | var reactify = require('reactify'); 7 | var source = require('vinyl-source-stream'); 8 | var connect = require('gulp-connect'); 9 | var nodemon = require('gulp-nodemon'); 10 | var jest = require('gulp-jest'); 11 | 12 | var paths = { 13 | app: ['./client/app/src/app.jsx'], 14 | js: ['./client/app/src/**/*.*'], 15 | }; 16 | 17 | gulp.task('browserify', function() { 18 | // Browserify/bundle the JS. 19 | browserify(paths.app) 20 | .transform(reactify) 21 | .bundle() 22 | .pipe(source('bundle.js')) 23 | .pipe(gulp.dest('./client/app/build/')); 24 | // .pipe(connect.reload()); 25 | }); 26 | 27 | // gulp.task('connect', function(){ 28 | // connect.server({ 29 | // root: 'templates/app', 30 | // livereload: true, 31 | // port: 8080 32 | // }); 33 | // }); 34 | 35 | gulp.task('nodemon', function(done){ 36 | nodemon({ script: './server/app.js', env: { 'NODE_ENV': 'development'}, nodeArgs: ['--harmony']}) 37 | .on('restart'); 38 | }) 39 | 40 | // Rerun the task when a file changes 41 | gulp.task('watch', function() { 42 | gulp.watch(paths.js, ['browserify']); 43 | }); 44 | 45 | // The default task (called when you run `gulp` from cli) 46 | gulp.task('default', ['serve']); 47 | 48 | gulp.task('serve', ['browserify', 'nodemon']); 49 | 50 | // Update Express is serving the right files 51 | // Browserify/Reactify/Serve 52 | 53 | gulp.task('jest', function () { 54 | return gulp.src('./client/app/src/').pipe(jest({ 55 | scriptPreprocessor: "./preprocessor.js", 56 | unmockedModulePathPatterns: [ 57 | "node_modules/react" 58 | ], 59 | testDirectoryName: "tests", 60 | testPathIgnorePatterns: [ 61 | "node_modules", 62 | "spec/support" 63 | ], 64 | moduleFileExtensions: [ 65 | "js", 66 | "json", 67 | "react" 68 | ] 69 | })); 70 | }); 71 | -------------------------------------------------------------------------------- /templates/app-koa/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "version": "0.0.1", 4 | "description": "A template for fullstack react applications using koa with gulp and browserify", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node --harmony server/app.js", 8 | "test": "gulp test" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/urban-boa/react-fullstack.git" 13 | }, 14 | "dependencies": { 15 | "composable-middleware": "^0.3.0", 16 | "compression": "~1.0.1", 17 | "cookie-parser": "~1.0.1", 18 | "ejs": "~0.8.4", 19 | "flux": "^2.0.1", 20 | "gulp-jest": "*", 21 | "gulp-nodemon": "^1.0.4", 22 | "jquery-browserify": "^1.8.1", 23 | "jsonwebtoken": "^0.3.0", 24 | "koa": "^0.13.0", 25 | "koa-bodyparser": "^1.1.0", 26 | "koa-compress": "^1.0.8", 27 | "koa-error-handler": "0.0.0", 28 | "koa-favicon": "^1.2.0", 29 | "koa-livereload": "^0.1.23", 30 | "koa-logger": "^1.2.2", 31 | "koa-methodoverride": "^0.3.1", 32 | "koa-passport": "^1.1.2", 33 | "koa-router": "^3.5.0", 34 | "koa-session": "^2.0.0", 35 | "koa-session-mongo": "^1.0.1", 36 | "koa-static": "^1.4.7", 37 | "koa-views": "^2.1.0", 38 | "lodash": "~2.4.1", 39 | "method-override": "~1.0.0", 40 | "mongoose": "~3.8.8", 41 | "q": "^1.0.1", 42 | "react": "^0.11.2", 43 | "react-router": "^0.9.4", 44 | "react-tools": "^0.11.2" 45 | }, 46 | "author": "Uncanny Piranha", 47 | "license": "ISC", 48 | "bugs": { 49 | "url": "https://github.com/react-fullstack/slush-react-fullstack/issues" 50 | }, 51 | "engines": { 52 | "node": ">=0.11.9" 53 | }, 54 | "homepage": "https://github.com/react-fullstack/slush-react-fullstack/", 55 | "devDependencies": { 56 | "browserify": "^5.12.0", 57 | "gulp": "^3.8.8", 58 | "gulp-connect": "^2.0.6", 59 | "jest-cli": "^0.1.18", 60 | "reactify": "^0.14.0", 61 | "vinyl-source-stream": "^1.0.0" 62 | }, 63 | "jest": { 64 | "scriptPreprocessor": "/preprocessor.js", 65 | "unmockedModulePathPatterns": [ 66 | "/node_modules/react" 67 | ] 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /templates/app-koa/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 | -------------------------------------------------------------------------------- /templates/app-koa/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 | -------------------------------------------------------------------------------- /templates/app-koa/server/api/thing/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var controller = require('./thing.controller'); 4 | var router = require('koa-router'); 5 | 6 | const thingRouter = new router(); 7 | 8 | thingRouter.get('/api/things/', controller.index); 9 | thingRouter.get('/api/things/:id', controller.show); 10 | thingRouter.post('/api/things/', controller.create); 11 | thingRouter.put('/api/things/:id', controller.update); 12 | thingRouter.patch('/api/things/:id', controller.update); 13 | thingRouter.delete('/api/things/:id', controller.destroy); 14 | 15 | module.exports = thingRouter; 16 | 17 | -------------------------------------------------------------------------------- /templates/app-koa/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 *() { 17 | try { 18 | var things = yield Thing.find().exec(); 19 | } catch(err) { 20 | yield handleError(err); 21 | } 22 | this.response.status = 200; 23 | this.response.body = things; 24 | }; 25 | 26 | // Get a single thing 27 | exports.show = function *() { 28 | try { 29 | var thing = yield Thing.findById(this.params.id).exec(); 30 | } catch(err) { 31 | yield handleError(err); 32 | } 33 | if(!thing){ 34 | this.response.status = 404; 35 | } else { 36 | this.response.body = thing; 37 | } 38 | }; 39 | 40 | // Creates a new thing in the DB. 41 | exports.create = function *() { 42 | try { 43 | var thing = yield Thing.create(this.request.body); 44 | } catch(err) { 45 | yield handleError(err); 46 | } 47 | this.response.status = 201; 48 | this.response.body = thing; 49 | }; 50 | 51 | // Updates an existing thing in the DB. 52 | exports.update = function *() { 53 | if(this.request.body._id) { 54 | delete this.request.body._id; 55 | } 56 | 57 | try { 58 | var thing = yield Thing.findById(this.params.id).exec(); 59 | } catch(err) { 60 | yield handleError(err); 61 | } 62 | 63 | if(! thing){ 64 | this.response.status = 404; 65 | } else { 66 | var updated = _.merge(thing, this.request.body); 67 | try { 68 | yield updated.save(); 69 | } catch (err) { 70 | handleError(err); 71 | } 72 | this.response.status = 200; 73 | this.response.body = thing; 74 | } 75 | }; 76 | 77 | // Deletes a thing from the DB. 78 | exports.destroy = function *() { 79 | try { 80 | var thing = yield Thing.findById(this.params.id).exec(); 81 | } catch (err) { 82 | handleError(err); 83 | } 84 | 85 | if(!thing){ 86 | this.response.status = 404; 87 | } else { 88 | try { 89 | thing.remove(); //need to yield here? 90 | } catch(err) { 91 | handleError(err); 92 | } 93 | this.response.status = 204; 94 | } 95 | }; 96 | 97 | function *handleError(err) { 98 | this.throw(500, err); 99 | } -------------------------------------------------------------------------------- /templates/app-koa/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 | item: String, 8 | info: String, 9 | active: Boolean 10 | }); 11 | 12 | module.exports = mongoose.model('Thing', ThingSchema); -------------------------------------------------------------------------------- /templates/app-koa/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 | -------------------------------------------------------------------------------- /templates/app-koa/server/api/user/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var controller = require('./user.controller'); 4 | var auth = require('../../auth/auth.service'); 5 | var router = require('koa-router'); 6 | 7 | const userRouter = new router(); 8 | 9 | userRouter.get('/api/users/', auth.isAuthenticated, controller.index); 10 | userRouter.delete('/api/users/:id', auth.isAuthenticated, controller.destroy); 11 | userRouter.get('/api/users/me', auth.isAuthenticated, controller.me); 12 | userRouter.put('/api/users/:id/password', auth.isAuthenticated, controller.changePassword); 13 | userRouter.get('/api/users/:id', auth.isAuthenticated, controller.show); 14 | userRouter.post('/api/users/', controller.create); 15 | 16 | module.exports = userRouter; 17 | -------------------------------------------------------------------------------- /templates/app-koa/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 | -------------------------------------------------------------------------------- /templates/app-koa/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 koa = require('koa'); 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 = koa(); 22 | 23 | require('./config/express')(app); 24 | require('./routes')(app); 25 | var server = require('http').createServer(app.callback()); 26 | 27 | // Start server 28 | server.listen(config.port, config.ip, function () { 29 | console.log('Koa server listening on %d, in %s mode', config.port, process.env.NODE_ENV); 30 | }); 31 | 32 | // Expose app 33 | exports = module.exports = app; -------------------------------------------------------------------------------- /templates/app-koa/server/auth/auth.service.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var config = require('../config/environment'); 4 | var jwt = require('jsonwebtoken'); 5 | var User = require('../api/user/user.model'); 6 | 7 | /** 8 | * Attaches the user object to the request if authenticated 9 | * Otherwise returns 403 10 | */ 11 | function *isAuthenticated(next) { 12 | var token; 13 | if(this.request.query){ 14 | token = this.request.query.access_token; 15 | } 16 | try { 17 | var userId = jwt.decode(token); 18 | } catch (err){ 19 | this.throw(401) 20 | } 21 | try { 22 | var user = yield User.findById(userId._id).exec(); 23 | } catch (err){ 24 | yield next(err); 25 | } 26 | if(!user){ 27 | this.response.status = 401; 28 | } else { 29 | this.request.user = user; 30 | } 31 | yield next 32 | } 33 | 34 | /** 35 | * Checks if the user role meets the minimum requirements of the route 36 | */ 37 | function *hasRole(roleRequired) { 38 | if (!roleRequired) { 39 | this.throw('Required role needs to be set'); 40 | } 41 | 42 | yield isAuthenticated(); 43 | if(config.userRoles.indexOf(this.request.user.role) < config.userRoles.indexOf(roleRequired)){ 44 | this.response.status = 403; 45 | } else { 46 | yield next; 47 | } 48 | } 49 | 50 | /** 51 | * Returns a jwt token signed by the app secret 52 | */ 53 | function signToken(id) { 54 | return jwt.sign({ _id: id }, config.secrets.session, { expiresInMinutes: 60*5 }); 55 | } 56 | 57 | /** 58 | * Set token cookie directly for oAuth strategies 59 | */ 60 | function *setTokenCookie() { 61 | if (!this.request.user) { 62 | this.response.status = 404; 63 | this.response.body = {message: 'Something went wrong, please try again.'}; 64 | } else { 65 | //Might not be working correctly 66 | var token = signToken(this.request.user._id, this.request.user.role); 67 | this.response.cookie('token', JSON.stringify(token)); 68 | this.response.redirect('/'); 69 | } 70 | } 71 | 72 | exports.isAuthenticated = isAuthenticated; 73 | exports.hasRole = hasRole; 74 | exports.signToken = signToken; 75 | exports.setTokenCookie = setTokenCookie; -------------------------------------------------------------------------------- /templates/app-koa/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; -------------------------------------------------------------------------------- /templates/app-koa/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 | }; -------------------------------------------------------------------------------- /templates/app-koa/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; -------------------------------------------------------------------------------- /templates/app-koa/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 | -------------------------------------------------------------------------------- /templates/app-koa/server/auth/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var config = require('../config/environment'); 4 | var User = require('../api/user/user.model'); 5 | 6 | var router = require('koa-router'); 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 | const authRouter = new router(); 15 | authRouter.post('/auth/local/', require('./local/index')); 16 | 17 | // authRouter.use('/facebook', require('./facebook')); 18 | // authRouter.use('/twitter', require('./twitter')); 19 | // authRouter.use('/google', require('./google')); 20 | 21 | module.exports = authRouter; 22 | -------------------------------------------------------------------------------- /templates/app-koa/server/auth/local/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var auth = require('../auth.service'); 4 | var User = require('../../api/user/user.model'); 5 | 6 | module.exports = function *() { 7 | var email = this.request.body.email; 8 | var password = this.request.body.password; 9 | try { 10 | var user = yield User.findOne({email: email}).exec(); 11 | } catch (err){ 12 | this.throw(500); 13 | } 14 | if(!user){ 15 | this.throw(401, 'Incorrect email or password'); 16 | } 17 | if(!user.authenticate(password)){ 18 | this.throw(401, 'Incorrect email or password'); 19 | } 20 | var token = auth.signToken(user._id, user.role); 21 | this.response.body = {token: token}; 22 | }; 23 | -------------------------------------------------------------------------------- /templates/app-koa/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 | }; -------------------------------------------------------------------------------- /templates/app-koa/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; -------------------------------------------------------------------------------- /templates/app-koa/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 | }; -------------------------------------------------------------------------------- /templates/app-koa/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 | -------------------------------------------------------------------------------- /templates/app-koa/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/slushy-dev' 9 | }, 10 | 11 | seedDB: true 12 | }; 13 | -------------------------------------------------------------------------------- /templates/app-koa/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: 'slushy-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') || {}); -------------------------------------------------------------------------------- /templates/app-koa/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/slushy' 22 | } 23 | }; -------------------------------------------------------------------------------- /templates/app-koa/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/slushy-test' 9 | } 10 | }; -------------------------------------------------------------------------------- /templates/app-koa/server/config/express.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Express configuration 3 | */ 4 | 5 | 'use strict'; 6 | 7 | var serve = require('koa-static'); 8 | var session = require('koa-session'); 9 | var mongoStore = require('koa-session-mongo'); 10 | var bodyParser = require('koa-bodyparser'); 11 | var passport = require('koa-passport'); 12 | var views = require('koa-views'); 13 | var methodOverride = require('koa-methodoverride'); 14 | var compression = require('koa-compress'); 15 | var logger = require('koa-logger'); 16 | var errorHandler = require('koa-error-handler'); 17 | var favicon = require('koa-favicon'); 18 | var path = require('path'); 19 | var config = require('./environment'); 20 | var mongoose = require('mongoose'); 21 | 22 | module.exports = function(app) { 23 | // var env = app.get('env'); 24 | 25 | app.use(views(config.root+'/server/views', { 26 | default: 'html' 27 | })); 28 | app.use(compression()); 29 | app.use(bodyParser()); 30 | app.use(methodOverride()); 31 | app.use(passport.initialize()); 32 | app.use(passport.session()); 33 | 34 | app.keys = [config.secrets.session]; 35 | app.use(session({ 36 | store: mongoStore.create({ 37 | mongoose: mongoose.connection 38 | }) 39 | })); 40 | 41 | // if ('production' === env) { 42 | // app.use(favicon(path.join(config.root, 'public', 'favicon.ico'))); 43 | // app.use(serve(path.join(config.root, 'public'))); 44 | // app.set('appPath', config.root + '/public'); 45 | // app.use(morgan('dev')); 46 | // } 47 | 48 | // if ('development' === env || 'test' === env) { 49 | // app.use(require('connect-livereload')()); 50 | // app.use(serve(path.join(config.root, '.tmp'))); 51 | // app.use(serve(path.join(config.root, 'client'))); 52 | // app.set('appPath', 'client'); 53 | // app.use(morgan('dev')); 54 | // app.use(errorHandler()); // Error handler - has to be last 55 | // } 56 | 57 | // app.use(favicon(path.join(config.root, 'public', 'favicon.ico'))); 58 | app.use(require('koa-livereload')()); 59 | app.use(serve(path.join(config.root, '.tmp'))); 60 | app.use(serve(path.join(config.root, 'client'))); 61 | app.use(logger()); 62 | errorHandler(app); // Error handler - has to be last 63 | }; 64 | -------------------------------------------------------------------------------- /templates/app-koa/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: "slushy-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 | -------------------------------------------------------------------------------- /templates/app-koa/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: 'slushy-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 | -------------------------------------------------------------------------------- /templates/app-koa/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 | item : '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 | item : 'Server and Client integration', 17 | info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' 18 | }, { 19 | item : '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 | item : 'Modular Structure', 23 | info : 'Best practice client and server structures allow for more code reusability and maximum scalability' 24 | }, { 25 | item : '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 | item : '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 | }); 50 | -------------------------------------------------------------------------------- /templates/app-koa/server/routes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Main application routes 3 | */ 4 | 5 | 'use strict'; 6 | 7 | var errors = require('./components/errors'); 8 | var router = require('koa-router'); 9 | var thingRouter = require('./api/thing/index'); 10 | var userRouter = require('./api/user/index'); 11 | var authRouter = require('./auth/index'); 12 | 13 | module.exports = function(app) { 14 | 15 | app.use(router(app)); 16 | 17 | app.use(thingRouter.middleware()); 18 | app.use(userRouter.middleware()); 19 | app.use(authRouter.middleware()); 20 | 21 | //MIGHT NEED TO SWITCH TO THIS 22 | // All undefined asset or api routes should return a 404 23 | app.all('/:url(api|auth|components|app|bower_components|assets)/*', errors[404]); 24 | 25 | // All other routes should redirect to the index.html 26 | app.all('/*', function *(next){ 27 | res.sendfile(app.get('appPath') + '/index.html'); 28 | }); 29 | }; 30 | -------------------------------------------------------------------------------- /templates/app/README.md: -------------------------------------------------------------------------------- 1 | ## ## 2 | 3 | 4 | 5 | ### by ### 6 | 7 | 8 | The MEAN stack just got MEANER. Mongo, Express, Angular, Node et React. 9 | 10 | A Full Stack Application built with the React-Fullstack Slush Generator! 11 | 12 | -------------------------------------------------------------------------------- /templates/app/client/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <?? humanizedAppName ?> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /templates/app/client/app/src/actions/AppActions.js: -------------------------------------------------------------------------------- 1 | /*ReadMe: 2 | Actions are data packaged from user interactions or web APIs. 3 | It is an object literal containing the new fields of data and 4 | specific action types. Actions are sent to the dispatcher before 5 | the store. 6 | */ 7 | 8 | 'use strict'; 9 | var AppDispatcher = require('../dispatchers/AppDispatcher'); 10 | var AppConstants = require('../constants/AppConstants'); 11 | 12 | var AppActions = { 13 | populateAction: function() { 14 | return AppDispatcher.handleViewAction({ 15 | actionType: AppConstants.POPULATE 16 | }); 17 | }, 18 | addItemAction: function(text) { 19 | AppDispatcher.handleViewAction({ 20 | actionType: AppConstants.ADD, 21 | text: text 22 | }); 23 | }, 24 | removeItemAction: function(id) { 25 | AppDispatcher.handleViewAction({ 26 | actionType: AppConstants.REMOVE, 27 | id: id 28 | }); 29 | } 30 | }; 31 | 32 | module.exports = AppActions; 33 | -------------------------------------------------------------------------------- /templates/app/client/app/src/app.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | /*ReadMe: 4 | Components are where you will be designing your view in the 5 | render section. This code is written in jsx; 6 | is used below for changing views for routing. 7 | 8 | To add components, you can run react-fullstack:component, and it will create a new component file in the folder. 9 | */ 10 | 11 | 12 | 'use strict'; 13 | 14 | var React = require('react'); 15 | 16 | var LOGIN = require('./components/Login.jsx'); 17 | var HOME = require('./components/Home.jsx'); 18 | var SIGNUP = require('./components/Signup.jsx'); 19 | var NAV = require('./components/NavBar.jsx'); 20 | var BANNER = require('./components/Banner.jsx'); 21 | var FOOTER = require('./components/Footer.jsx'); 22 | 23 | var Router = require('react-router'); 24 | var Route = Router.Route; 25 | var Routes = Router.Routes; 26 | var NotFoundRoute = Router.NotFoundRoute; 27 | var DefaultRoute = Router.DefaultRoute; 28 | var Link = Router.Link; 29 | 30 | 31 | var APP = React.createClass({ 32 | render: function(){ 33 | return ( 34 |
    35 |
    40 | ) 41 | } 42 | }) 43 | 44 | var routes = ( 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | ); 54 | 55 | module.exports = React.renderComponent(routes, document.body); 56 | -------------------------------------------------------------------------------- /templates/app/client/app/src/components/Banner.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | var React = require('react'); 5 | 6 | var BANNER = React.createClass({ 7 | 8 | 9 | render: function(){ 10 | return ( 11 |
    12 | 13 |
    14 | ) 15 | } 16 | }) 17 | 18 | module.exports = BANNER; -------------------------------------------------------------------------------- /templates/app/client/app/src/components/Footer.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | var React = require('react'); 5 | 6 | var FOOTER = React.createClass({ 7 | 8 | 9 | render: function(){ 10 | return ( 11 |
    12 |
    13 |

    14 | Fork us on Github! 15 |

    16 |
    17 |
    18 | ) 19 | } 20 | }) 21 | 22 | module.exports = FOOTER; 23 | -------------------------------------------------------------------------------- /templates/app/client/app/src/components/Home.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | 'use strict'; 5 | 6 | var React = require('react'); 7 | 8 | var AppStore = require('../stores/AppStore'); 9 | var AppActions = require('../actions/AppActions'); 10 | 11 | var NAV = require('./NavBar.jsx'); 12 | var BANNER = require('./Banner.jsx'); 13 | var TODO = require('./ToDo.jsx'); 14 | 15 | var Q = require('q'); 16 | 17 | function getAppState(){ 18 | return AppStore.getData(); 19 | }; 20 | 21 | function getInitialAppState(){ 22 | return AppStore.getInitialData(); 23 | } 24 | 25 | var APP = React.createClass({ 26 | getInitialState: function(){ 27 | return getInitialAppState(); 28 | // return getAppState(); 29 | // return null; 30 | }, 31 | 32 | _onChange: function(){ 33 | // this.setState(getAppState()); 34 | var that = this; 35 | Q(getAppState()).then(function(promise){ 36 | console.log('change') 37 | console.log(promise) 38 | that.setState({todos: promise}) 39 | }) 40 | 41 | }, 42 | 43 | componentDidMount: function(){ 44 | var that = this; 45 | console.log(1) 46 | AppStore.addChangeListener(this._onChange); 47 | // Q(AppActions.populateAction()).then(function(promisedData){ 48 | // console.log(promisedData); 49 | // this.setState(promisedData); 50 | // }); 51 | Q(getAppState()).then(function(promise){ 52 | console.log(2) 53 | console.log("this:",that) 54 | console.log(promise) 55 | // var data = {todos: promise} 56 | that.setState({todos: promise}) 57 | }) 58 | }, 59 | 60 | componentWillUnmount: function(){ 61 | AppStore.removeChangeListener(this._onChange); 62 | }, 63 | 64 | handleClick: function(){ 65 | AppActions.exampleAction('Data from View'); 66 | }, 67 | 68 | render: function(){ 69 | return ( 70 |
    71 | 72 |
    73 | ) 74 | } 75 | }) 76 | 77 | module.exports = APP; 78 | -------------------------------------------------------------------------------- /templates/app/client/app/src/components/Item.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | 'use strict'; 5 | 6 | 7 | var React = require('react'); 8 | 9 | var AppStore = require('../stores/AppStore'); 10 | var AppActions = require('../actions/AppActions'); 11 | 12 | 13 | var ITEM = React.createClass({ 14 | 15 | handleClick: function(e) { 16 | e.preventDefault(); 17 | AppActions.removeItemAction(this.props.item.id); 18 | }, 19 | 20 | render: function(){ 21 | return ( 22 |
  • 23 | {this.props.item.item} 24 | x 25 |
  • 26 | ) 27 | } 28 | }) 29 | 30 | module.exports = ITEM; -------------------------------------------------------------------------------- /templates/app/client/app/src/components/Login.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | 'use strict'; 5 | 6 | var React = require('react'); 7 | 8 | var AppStore = require('../stores/AppStore'); 9 | var AppActions = require('../actions/AppActions'); 10 | 11 | var LOGIN = React.createClass({ 12 | 13 | handleSubmit: function(e){ 14 | e.preventDefault(); 15 | 16 | var email = this.refs.email.getDOMNode().value.trim(); 17 | var password = this.refs.password.getDOMNode().value.trim(); 18 | 19 | var userData = { 20 | email: email, 21 | password: password 22 | }; 23 | 24 | $.ajax({ 25 | type: 'GET', 26 | data: JSON.stringify(userData), 27 | contentType: 'application/json', 28 | url: '/api/users/', 29 | success: function(item) { 30 | console.log('Successfully added: ', item); 31 | }, 32 | failure: function(item) { 33 | console.log('Failed! OH NOOOO!'); 34 | } 35 | }); 36 | }, 37 | 38 | handleInput: function(e) { 39 | console.log("at handleInput in Login") 40 | }, 41 | 42 | render: function(){ 43 | return ( 44 |
    45 |
    46 |

    Login

    47 |
    48 |
    49 | 50 |
    51 | 52 |
    53 |
    54 |
    55 | 56 |
    57 | 58 |
    59 |
    60 |
    61 |
    62 |
    63 | 66 |
    67 |
    68 |
    69 |
    70 |
    71 | 72 |
    73 |
    74 |
    75 |
    76 |
    77 | ) 78 | } 79 | }) 80 | 81 | module.exports = LOGIN; 82 | -------------------------------------------------------------------------------- /templates/app/client/app/src/components/NavBar.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 4 | 'use strict'; 5 | 6 | var React = require('react'); 7 | 8 | var AppStore = require('../stores/AppStore'); 9 | var AppActions = require('../actions/AppActions'); 10 | 11 | var Router = require('react-router'); 12 | var Link = Router.Link; 13 | 14 | function getAppState(){ 15 | return AppStore.getData(); 16 | }; 17 | 18 | var NAV = React.createClass({ 19 | 20 | render: function(){ 21 | return ( 22 | 29 | ); 30 | } 31 | }) 32 | 33 | module.exports = NAV; -------------------------------------------------------------------------------- /templates/app/client/app/src/components/ToDo.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 'use strict'; 4 | 5 | 6 | var React = require('react'); 7 | 8 | var AppStore = require('../stores/AppStore'); 9 | var AppActions = require('../actions/AppActions'); 10 | var ITEM = require('./Item.jsx'); 11 | 12 | 13 | var TODO = React.createClass({ 14 | 15 | handleClick: function() { 16 | 17 | var task = this.refs.todo.getDOMNode().value.trim(); 18 | if (task != '') { 19 | AppActions.addItemAction(task); 20 | } 21 | this.refs.todo.getDOMNode().value = ''; 22 | }, 23 | 24 | handleInput: function(e) { 25 | if (e.nativeEvent.charCode === 13) { 26 | this.handleClick(); 27 | } 28 | }, 29 | 30 | render: function(){ 31 | var items = this.props.allTodos.map(function(item, i) { 32 | return 33 | }); 34 | return ( 35 |
    36 |
    37 |

    Welcome To The React-Flux-Fullstack Slush Generator

    38 |
    39 | 40 | 41 | 42 | 43 |
    44 |

    Features:

    45 |
      46 | {items} 47 |
    48 |
    49 |
    50 | ); 51 | } 52 | }) 53 | 54 | module.exports = TODO; -------------------------------------------------------------------------------- /templates/app/client/app/src/constants/AppConstants.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var keyMirror = require('react/lib/keyMirror'); 4 | 5 | module.exports = keyMirror({ 6 | POPULATE: null, 7 | ADD: null, 8 | REMOVE: null 9 | }); 10 | -------------------------------------------------------------------------------- /templates/app/client/app/src/dispatchers/AppDispatcher.js: -------------------------------------------------------------------------------- 1 | /*ReadMe: 2 | Dispatchers operates as the central hub of data flow in a Flux application. 3 | They organize events into a single flow and ensure dependencies are taken care 4 | of at the right time before events continue to process. Dispatchers act as a 5 | bridge function between user events/ web APIs and the stores so that the correct 6 | changes can be made. 7 | */ 8 | 'use strict'; 9 | var Dispatcher = require('flux').Dispatcher; 10 | var copyProperties = require('react/lib/copyProperties'); 11 | var AppDispatcher = copyProperties(new Dispatcher(), { 12 | 13 | /** 14 | * A bridge function between the views and the dispatcher, marking the action 15 | * as a view action. Another variant here could be handleServerAction. 16 | * @param {object} action The data coming from the view. 17 | */ 18 | handleViewAction: function(action) { 19 | return this.dispatch({ 20 | source: 'VIEW_ACTION', 21 | action: action 22 | }); 23 | } 24 | 25 | }); 26 | 27 | module.exports = AppDispatcher; 28 | -------------------------------------------------------------------------------- /templates/app/client/app/src/preprocessor.js: -------------------------------------------------------------------------------- 1 | // preprocessor.js 2 | var ReactTools = require('react-tools'); 3 | module.exports = { 4 | process: function(src) { 5 | return ReactTools.transform(src); 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /templates/app/client/app/src/tests/app-items-test.js: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | jest.dontMock('../components/Home.jsx'); 4 | jest.dontMock('../components/ToDo.jsx'); 5 | jest.dontMock('../components/Item.jsx'); 6 | 7 | jest.dontMock('../stores/AppStore'); 8 | jest.dontMock('../actions/AppActions'); 9 | jest.dontMock('../dispatchers/AppDispatcher'); 10 | jest.dontMock('../constants/AppConstants'); 11 | jest.dontMock('react/lib/merge'); 12 | 13 | 14 | describe('Main App Items', function() { 15 | 16 | var React = require('react/addons'); 17 | var App = require('../components/Home.jsx'); 18 | var TestUtils = React.addons.TestUtils; 19 | 20 | it('can add new item', function() { 21 | 22 | var testApp = TestUtils.renderIntoDocument(); 23 | var input = TestUtils.findRenderedDOMComponentWithClass(testApp, "form-control"); 24 | var button = TestUtils.findRenderedDOMComponentWithClass(testApp, "btn"); 25 | 26 | input.getDOMNode().value = "Test"; 27 | TestUtils.Simulate.click(button.getDOMNode()); 28 | expect(input.getDOMNode().value).toEqual(''); 29 | 30 | }); 31 | }); -------------------------------------------------------------------------------- /templates/app/client/app/src/tests/app-test.js: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | jest.dontMock('../components/Home.jsx'); 4 | jest.dontMock('../components/ToDo.jsx'); 5 | jest.dontMock('../components/Item.jsx'); 6 | jest.dontMock('../stores/AppStore.js'); 7 | 8 | describe('Main App', function() { 9 | it('has render method', function() { 10 | 11 | var React = require('react/addons'); 12 | var App = require('../components/Home.jsx'); 13 | var TestUtils = React.addons.TestUtils; 14 | 15 | var testApp = TestUtils.renderIntoDocument(); 16 | 17 | expect(TestUtils.isCompositeComponent(testApp)).toEqual(true); 18 | expect(testApp.render).toBeDefined(); 19 | 20 | // var items = TestUtils.scryRenderedDOMComponentsWithClass(testApp, "list-group-item"); 21 | 22 | // for (var i = 0; i < items.length; i++) { 23 | // console.log(items[i].getDOMNode().textContent); 24 | // } 25 | }); 26 | }); -------------------------------------------------------------------------------- /templates/app/client/app/styles/main.css: -------------------------------------------------------------------------------- 1 | .navbar { 2 | margin-bottom: 0; 3 | } 4 | 5 | .list-group { 6 | margin-top: 25px; 7 | margin-bottom: 150px; 8 | } 9 | 10 | .banner { 11 | text-align: center; 12 | background-color: #D184B7; 13 | padding-top: 30px; 14 | padding-bottom: 30px; 15 | } 16 | 17 | .login { 18 | margin-right: 25px; 19 | } 20 | 21 | .input-group { 22 | margin-top: 25px; 23 | } 24 | 25 | .footer { 26 | position: fixed; 27 | bottom: 0; 28 | width: 100%; 29 | /* Set the fixed height of the footer here */ 30 | height: 60px; 31 | background-color: #f5f5f5; 32 | } 33 | 34 | .container .text-muted { 35 | margin: 20px 0; 36 | } 37 | 38 | .control-label { 39 | margin-left: 20px; 40 | } 41 | 42 | .control-button { 43 | margin-left: 20px; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /templates/app/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <?? humanizedAppName ?> 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /templates/app/gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var gulp = require('gulp'); 4 | 5 | var browserify = require('browserify'); 6 | var reactify = require('reactify'); 7 | var source = require('vinyl-source-stream'); 8 | var connect = require('gulp-connect'); 9 | var nodemon = require('gulp-nodemon'); 10 | var jest = require('gulp-jest'); 11 | 12 | var paths = { 13 | app: ['./client/app/src/app.jsx'], 14 | js: ['./client/app/src/**/*.*'], 15 | }; 16 | 17 | gulp.task('browserify', function() { 18 | // Browserify/bundle the JS. 19 | browserify(paths.app) 20 | .transform(reactify) 21 | .bundle() 22 | .pipe(source('bundle.js')) 23 | .pipe(gulp.dest('./client/app/build/')); 24 | // .pipe(connect.reload()); 25 | }); 26 | 27 | // gulp.task('connect', function(){ 28 | // connect.server({ 29 | // root: 'templates/app', 30 | // livereload: true, 31 | // port: 8080 32 | // }); 33 | // }); 34 | 35 | gulp.task('nodemon', function(done){ 36 | nodemon({ script: './server/app.js', env: { 'NODE_ENV': 'development'}}) 37 | .on('restart'); 38 | }); 39 | 40 | // Rerun the task when a file changes 41 | gulp.task('watch', function() { 42 | gulp.watch(paths.js, ['browserify']); 43 | }); 44 | 45 | // The default task (called when you run `gulp` from cli) 46 | gulp.task('default', ['serve']); 47 | 48 | gulp.task('serve', ['browserify', 'nodemon']); 49 | 50 | // Update Express is serving the right files 51 | // Browserify/Reactify/Serve 52 | 53 | gulp.task('jest', function () { 54 | return gulp.src('./client/app/src/').pipe(jest({ 55 | scriptPreprocessor: "./preprocessor.js", 56 | unmockedModulePathPatterns: [ 57 | "node_modules/react" 58 | ], 59 | testDirectoryName: "tests", 60 | testPathIgnorePatterns: [ 61 | "node_modules", 62 | "spec/support" 63 | ], 64 | moduleFileExtensions: [ 65 | "js", 66 | "json", 67 | "react" 68 | ] 69 | })); 70 | }); 71 | -------------------------------------------------------------------------------- /templates/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "version": "0.0.1", 4 | "description": "A template for fullstack react applications with gulp and browserify", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node server/app.js", 8 | "test": "gulp test" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/urban-boa/react-fullstack.git" 13 | }, 14 | "dependencies": { 15 | "body-parser": "~1.5.0", 16 | "composable-middleware": "^0.3.0", 17 | "compression": "~1.0.1", 18 | "connect-mongo": "^0.4.1", 19 | "cookie-parser": "~1.0.1", 20 | "ejs": "~0.8.4", 21 | "errorhandler": "~1.0.0", 22 | "express": "~4.0.0", 23 | "express-jwt": "^0.1.3", 24 | "express-session": "~1.0.2", 25 | "flux": "^2.0.1", 26 | "gulp-nodemon": "^1.0.4", 27 | "jquery-browserify": "^1.8.1", 28 | "jsonwebtoken": "^0.3.0", 29 | "lodash": "~2.4.1", 30 | "method-override": "~1.0.0", 31 | "mongoose": "~3.8.8", 32 | "morgan": "~1.0.0", 33 | "passport": "~0.2.0", 34 | "passport-facebook": "latest", 35 | "passport-google-oauth": "latest", 36 | "passport-local": "~0.1.6", 37 | "passport-twitter": "latest", 38 | "q": "^1.0.1", 39 | "react": "^0.11.2", 40 | "react-router": "^0.9.4", 41 | "serve-favicon": "~2.0.1", 42 | "gulp-jest": "*", 43 | "react-tools": "^0.11.2" 44 | }, 45 | "author": "Urban Boa", 46 | "license": "ISC", 47 | "bugs": { 48 | "url": "https://github.com/urban-boa/react-fullstack/issues" 49 | }, 50 | "engines": { 51 | "node": ">=0.10.0" 52 | }, 53 | "homepage": "https://github.com/urban-boa/react-fullstack/", 54 | "devDependencies": { 55 | "browserify": "^5.12.0", 56 | "gulp": "^3.8.8", 57 | "gulp-connect": "^2.0.6", 58 | "jest-cli": "^0.1.18", 59 | "reactify": "^0.14.0", 60 | "vinyl-source-stream": "^1.0.0" 61 | }, 62 | "jest": { 63 | "scriptPreprocessor": "/preprocessor.js", 64 | "unmockedModulePathPatterns": ["/node_modules/react"] 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /templates/app/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 | -------------------------------------------------------------------------------- /templates/app/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 | -------------------------------------------------------------------------------- /templates/app/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; -------------------------------------------------------------------------------- /templates/app/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 | } -------------------------------------------------------------------------------- /templates/app/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 | item: String, 8 | info: String, 9 | active: Boolean 10 | }); 11 | 12 | module.exports = mongoose.model('Thing', ThingSchema); -------------------------------------------------------------------------------- /templates/app/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 | -------------------------------------------------------------------------------- /templates/app/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 | -------------------------------------------------------------------------------- /templates/app/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 | -------------------------------------------------------------------------------- /templates/app/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 | -------------------------------------------------------------------------------- /templates/app/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 | require('./config/express')(app); 24 | require('./routes')(app); 25 | 26 | // Start server 27 | server.listen(config.port, config.ip, function () { 28 | console.log('Express server listening on %d, in %s mode', config.port, app.get('env')); 29 | }); 30 | 31 | // Expose app 32 | exports = module.exports = app; -------------------------------------------------------------------------------- /templates/app/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; -------------------------------------------------------------------------------- /templates/app/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; -------------------------------------------------------------------------------- /templates/app/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 | }; -------------------------------------------------------------------------------- /templates/app/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; -------------------------------------------------------------------------------- /templates/app/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 | -------------------------------------------------------------------------------- /templates/app/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; -------------------------------------------------------------------------------- /templates/app/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; -------------------------------------------------------------------------------- /templates/app/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 | }; -------------------------------------------------------------------------------- /templates/app/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; -------------------------------------------------------------------------------- /templates/app/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 | }; -------------------------------------------------------------------------------- /templates/app/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 | -------------------------------------------------------------------------------- /templates/app/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/slushy-dev' 9 | }, 10 | 11 | seedDB: true 12 | }; 13 | -------------------------------------------------------------------------------- /templates/app/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: 'slushy-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') || {}); -------------------------------------------------------------------------------- /templates/app/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/slushy' 22 | } 23 | }; -------------------------------------------------------------------------------- /templates/app/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/slushy-test' 9 | } 10 | }; -------------------------------------------------------------------------------- /templates/app/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 | }; 60 | -------------------------------------------------------------------------------- /templates/app/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: "slushy-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 | -------------------------------------------------------------------------------- /templates/app/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: 'slushy-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 | -------------------------------------------------------------------------------- /templates/app/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 | item : '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 | item : 'Server and Client integration', 17 | info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' 18 | }, { 19 | item : '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 | item : 'Modular Structure', 23 | info : 'Best practice client and server structures allow for more code reusability and maximum scalability' 24 | }, { 25 | item : '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 | item : '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 | }); 50 | -------------------------------------------------------------------------------- /templates/app/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 | -------------------------------------------------------------------------------- /templates/crud-app/client/crudApp/src/actions/CrudAppActions.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Dispatcher = require('../dispatchers/Dispatcher'); 3 | var Constants = require('../constants/Constants'); 4 | 5 | var Actions = { 6 | populateAction: function() { 7 | return Dispatcher.handleViewAction({ 8 | actionType: Constants.POPULATE 9 | }); 10 | }, 11 | addItemAction: function(text) { 12 | Dispatcher.handleViewAction({ 13 | actionType: Constants.ADD, 14 | text: text 15 | }); 16 | }, 17 | removeItemAction: function(id) { 18 | Dispatcher.handleViewAction({ 19 | actionType: Constants.REMOVE, 20 | id: id 21 | }); 22 | } 23 | }; 24 | 25 | module.exports = Actions; 26 | -------------------------------------------------------------------------------- /templates/crud-app/client/crudApp/src/components/CrudApp.jsx: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var React = require('react'); 4 | 5 | var Store = require('../stores/Store'); 6 | var Actions = require('../actions/Actions'); 7 | 8 | function getAppState(){ 9 | return Store.getData() 10 | }; 11 | 12 | var = React.createClass({ 13 | getInitialState: function(){ 14 | return getAppState(); 15 | }, 16 | 17 | _onChange: function(){ 18 | this.setState(getAppState()); 19 | }, 20 | 21 | componentDidMount: function(){ 22 | Store.addChangeListener(this._onChange); 23 | }, 24 | 25 | componentWillUnmount: function(){ 26 | Store.removeChangeListener(this._onChange); 27 | }, 28 | 29 | handleClick: function(){ 30 | Actions.exampleAction('Data from View'); 31 | }, 32 | 33 | render: function(){ 34 | return ( 35 |
    36 | 37 |
    38 | ) 39 | } 40 | }) 41 | 42 | module.exports = ; -------------------------------------------------------------------------------- /templates/crud-app/client/crudApp/src/constants/CrudAppConstants.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var keyMirror = require('react/lib/keyMirror'); 4 | 5 | module.exports = keyMirror({ 6 | POPULATE: null, 7 | ADD: null, 8 | REMOVE: null 9 | }); 10 | -------------------------------------------------------------------------------- /templates/crud-app/client/crudApp/src/dispatchers/CrudAppDispatcher.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Dispatcher = require('flux').Dispatcher; 3 | var copyProperties = require('react/lib/copyProperties'); 4 | var Dispatcher = copyProperties(new Dispatcher(), { 5 | 6 | /** 7 | * A bridge function between the views and the dispatcher, marking the action 8 | * as a view action. Another variant here could be handleServerAction. 9 | * @param {object} action The data coming from the view. 10 | */ 11 | handleViewAction: function(action) { 12 | return this.dispatch({ 13 | source: 'VIEW_ACTION', 14 | action: action 15 | }); 16 | } 17 | 18 | }); 19 | 20 | module.exports = Dispatcher; 21 | -------------------------------------------------------------------------------- /templates/crud-app/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 | -------------------------------------------------------------------------------- /templates/crud-app/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 | -------------------------------------------------------------------------------- /templates/crud-app/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; -------------------------------------------------------------------------------- /templates/crud-app/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 | } -------------------------------------------------------------------------------- /templates/crud-app/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 | item: String, 8 | info: String, 9 | active: Boolean 10 | }); 11 | 12 | module.exports = mongoose.model('Thing', ThingSchema); -------------------------------------------------------------------------------- /templates/crud-app/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 | -------------------------------------------------------------------------------- /templates/crud-app/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 | -------------------------------------------------------------------------------- /templates/crud-app/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 | -------------------------------------------------------------------------------- /templates/crud-app/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 | require('./config/express')(app); 24 | require('./routes')(app); 25 | 26 | // Start server 27 | server.listen(config.port, config.ip, function () { 28 | console.log('Express server listening on %d, in %s mode', config.port, app.get('env')); 29 | }); 30 | 31 | // Expose app 32 | exports = module.exports = app; -------------------------------------------------------------------------------- /templates/crud-app/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; -------------------------------------------------------------------------------- /templates/crud-app/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; -------------------------------------------------------------------------------- /templates/crud-app/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 | }; -------------------------------------------------------------------------------- /templates/crud-app/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; -------------------------------------------------------------------------------- /templates/crud-app/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 | -------------------------------------------------------------------------------- /templates/crud-app/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; -------------------------------------------------------------------------------- /templates/crud-app/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; -------------------------------------------------------------------------------- /templates/crud-app/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 | }; -------------------------------------------------------------------------------- /templates/crud-app/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; -------------------------------------------------------------------------------- /templates/crud-app/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 | }; -------------------------------------------------------------------------------- /templates/crud-app/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 | -------------------------------------------------------------------------------- /templates/crud-app/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/slushy-dev' 9 | }, 10 | 11 | seedDB: true 12 | }; 13 | -------------------------------------------------------------------------------- /templates/crud-app/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: 'slushy-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') || {}); -------------------------------------------------------------------------------- /templates/crud-app/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/slushy' 22 | } 23 | }; -------------------------------------------------------------------------------- /templates/crud-app/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/slushy-test' 9 | } 10 | }; -------------------------------------------------------------------------------- /templates/crud-app/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 | }; 60 | -------------------------------------------------------------------------------- /templates/crud-app/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: "slushy-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 | -------------------------------------------------------------------------------- /templates/crud-app/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: 'slushy-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 | -------------------------------------------------------------------------------- /templates/crud-app/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 | item : '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 | item : 'Server and Client integration', 17 | info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' 18 | }, { 19 | item : '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 | item : 'Modular Structure', 23 | info : 'Best practice client and server structures allow for more code reusability and maximum scalability' 24 | }, { 25 | item : '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 | item : '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 | }); 50 | -------------------------------------------------------------------------------- /templates/crud-app/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 | -------------------------------------------------------------------------------- /templates/react-actions/react-actions.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var AppDispatcher = require('../dispatchers/AppDispatcher'); 3 | var AppConstants = require('../constants/AppConstants'); 4 | 5 | var Actions = { 6 | 7 | }; 8 | 9 | module.exports = Actions; 10 | -------------------------------------------------------------------------------- /templates/react-component/react-component.jsx: -------------------------------------------------------------------------------- 1 | /** @jsx React.DOM */ 2 | 3 | 'use strict'; 4 | 5 | var React = require('react'); 6 | 7 | var AppStore = require('../stores/AppStore'); 8 | var AppActions = require('../actions/AppActions'); 9 | 10 | 11 | function getAppState(){ 12 | return AppStore.getData(); 13 | }; 14 | 15 | var = React.createClass({ 16 | getInitialState: function(){ 17 | return getAppState(); 18 | }, 19 | 20 | _onChange: function(){ 21 | this.setState(getAppState()); 22 | }, 23 | 24 | componentDidMount: function(){ 25 | AppStore.addChangeListener(this._onChange); 26 | }, 27 | 28 | componentWillUnmount: function(){ 29 | AppStore.removeChangeListener(this._onChange); 30 | }, 31 | 32 | handleClick: function(){ 33 | AppActions.exampleAction('Data from View'); 34 | }, 35 | 36 | render: function(){ 37 | return ( 38 |
    39 |
    40 | ) 41 | } 42 | }) 43 | 44 | module.exports = ; -------------------------------------------------------------------------------- /templates/react-constants/react-constants.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var keyMirror = require('react/lib/keyMirror'); 4 | 5 | module.exports = keyMirror({ 6 | 7 | }); 8 | -------------------------------------------------------------------------------- /templates/react-dispatcher/react-dispatcher.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Dispatcher = require('flux').Dispatcher; 3 | var copyProperties = require('react/lib/copyProperties'); 4 | var Dispatcher = copyProperties(new Dispatcher(), { 5 | 6 | /** 7 | * A bridge function between the views and the dispatcher, marking the action 8 | * as a view action. Another variant here could be handleServerAction. 9 | * @param {object} action The data coming from the view. 10 | */ 11 | handleViewAction: function(action) { 12 | return this.dispatch({ 13 | source: 'VIEW_ACTION', 14 | action: action 15 | }); 16 | } 17 | 18 | }); 19 | 20 | module.exports = Dispatcher; 21 | --------------------------------------------------------------------------------