├── .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 | ## ? humanizedAppName ?> ##
2 |
3 | ? appDescription ?>
4 |
5 | ### by ? capitalizedAppAuthor ?> ###
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 |
42 |
43 |
44 |
45 |
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 |
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 |
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 |
20 | React-Flux-Fullstack
21 |
22 | Signup
23 | Login
24 |
25 |
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 |
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 | Add
45 |
46 |
47 |
Features:
48 |
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": "? slugifiedAppName ?>",
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 | ## ? humanizedAppName ?> ##
2 |
3 | ? appDescription ?>
4 |
5 | ### by ? capitalizedAppAuthor ?> ###
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 |
36 |
37 |
38 |
39 |
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 |
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 |
23 | React-Flux-Fullstack
24 |
25 | Signup
26 | Login
27 |
28 |
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 | Add
42 |
43 |
44 |
Features:
45 |
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": "? slugifiedAppName ?>",
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 | ## ? humanizedAppName ?> ##
2 |
3 | ? appDescription ?>
4 |
5 | ### by ? capitalizedAppAuthor ?> ###
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 |
36 |
37 |
38 |
39 |
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 |
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 |
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 |
23 | React-Flux-Fullstack
24 |
25 | Signup
26 | Login
27 |
28 |
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 | Add
42 |
43 |
44 |
Features:
45 |
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": "? slugifiedAppName ?>",
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 ? classifiedCrudName ?>Dispatcher = require('../dispatchers/? classifiedCrudName ?>Dispatcher');
3 | var ? classifiedCrudName ?>Constants = require('../constants/? classifiedCrudName ?>Constants');
4 |
5 | var ? classifiedCrudName ?>Actions = {
6 | populateAction: function() {
7 | return ? classifiedCrudName ?>Dispatcher.handleViewAction({
8 | actionType: ? classifiedCrudName ?>Constants.POPULATE
9 | });
10 | },
11 | addItemAction: function(text) {
12 | ? classifiedCrudName ?>Dispatcher.handleViewAction({
13 | actionType: ? classifiedCrudName ?>Constants.ADD,
14 | text: text
15 | });
16 | },
17 | removeItemAction: function(id) {
18 | ? classifiedCrudName ?>Dispatcher.handleViewAction({
19 | actionType: ? classifiedCrudName ?>Constants.REMOVE,
20 | id: id
21 | });
22 | }
23 | };
24 |
25 | module.exports = ? classifiedCrudName ?>Actions;
26 |
--------------------------------------------------------------------------------
/templates/crud-app/client/crudApp/src/components/CrudApp.jsx:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react');
4 |
5 | var ? classifiedCrudName ?>Store = require('../stores/? classifiedCrudName ?>Store');
6 | var ? classifiedCrudName ?>Actions = require('../actions/? classifiedCrudName ?>Actions');
7 |
8 | function getAppState(){
9 | return ? classifiedCrudName ?>Store.getData()
10 | };
11 |
12 | var ? classifiedCrudName ?> = React.createClass({
13 | getInitialState: function(){
14 | return getAppState();
15 | },
16 |
17 | _onChange: function(){
18 | this.setState(getAppState());
19 | },
20 |
21 | componentDidMount: function(){
22 | ? classifiedCrudName ?>Store.addChangeListener(this._onChange);
23 | },
24 |
25 | componentWillUnmount: function(){
26 | ? classifiedCrudName ?>Store.removeChangeListener(this._onChange);
27 | },
28 |
29 | handleClick: function(){
30 | ? classifiedCrudName ?>Actions.exampleAction('Data from View');
31 | },
32 |
33 | render: function(){
34 | return (
35 |
36 |
37 |
38 | )
39 | }
40 | })
41 |
42 | module.exports = ? classifiedCrudName ?>;
--------------------------------------------------------------------------------
/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 ? classifiedCrudName ?>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 = ? classifiedCrudName ?>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 ? classifiedActionName ?>Actions = {
6 |
7 | };
8 |
9 | module.exports = ? classifiedActionName ?>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 ? classifiedComponentName ?> = 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 = ? classifiedComponentName ?>;
--------------------------------------------------------------------------------
/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 ? classifiedDispatcherName ?>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 = ? classifiedDispatcherName ?>Dispatcher;
21 |
--------------------------------------------------------------------------------