├── server
├── config
│ ├── env
│ │ ├── default.json
│ │ ├── local.json
│ │ ├── development.json
│ │ ├── production.json
│ │ └── index.js
│ ├── winston.js
│ ├── s3.js
│ ├── socket.io.js
│ ├── passport.js
│ └── express.js
├── api
│ ├── index.js
│ └── auth
│ │ └── index.js
├── middleware
│ └── auth.middleware.js
├── routes.js
├── seed
│ └── index.js
├── app.js
└── models
│ └── User.js
├── zip
└── build.zip
├── .babelrc
├── client
├── assets
│ ├── fonts
│ │ └── semantic-ui
│ │ │ ├── icons.eot
│ │ │ ├── icons.otf
│ │ │ ├── icons.ttf
│ │ │ ├── icons.woff
│ │ │ └── icons.woff2
│ └── sass
│ │ └── app.sass
├── src
│ ├── modules
│ │ └── Pages
│ │ │ └── Home.js
│ ├── index.js
│ ├── services
│ │ ├── Storage.js
│ │ └── Notify.js
│ ├── routes.js
│ ├── App.js
│ ├── stores
│ │ └── Auth.js
│ └── decorators
│ │ └── provideStore.js
└── index.ejs
├── gulp
├── conf.js
├── build.js
└── develop.js
├── .env.dist
├── .editorconfig
├── gulpfile.babel.js
├── bower.json
├── webpack
├── common.config.js
├── webpack.dev.config.js
└── webpack.prod.config.js
├── package.json
├── readme.md
└── .gitignore
/server/config/env/default.json:
--------------------------------------------------------------------------------
1 | {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/server/config/env/local.json:
--------------------------------------------------------------------------------
1 | {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/server/config/env/development.json:
--------------------------------------------------------------------------------
1 | {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/server/config/env/production.json:
--------------------------------------------------------------------------------
1 | {
2 |
3 | }
4 |
--------------------------------------------------------------------------------
/zip/build.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thaerlabs/full-stack-react-mobx/HEAD/zip/build.zip
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["react", "es2015", "stage-0"],
3 | "plugins": ["transform-decorators-legacy"]
4 | }
5 |
--------------------------------------------------------------------------------
/client/assets/fonts/semantic-ui/icons.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thaerlabs/full-stack-react-mobx/HEAD/client/assets/fonts/semantic-ui/icons.eot
--------------------------------------------------------------------------------
/client/assets/fonts/semantic-ui/icons.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thaerlabs/full-stack-react-mobx/HEAD/client/assets/fonts/semantic-ui/icons.otf
--------------------------------------------------------------------------------
/client/assets/fonts/semantic-ui/icons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thaerlabs/full-stack-react-mobx/HEAD/client/assets/fonts/semantic-ui/icons.ttf
--------------------------------------------------------------------------------
/client/assets/fonts/semantic-ui/icons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thaerlabs/full-stack-react-mobx/HEAD/client/assets/fonts/semantic-ui/icons.woff
--------------------------------------------------------------------------------
/client/assets/fonts/semantic-ui/icons.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thaerlabs/full-stack-react-mobx/HEAD/client/assets/fonts/semantic-ui/icons.woff2
--------------------------------------------------------------------------------
/server/api/index.js:
--------------------------------------------------------------------------------
1 | import { Router } from 'express';
2 |
3 | const router = Router();
4 |
5 | router.use('/auth', require('./auth'));
6 |
7 | module.exports = router;
8 |
--------------------------------------------------------------------------------
/gulp/conf.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The main paths of your project handle these with care
3 | */
4 |
5 | module.exports.paths = {
6 | src: 'client',
7 | dist: 'build',
8 | server: 'server'
9 | };
10 |
--------------------------------------------------------------------------------
/client/src/modules/Pages/Home.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | export default class Home extends Component {
4 | render() {
5 | return (
6 |
Home page
7 | );
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/client/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { Router, browserHistory } from 'react-router';
4 | import getRoutes from './routes';
5 |
6 | ReactDOM.render({getRoutes()}, document.getElementById('root'));
7 |
--------------------------------------------------------------------------------
/client/src/services/Storage.js:
--------------------------------------------------------------------------------
1 | import Basil from 'basil.js';
2 |
3 | const namespace = 'pim';
4 |
5 | export default new Basil({
6 | namespace
7 | });
8 |
9 | export const cookieStorage = new Basil({
10 | namespace,
11 | storages: ['cookie']
12 | });
13 |
14 | export const localStorage = new Basil({
15 | namespace,
16 | storages: ['local']
17 | });
18 |
--------------------------------------------------------------------------------
/client/src/routes.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Route, IndexRoute } from 'react-router';
3 |
4 | import App from './App';
5 | import Home from 'modules/Pages/Home';
6 |
7 | export default function getRoutes() {
8 | return (
9 |
10 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/server/middleware/auth.middleware.js:
--------------------------------------------------------------------------------
1 | import boom from 'boom';
2 |
3 | /**
4 | * Authentication middleware
5 | * @param req
6 | * @param res
7 | * @param next
8 | * @returns {*}
9 | */
10 | export function isAuthenticated(req, res, next){
11 | if(req.isAuthenticated()){
12 | return next();
13 | }
14 |
15 | next(boom.unauthorized('you are not authorized'));
16 | }
17 |
--------------------------------------------------------------------------------
/server/routes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Main application routes
3 | */
4 |
5 | 'use strict';
6 | import { Router } from 'express';
7 |
8 | const router = Router();
9 |
10 | /**
11 | * /api/ routes
12 | */
13 | router.use('/api/', require('./api'));
14 |
15 | /**
16 | * render client/index.ejs
17 | */
18 | router.get('/*', function(req, res) {
19 | res.render('index');
20 | });
21 |
22 | module.exports = router;
23 |
--------------------------------------------------------------------------------
/.env.dist:
--------------------------------------------------------------------------------
1 | NODE_ENV=local
2 | PORT=3300
3 | IP=
4 | DOMAIN=http://localhost:3300
5 |
6 | #Dev
7 | WEBPACK_DEV_PORT=3150
8 | BS_DEV_PORT=3010
9 |
10 | #Session
11 | SESSION_SECRET=S6aAY8vLxE3Y8LSJLx9
12 |
13 | #Mongodb
14 | MONGODB_URL=mongodb://localhost/react-base
15 |
16 | #Logging
17 | LOG_FILE_PATH=./www_log.log
18 | LOG_FILE_SIZE=10485760
19 |
20 | #JWT
21 | JWT_SECRET=yallatoken
22 |
23 | #S3
24 | AWS_ACCESS_KEY=
25 | AWS_SECRET_KEY=
26 | AWS_BUCKET=tjwl
27 | AWS_BUCKET_ASSETS_SUBFOLDER=cms/assets/
28 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
--------------------------------------------------------------------------------
/server/seed/index.js:
--------------------------------------------------------------------------------
1 | import User from '../models/User';
2 | import bluebird from 'bluebird';
3 |
4 | export function init(){
5 | bluebird.coroutine(function *() {
6 | /**
7 | * Initializing users
8 | */
9 | yield User.remove({});
10 | yield new User({username: 'admin', password: 'admin123', passwordConfirmation: 'admin123'}).save();
11 | yield new User({username: 'user', password: 'user123', passwordConfirmation: 'user123'}).save();
12 |
13 | console.log("Seeding completed");
14 |
15 | })().catch((err) => {
16 | console.log(err);
17 | });
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/client/src/services/Notify.js:
--------------------------------------------------------------------------------
1 | import singleton from 'singleton';
2 |
3 | const defaults = {
4 | position: 'tc'
5 | };
6 |
7 | class Notify extends singleton {
8 |
9 | notificationSystem = null;
10 |
11 | init(notificationSystem) {
12 | this.notificationSystem = notificationSystem;
13 | }
14 |
15 | addNotification(notification) {
16 | this.notificationSystem.addNotification(Object.assign(notification, defaults));
17 | }
18 |
19 | removeNotification(notification) {
20 | this.notificationSystem.removeNotification(Object.assign(notification, defaults));
21 | }
22 | }
23 |
24 | export default Notify.get();
25 |
--------------------------------------------------------------------------------
/client/assets/sass/app.sass:
--------------------------------------------------------------------------------
1 | @font-face
2 | font-family: 'Icons'
3 | src: url("/assets/fonts/semantic-ui/icons.eot")
4 | src: url("/assets/fonts/semantic-ui/icons.eot?#iefix") format('embedded-opentype'), url("/assets/fonts/semantic-ui/icons.woff2") format('woff2'), url("/assets/fonts/semantic-ui/icons.woff") format('woff'), url("/assets/fonts/semantic-ui/icons.ttf") format('truetype'), url("/assets/fonts/semantic-ui/icons.svg#icons") format('svg')
5 | font-style: normal
6 | font-weight: normal
7 | font-variant: normal
8 | text-decoration: inherit
9 | text-transform: none
10 |
11 |
12 | body
13 | background-color: #f0f0f0
14 |
--------------------------------------------------------------------------------
/server/api/auth/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import {Router} from 'express';
4 | import passport from 'passport';
5 | import Boom from 'boom';
6 | import jwt from 'jsonwebtoken';
7 | import config from 'nconf';
8 |
9 | /**
10 | * Setting up authentication routes
11 | */
12 | var router = Router();
13 |
14 | router.post('/login', passport.authenticate('local'), (req, res) => {
15 | const token = jwt.sign(req.user.toObject(), config.get('JWT_SECRET'));
16 |
17 | res.json({
18 | user: req.user,
19 | token
20 | });
21 | });
22 |
23 | router.get('/logout', function (req, res) {
24 | req.logout();
25 | res.sendStatus(204);
26 | });
27 |
28 | module.exports = router;
29 |
--------------------------------------------------------------------------------
/gulpfile.babel.js:
--------------------------------------------------------------------------------
1 | import gulp from "gulp";
2 | import read from 'fs-readdir-recursive';
3 |
4 | require('dotenv').load({
5 | path: __dirname + '/server/.env'
6 | });
7 |
8 | /**
9 | * This will load all js or coffee files in the gulp directory
10 | * in order to load all gulp tasks
11 | */
12 | read('./gulp', function(file){
13 | return (/\.(js|coffee)$/i).test(file);
14 | }).map( file => {
15 | require('./gulp/' + file);
16 | });
17 |
18 | /**
19 | * for dev
20 | */
21 | gulp.task('default', ['webpack-dev-server', 'sass:watch', 'serve']);
22 |
23 | /**
24 | * for bundling
25 | */
26 | gulp.task('bundle', ['webpack-build', 'sass'], function () {
27 | gulp.start('zip');
28 | });
29 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hotelPim",
3 | "description": "",
4 | "ignore": [
5 | "**/.*",
6 | "node_modules",
7 | "bower_components",
8 | "test",
9 | "tests"
10 | ],
11 | "dependencies": {
12 | "codemirror": "^5.13.2",
13 | "leaflet": "^0.7.7",
14 | "font-awesome": "^4.5.0",
15 | "froala-wysiwyg-editor": "^2.2.2",
16 | "semantic": "^2.1.8",
17 | "jquery": "^2.2.2"
18 | },
19 | "overrides": {
20 | "codemirror": {
21 | "main": [
22 | "lib/codemirror.js",
23 | "lib/codemirror.css",
24 | "mode/xml/xml.js"
25 | ]
26 | },
27 | "semantic": {
28 | "main": [
29 | "dist/semantic.css",
30 | "dist/semantic.js"
31 | ]
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/client/src/App.js:
--------------------------------------------------------------------------------
1 | /**
2 | * App component, mounted at '/'
3 | *
4 | * this is the main application frame, register here stores to be passed down to all subviews in `this.context.store`
5 | */
6 |
7 | import React, { Component } from 'react';
8 | import provideStore from 'decorators/provideStore';
9 | import NotificationSystem from 'react-notification-system';
10 | import Auth from 'stores/Auth';
11 | import Notify from 'services/Notify';
12 |
13 | @provideStore({
14 | auth: Auth
15 | })
16 | export default class App extends Component {
17 |
18 | componentDidMount() {
19 | Notify.init(this.refs.notificationSystem);
20 | }
21 |
22 | render() {
23 | return (
24 |
25 | {this.props.children}
26 |
27 |
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/server/config/env/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import nconf from 'nconf';
4 | import fs from 'fs';
5 | import path from 'path'
6 |
7 | /**
8 | * Initialize Configurations
9 | */
10 | export function init() {
11 | /**
12 | * Loading config from environment
13 | */
14 | nconf.env();
15 |
16 | /**
17 | * Verify Config file exist
18 | */
19 | const configFilePath = path.join(__dirname, process.env.NODE_ENV + '.json');
20 | try {
21 | fs.accessSync(configFilePath, fs.F_OK);
22 | }
23 | catch (err) {
24 | console.log("Configuration file does not exist for current environment");
25 | process.exit(1);
26 | }
27 |
28 | /**
29 | * Loading default and environment specific config
30 | */
31 | nconf
32 | .file({file: configFilePath})
33 | .defaults(require('./default.json'));
34 | }
35 |
36 |
37 |
--------------------------------------------------------------------------------
/client/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hotels
6 |
7 |
8 | <% if (env !== 'local') { %>
9 |
10 | <% } %>
11 |
12 |
13 |
14 |
15 |
16 | <% if (env === 'local') { %>
17 |
20 | <% } %>
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/server/config/winston.js:
--------------------------------------------------------------------------------
1 | import winston from 'winston';
2 | import config from 'nconf';
3 |
4 | /**
5 | * Winston trnasports
6 | * @type {*[]}
7 | */
8 | const winstonTransports = [
9 | {
10 | type: winston.transports.File,
11 | options: {
12 | level: 'error',
13 | filename: config.get('LOG_FILE_PATH'),
14 | logstash: true,
15 | maxsize: config.get('LOG_FILE_SIZE')
16 | }
17 | }
18 | ];
19 |
20 | /**
21 | * Return Transport Instances
22 | * @returns {Array}
23 | */
24 | export function getTransportsInstance() {
25 | return winstonTransports.map(function (t) {
26 | return new t.type(t.options);
27 | })
28 | }
29 |
30 | /**
31 | * Initialize winston config
32 | */
33 | export function init(){
34 | winston.remove(winston.transports.Console);
35 |
36 | winstonTransports.forEach(function (t) {
37 | winston.add(t.type, t.options);
38 | });
39 | }
40 |
--------------------------------------------------------------------------------
/server/config/s3.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 | import crypto from 'crypto';
3 |
4 | var s3Config = {
5 | bucket: process.env.AWS_BUCKET,
6 | region: 's3',
7 | keyStart: process.env.AWS_BUCKET_ASSETS_SUBFOLDER,
8 | acl: 'public-read',
9 | accessKeyId: process.env.AWS_ACCESS_KEY
10 | };
11 |
12 | s3Config.policy = {
13 | expiration: moment().add(1, 'days').toISOString(),
14 | conditions: [
15 | {bucket: s3Config.bucket},
16 | {acl: s3Config.acl},
17 | {success_action_status: '201'},
18 | {'x-requested-with': 'xhr'},
19 | ['starts-with', '$key', s3Config.keyStart],
20 | ['starts-with', '$Content-Type', '']
21 | ]
22 | };
23 | s3Config.policy = new Buffer(JSON.stringify(s3Config.policy)).toString('base64');
24 |
25 | var hash = crypto.createHmac('sha1', process.env.AWS_SECRET_KEY);
26 | s3Config.signature = new Buffer(hash.update(s3Config.policy).digest()).toString('base64');
27 |
28 | module.exports.s3Config = s3Config;
29 |
--------------------------------------------------------------------------------
/client/src/stores/Auth.js:
--------------------------------------------------------------------------------
1 | import { get, post } from 'axios';
2 | import { observable, computed } from 'mobx';
3 | import jwt from 'jsonwebtoken';
4 | import singleton from 'singleton';
5 |
6 | import Storage from 'services/Storage';
7 |
8 | class Auth extends singleton {
9 | @observable user = null;
10 |
11 | constructor() {
12 | super();
13 |
14 | const token = Storage.get('token');
15 |
16 | if (token) {
17 | this.user = jwt.verify(token, JWT_SECRET);
18 | }
19 | }
20 |
21 | @computed get isLoggedIn() {
22 | return !!this.user;
23 | }
24 |
25 | login(username, password) {
26 | return post('/api/auth/login', {
27 | username, password
28 | })
29 | .then((res) => {
30 | this.user = res.data.user;
31 | Storage.set('token', res.data.token);
32 | return res;
33 | });
34 | }
35 |
36 | logout() {
37 | Storage.remove('token');
38 | this.user = null;
39 | return get('/api/auth/logout');
40 | }
41 | }
42 |
43 | export default Auth.get();
44 |
--------------------------------------------------------------------------------
/server/config/socket.io.js:
--------------------------------------------------------------------------------
1 | import _ from 'lodash';
2 |
3 | export function init(io, sessionMiddleware){
4 | io.use(function(socket, next) {
5 | sessionMiddleware(socket.request, socket.request.res, next);
6 | });
7 |
8 | io.on('connection', function(socket){
9 | const user = socket.request.session.passport ? socket.request.session.passport.user : {};
10 |
11 | if(!_.isUndefined(user)){
12 | socket.broadcast.emit('connectUser', user.username + ' has just sign in.');
13 | }
14 |
15 | socket.on('join_hotel_edit', function (room) {
16 | socket.join(room);
17 | socket.broadcast.to(room).emit('hotel', user.username + ' is also on same page.');
18 | });
19 |
20 | socket.on('leave_hotel_edit', function (room) {
21 | socket.broadcast.to(room).emit('hotel', user.username + ' has left edit page.');
22 | socket.leave(room);
23 |
24 | });
25 |
26 | socket.on('disconnect', function () {
27 | socket.broadcast.emit('disconnectUser', user.username + ' has just sign out.');
28 | });
29 | });
30 | }
31 |
--------------------------------------------------------------------------------
/client/src/decorators/provideStore.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | /**
4 | * This decorator is to be used in the Root component to inject in the `context` the designated stores
5 | *
6 | * @example
7 | *
8 | * @provideStore({
9 | * auth: Auth,
10 | * hotel: Hotel,
11 | * language: Language
12 | * })
13 | * export default class App extends Component {
14 | * render() {
15 | * return (
16 | * {this.props.children}
17 | * );
18 | * }
19 | * }
20 | *
21 | * @param store
22 | * @returns {Function}
23 | */
24 | function provideStore(store) {
25 | return function (DecoratedComponent) {
26 | return class extends Component {
27 | static childContextTypes = {
28 | store: React.PropTypes.object
29 | };
30 |
31 | getChildContext() {
32 | return {
33 | store: store
34 | };
35 | }
36 |
37 | render() {
38 | return (
39 |
40 | );
41 | }
42 | }
43 | }
44 | };
45 |
46 | export default provideStore;
47 |
--------------------------------------------------------------------------------
/server/config/passport.js:
--------------------------------------------------------------------------------
1 | import passport from 'passport';
2 | import { Strategy } from 'passport-local';
3 | import Boom from 'boom';
4 | import User from '../models/User';
5 | import bluebird from 'bluebird';
6 |
7 | /**
8 | * Passport configuration
9 | */
10 | export function init() {
11 |
12 | passport.serializeUser(function(user, done) {
13 | done(null, user);
14 | });
15 |
16 | passport.deserializeUser(function(user, done) {
17 | done(null, user);
18 | });
19 |
20 | passport.use(new Strategy(function(username, password, done) {
21 |
22 | bluebird.coroutine(function *(){
23 | var user = yield User
24 | .findOne({username: username})
25 | .populate('role', '_id name menuItems');
26 |
27 | if (!user)
28 | return done(Boom.badRequest("Invalid username"));
29 |
30 | if (!user.isActive) {
31 | return done(Boom.forbidden("You access has been blocked."));
32 | }
33 |
34 | if (!user.authenticate(password)) {
35 | return done(Boom.badRequest("Invalid password"));
36 | }
37 |
38 | done(null, user);
39 | })().catch((err) => {
40 | done(Boom.wrap(err, 422));
41 | });
42 | }));
43 | }
44 |
--------------------------------------------------------------------------------
/webpack/common.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
3 | var wiredep = require('wiredep')();
4 | var BowerWebpackPlugin = require("bower-webpack-plugin");
5 |
6 | module.exports = {
7 | entry: {
8 | vendor_node: [
9 | 'react',
10 | 'react-dom',
11 | 'react-router',
12 | 'react-mixin',
13 | 'mobx',
14 | 'mobx-react',
15 | 'lodash',
16 | 'classnames',
17 | 'jsonwebtoken',
18 | 'axios',
19 | 'basil.js',
20 | 'react-time-picker',
21 | 'react-leaflet',
22 | 'singleton',
23 | 'leaflet',
24 | 'socket.io-client',
25 | 'react-notification-system',
26 | 'react-dropzone',
27 | 'griddle-react'
28 | ],
29 | vendor_bower: []
30 | .concat(wiredep['css'])
31 | .concat(wiredep['scss'])
32 | .concat(wiredep['js'])
33 | },
34 | plugins: [
35 | new BowerWebpackPlugin({
36 | modulesDirectories: ["bower_components"],
37 | manifestFiles: "bower.json",
38 | searchResolveModulesDirectories: false
39 | }),
40 | new webpack.ProvidePlugin({
41 | $: "jquery",
42 | jQuery: "jquery",
43 | "window.jQuery": "jquery"
44 | }),
45 | new CommonsChunkPlugin({name: ['vendor_node', 'vendor_bower'], minChunks: Infinity})
46 | ]
47 | };
48 |
--------------------------------------------------------------------------------
/gulp/build.js:
--------------------------------------------------------------------------------
1 | import gulp from "gulp";
2 | import gutil from "gulp-util";
3 | import webpack from "webpack";
4 | import webpackProdConfig from "../webpack/webpack.prod.config";
5 | import copy from "gulp-copy";
6 | import debug from "gulp-debug";
7 | import path from "path";
8 | import zip from "gulp-zip";
9 | import {paths} from './conf';
10 |
11 | /**
12 | * webpack build task
13 | */
14 | gulp.task('webpack-build', function(callback) {
15 |
16 | var config = Object.create(webpackProdConfig);
17 | var compiler = webpack(config);
18 |
19 | compiler.run(function(err, stats) {
20 | if(err) throw new gutil.PluginError("webpack-build", err);
21 |
22 | callback();
23 | });
24 |
25 | });
26 |
27 | /**
28 | * Copy content to build folder
29 | */
30 | gulp.task('copy', function () {
31 | var allFiles = [].concat(
32 | 'package.json',
33 | 'server/**/*',
34 | './.babelrc',
35 | path.join(paths.src, '/assets/fonts/**/*'),
36 | path.join(paths.src, '/dist/**/*'),
37 | path.join(paths.src, '/index.ejs')
38 | );
39 | return gulp.src(allFiles)
40 | .pipe(debug({
41 | title: 'file:'
42 | }))
43 | .pipe(copy(paths.dist));
44 | });
45 |
46 | /**
47 | * Zip the build folder
48 | */
49 | gulp.task('zip', ['copy'], function() {
50 | return gulp.src(path.join(paths.dist, '/**/*'), {dot: true})
51 | .pipe(zip('build.zip'))
52 | .pipe(gulp.dest('zip'));
53 |
54 | });
55 |
--------------------------------------------------------------------------------
/server/app.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Bootstrap application file
3 | *
4 | * This is the main entry point of the application, it will load configurations, initialize the app and start the
5 | * Express server
6 | */
7 | // Load babel for subsequent imports in ES2015
8 | require('babel-register');
9 | require('babel-polyfill');
10 |
11 | // Load .env file
12 | require('dotenv').load({
13 | path: __dirname + '/.env'
14 | });
15 |
16 | // Load config
17 | require('./config/env').init();
18 |
19 |
20 | var config = require('nconf');
21 | var mongoose = require('mongoose');
22 | var connectMongo = require('connect-mongo');
23 | var session = require('express-session');
24 |
25 | // Configure and connect Mongoose
26 | //set mongoose Promise provider to bluebird
27 | mongoose.Promise = require('bluebird');
28 | // Connect to database
29 | mongoose.connect(config.get('MONGODB_URL'));
30 |
31 | /**
32 | * Configure passport
33 | */
34 | require('./config/passport').init();
35 |
36 | /**
37 | * Configure winston
38 | */
39 | require('./config/winston').init();
40 |
41 | /**
42 | * Initialize session middleware
43 | */
44 | const MongoStore = connectMongo(session);
45 | const sessionMiddleware = session({
46 | secret: config.get("SESSION_SECRET"),
47 | resave: true,
48 | saveUninitialized: true,
49 | store: new MongoStore({
50 | mongooseConnection: mongoose.connection
51 | })
52 | });
53 |
54 | /**
55 | * Require Express app
56 | */
57 | require('./config/express').init(sessionMiddleware);
58 | var app = require('./config/express').getAppInstance();
59 |
60 | var server = app.listen(config.get("PORT"), function() {
61 | console.log('Express server listening on http://localhost:' + config.get("PORT"));
62 | });
63 |
64 | /**
65 | * Initializing Socket
66 | */
67 | var io = require('socket.io').listen(server);
68 | require('./config/socket.io').init(io, sessionMiddleware);
69 |
70 | /**
71 | * Seed user, roles and menu items
72 | */
73 |
74 | require('./seed').init();
75 |
--------------------------------------------------------------------------------
/webpack/webpack.dev.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 | var CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
4 | var commonConfig = require('./common.config');
5 |
6 | require('dotenv').load({
7 | path: './server/.env'
8 | });
9 |
10 | module.exports = {
11 | devtool: 'eval',
12 | entry: Object.assign({}, {
13 | app: [
14 | `webpack-dev-server/client?http://localhost:${process.env.WEBPACK_DEV_PORT}`,
15 | 'webpack/hot/only-dev-server',
16 | './client/src/index'
17 | ]
18 | }, commonConfig.entry),
19 | output: {
20 | path: path.join(__dirname, './client/dist'),
21 | filename: '[name].js',
22 | publicPath: `http://localhost:${process.env.WEBPACK_DEV_PORT}/static/`
23 | },
24 | progress: true,
25 | resolve: {
26 | modulesDirectories: ['bower_components', 'node_modules', './client/src']
27 | },
28 | node: {
29 | fs: 'empty',
30 | net: 'empty',
31 | tls: 'empty'
32 | },
33 | module: {
34 | loaders: [
35 | {
36 | test: /\.js?$/,
37 | exclude: /(node_modules|bower_components)/,
38 | loaders: ['react-hot','babel']
39 | },
40 | {
41 | test: /\.css$/,
42 | loader: 'style-loader!css-loader'
43 | },
44 | {
45 | test: /\.scss$/,
46 | loaders: ['style', 'css', 'sass']
47 | },
48 | { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
49 | loader: 'url-loader?limit=10000&mimetype=application/font-woff'
50 | },
51 | { test: /\.(png|jpg|jpeg|gif|ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
52 | loader: 'file-loader'
53 | }
54 | ]
55 | },
56 | plugins: [
57 | new webpack.DefinePlugin({
58 | 'JWT_SECRET': JSON.stringify(process.env.JWT_SECRET),
59 | 'S3_CONFIG': JSON.stringify(require('../server/config/s3').s3Config)
60 | }),
61 | new webpack.HotModuleReplacementPlugin()
62 | ].concat(commonConfig.plugins)
63 | };
64 |
--------------------------------------------------------------------------------
/gulp/develop.js:
--------------------------------------------------------------------------------
1 | import gulp from "gulp";
2 | import gutil from "gulp-util";
3 | import nodemon from "gulp-nodemon";
4 | import sass from "gulp-sass";
5 | import webpack from "webpack";
6 | import WebpackDevServer from "webpack-dev-server";
7 | import webpackDevConfig from "../webpack/webpack.dev.config";
8 | import path from "path";
9 | import {paths} from './conf';
10 | var browserSync = require('browser-sync').create();
11 |
12 | /**
13 | * Initialize browser sync
14 | */
15 | gulp.task('serve', ['nodemon'], function() {
16 | browserSync.init({
17 | serveStatic: [path.join(paths.src, '/dist')],
18 | port: process.env.BS_DEV_PORT
19 | });
20 | });
21 |
22 | /**
23 | * Start nodejs with nodemon
24 | */
25 | gulp.task('nodemon', function () {
26 | nodemon({
27 | script: process.cwd() + '/server/app.js'
28 | , ext: 'js',
29 | ignore: [path.join(paths.src, '/**')]
30 | })
31 | });
32 |
33 | /**
34 | * webpack dev server
35 | */
36 | gulp.task("webpack-dev-server", function() {
37 | // Start a webpack-dev-server
38 |
39 | var config = Object.create(webpackDevConfig);
40 | var compiler = webpack(config);
41 |
42 | new WebpackDevServer(compiler, {
43 | publicPath: webpackDevConfig.output.publicPath,
44 | hot: true,
45 | stats: {
46 | assets: false,
47 | colors: true,
48 | version: false,
49 | hash: false,
50 | timings: true,
51 | chunks: true,
52 | chunkModules: false
53 | },
54 | watchOptions: {
55 | aggregateTimeout: 300,
56 | poll: 1000
57 | }
58 | }).listen(process.env.WEBPACK_DEV_PORT, "localhost", function(err) {
59 | if(err) throw new gutil.PluginError("webpack-dev-server", err);
60 | // Server listening
61 | gutil.log("[webpack-dev-server]", `http://localhost:${process.env.WEBPACK_DEV_PORT}${webpackDevConfig.output.publicPath}${webpackDevConfig.output.filename}`);
62 | });
63 | });
64 |
65 | /**
66 | * sass compilation
67 | */
68 | gulp.task('sass', function () {
69 | return gulp.src(path.join(paths.src, '/assets/sass/**/*.sass'))
70 | .pipe(sass().on('error', sass.logError))
71 | .pipe(gulp.dest(path.join(paths.src, '/dist')))
72 | .pipe(browserSync.stream());
73 | });
74 |
75 | /**
76 | * sass watch
77 | */
78 | gulp.task('sass:watch', ['sass'], function () {
79 | gulp.watch(path.join(paths.src, '/assets/sass/**/*.sass'), ['sass']);
80 | gulp.watch(path.join(paths.src, '/src/**/*.sass'), ['sass']);
81 | });
82 |
--------------------------------------------------------------------------------
/webpack/webpack.prod.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var ManifestPlugin = require('webpack-manifest-plugin');
3 | var CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
4 | var ExtractTextPlugin = require('extract-text-webpack-plugin');
5 | var commonConfig = require('./common.config');
6 | var CleanWebpackPlugin = require('clean-webpack-plugin');
7 |
8 |
9 | require('dotenv').load({
10 | path: './server/.env'
11 | });
12 |
13 | module.exports = {
14 | devtool: 'cheap-module-source-map',
15 | entry: Object.assign({}, {
16 | app: './client/src/index'
17 | }, commonConfig.entry),
18 | output: {
19 | path: './client/dist',
20 | filename: '[name]-[hash].js',
21 | publicPath: process.env.DOMAIN + '/dist/' //https://github.com/webpack/style-loader/issues/55
22 | },
23 | progress: true,
24 | resolve: {
25 | modulesDirectories: ['bower_components', 'node_modules', './client/src']
26 | },
27 | node: {
28 | fs: 'empty',
29 | net: 'empty',
30 | tls: 'empty'
31 | },
32 | module: {
33 | loaders: [
34 | {
35 | test: /\.js?$/,
36 | exclude: /(node_modules|bower_components)/,
37 | loaders: ['babel']
38 | },
39 | {
40 | test: /\.css$/,
41 | loader: ExtractTextPlugin.extract(
42 | 'style-loader',
43 | 'css-loader'
44 | )
45 | },
46 | {
47 | test: /\.scss$/,
48 | loader: ExtractTextPlugin.extract(
49 | 'style',
50 | 'css!sass'
51 | )
52 | },
53 | { test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
54 | loader: 'url-loader?limit=10000&mimetype=application/font-woff'
55 | },
56 | { test: /\.(png|jpg|jpeg|gif|ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
57 | loader: 'file-loader'
58 | }
59 | ]
60 | },
61 | plugins: [
62 | new CleanWebpackPlugin(['client/dist', 'build'],{
63 | root: process.cwd(),
64 | verbose: true
65 | }),
66 | new ExtractTextPlugin('[name]-[contenthash].css', {allChunks: false}),
67 | new webpack.DefinePlugin({
68 | 'process.env': {
69 | 'NODE_ENV': JSON.stringify('production')
70 | },
71 | 'JWT_SECRET': JSON.stringify(process.env.JWT_SECRET),
72 | 'S3_CONFIG': JSON.stringify(require('../server/config/s3').s3Config)
73 | }),
74 | new ManifestPlugin({
75 | basePath: '/dist/'
76 | }),
77 | new webpack.optimize.DedupePlugin(),
78 | new webpack.optimize.UglifyJsPlugin({
79 | compress: {
80 | warnings: false
81 | }
82 | })
83 | ].concat(commonConfig.plugins)
84 | };
85 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hotelPim",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "postinstall": "bower install",
6 | "start": "gulp build && node server/app.js",
7 | "dev": "gulp"
8 | },
9 | "dependencies": {
10 | "async": "^2.0.0-rc.2",
11 | "axios": "^0.9.1",
12 | "babel-polyfill": "^6.7.2",
13 | "babel-preset-es2015": "^6.6.0",
14 | "babel-register": "^6.7.2",
15 | "basil.js": "^0.4.3",
16 | "bluebird": "^3.3.4",
17 | "body-parser": "^1.15.0",
18 | "boom": "^3.1.2",
19 | "classnames": "^2.2.3",
20 | "compression": "^1.6.1",
21 | "connect-mongo": "^1.1.0",
22 | "cookie-parser": "^1.4.1",
23 | "dotenv": "^2.0.0",
24 | "ejs": "^2.4.1",
25 | "express": "^4.13.4",
26 | "express-session": "^1.13.0",
27 | "express-winston": "^1.3.0",
28 | "griddle-react": "^0.4.1",
29 | "jquery": "^2.2.2",
30 | "jsonwebtoken": "^5.7.0",
31 | "leaflet": "^0.7.7",
32 | "lodash": "^4.6.1",
33 | "method-override": "^2.3.5",
34 | "mobx": "^2.0.3",
35 | "mobx-react": "^3.0.3",
36 | "moment": "^2.12.0",
37 | "mongoose": "^4.4.7",
38 | "mongoose-unique-validator": "^1.0.2",
39 | "morgan": "^1.7.0",
40 | "multer": "^1.1.0",
41 | "mysql": "^2.10.2",
42 | "nconf": "^0.8.4",
43 | "passport": "^0.3.2",
44 | "passport-local": "^1.0.0",
45 | "react": "^0.14.7",
46 | "react-dom": "^0.14.7",
47 | "react-dropzone": "^3.3.2",
48 | "react-leaflet": "^0.10.2",
49 | "react-mixin": "^2.0.2",
50 | "react-notification-system": "^0.2.6",
51 | "react-router": "^2.0.1",
52 | "react-time-picker": "^1.1.0",
53 | "sequelize": "^3.19.3",
54 | "singleton": "^1.0.0",
55 | "socket.io": "^1.4.5",
56 | "socket.io-client": "^1.4.5",
57 | "winston": "^2.2.0"
58 | },
59 | "devDependencies": {
60 | "babel-core": "^6.7.2",
61 | "babel-loader": "^6.2.4",
62 | "babel-plugin-transform-decorators-legacy": "^1.3.4",
63 | "babel-preset-es2015": "^6.6.0",
64 | "babel-preset-react": "^6.5.0",
65 | "babel-preset-stage-0": "^6.5.0",
66 | "bower-webpack-plugin": "^0.1.9",
67 | "browser-sync": "^2.11.2",
68 | "clean-webpack-plugin": "^0.1.8",
69 | "css-loader": "^0.23.1",
70 | "eslint-loader": "^1.3.0",
71 | "extract-text-webpack-plugin": "^1.0.1",
72 | "file-loader": "^0.8.5",
73 | "fs-readdir-recursive": "^1.0.0",
74 | "gulp": "^3.9.1",
75 | "gulp-copy": "0.0.2",
76 | "gulp-debug": "^2.1.2",
77 | "gulp-nodemon": "^2.0.6",
78 | "gulp-sass": "^2.2.0",
79 | "gulp-util": "^3.0.7",
80 | "gulp-zip": "^3.2.0",
81 | "node-sass": "^3.4.2",
82 | "react-hot-loader": "^1.3.0",
83 | "sass-loader": "^3.2.0",
84 | "style-loader": "^0.13.0",
85 | "url-loader": "^0.5.7",
86 | "webpack": "^1.12.14",
87 | "webpack-dev-server": "^1.14.1",
88 | "webpack-manifest-plugin": "^1.0.0",
89 | "wiredep": "^4.0.0"
90 | },
91 | "engines": {
92 | "node": ">=0.10.0"
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/server/models/User.js:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose';
2 | var Schema = mongoose.Schema;
3 | import uniqueValidator from 'mongoose-unique-validator';
4 | import crypto from 'crypto';
5 |
6 | var UserSchema = new Schema({
7 | username: {type: String, unique: true, required: true, uniqueCaseInsensitive: true},
8 | lastLoginAt: Date,
9 | registeredAt: Date,
10 | FirstName: {type: String},
11 | LastName: {type: String},
12 | hashedPassword: String,
13 | isActive: {type: Boolean, default: true},
14 | salt: String,
15 | role: { type: Schema.Types.ObjectId, ref: 'Role', index: true }
16 | }, { timestamps: true });
17 |
18 | /**
19 | * Virtuals
20 | */
21 | UserSchema
22 | .virtual('password')
23 | .set(function (password) {
24 | this._password = password;
25 | this.salt = this.makeSalt();
26 | this.hashedPassword = this.encryptPassword(password);
27 | })
28 | .get(() => {
29 | return this._password;
30 | });
31 |
32 | UserSchema.virtual('passwordConfirmation')
33 | .get(function () {
34 | return this._passwordConfirmation;
35 | })
36 | .set(function (value) {
37 | this._passwordConfirmation = value;
38 | });
39 |
40 | /**
41 | * Validations
42 | */
43 |
44 | // Validate empty password
45 | UserSchema
46 | .path('hashedPassword')
47 | .validate(function (hashedPassword) {
48 | if (this._password || this._passwordConfirmation) {
49 | if (this._password.length < 5) {
50 | this.invalidate('password', 'Password must be at least 5 characters.');
51 | }
52 | if (this._password !== this._passwordConfirmation) {
53 | this.invalidate('passwordConfirmation', 'Password verification mismatch.');
54 | }
55 | }
56 |
57 | if (hashedPassword.length == 0) {
58 | this.invalidate('password', 'Password can not be blank');
59 | }
60 | }, null);
61 |
62 | /**
63 | * Methods
64 | */
65 | UserSchema.methods = {
66 | /**
67 | * Authenticate - check if the passwords are the same
68 | *
69 | * @param {String} plainText
70 | * @return {Boolean}
71 | * @api public
72 | */
73 | authenticate: function (plainText) {
74 | return this.encryptPassword(plainText) === this.hashedPassword;
75 | },
76 |
77 | /**
78 | * Make salt
79 | *
80 | * @return {String}
81 | * @api public
82 | */
83 | makeSalt: function () {
84 | return crypto.randomBytes(16).toString('base64');
85 | },
86 |
87 | /**
88 | * Encrypt password
89 | *
90 | * @param {String} password
91 | * @return {String}
92 | * @api public
93 | */
94 | encryptPassword: function (password) {
95 | if (!password || !this.salt) return '';
96 | var salt = new Buffer(this.salt, 'base64');
97 | return crypto.pbkdf2Sync(password, salt, 10000, 64).toString('base64');
98 | }
99 | };
100 |
101 | UserSchema.plugin(uniqueValidator, {message: 'User already exist for the provided {PATH}.'});
102 | module.exports = mongoose.model('User', UserSchema);
103 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # Full Stack Node + React-Mobx
2 |
3 |
4 | ## Sructure guide
5 |
6 | ```
7 | ├── actions
8 | ├── stores
9 | ├── views
10 | │ ├── Login
11 | │ │ └── Login.js
12 | │ └── Admin
13 | │ ├── lib
14 | │ │ └── components
15 | │ │ └── Avatar.js
16 | └── Sidebar.js
17 | │ ├── views
18 | │ │ ├── Hotels
19 | │ │ │ ├── views
20 | │ │ │ │ ├── List
21 | │ │ │ │ │ └── List.js
22 | │ │ │ │ ├── Create
23 | │ │ │ │ │ └── Create.js
24 | │ │ │ │ ├── Edit
25 | │ │ │ │ │ └── Edit.js
26 | │ │ │ └── Handler.js
27 | │ │ └── Dashboard
28 | │ │ ├── components
29 | │ │ │ ├── Stream.js
30 | │ │ │ ├── StreamItem.js
31 | │ │ │ ├── TodoItem.js
32 | │ │ │ └── TodoList.js
33 | │ │ └── Dashboard.js
34 | │ └── Admin.js
35 | └── index.js
36 |
37 | ```
38 |
39 | ## State management
40 |
41 | Application state management is done using [mobx](http://mobxjs.github.io/mobx/), MobX is one of the least obtrusive libraries you can use for state management.
42 |
43 | `stores/appStore.js`
44 |
45 | ```javascript
46 | import {observable} from 'mobx';
47 |
48 | var appStore = observable({
49 | timer: 0,
50 | resetTimer: function() { this.timer = 0 }
51 | });
52 | ```
53 |
54 | ```javascript
55 | import {observer} from 'mobx-react';
56 | import appStore from './stores/appStore';
57 |
58 | @observer
59 | class TimerView extends React.Component {
60 | render() {
61 | return ();
64 | }
65 |
66 | onReset = () => {
67 | this.props.appStore.resetTimer();
68 | }
69 | };
70 |
71 | setInterval(() => { appStore.timer++ }, 1000); //change state as you change objects, no flux/action creators/reducers loops. Profit!
72 |
73 | React.render(, document.body);
74 | ```
75 |
76 | ## Doing http requests
77 |
78 | [axios](https://github.com/mzabriskie/axios). Ultimately axios is an effort to provide a standalone $http-like service for use outside of Angular.
79 |
80 | ```javascript
81 | import { get, post } from 'axios';
82 |
83 | class Auth {
84 | login(username, password) {
85 | return post('/api/auth/login', {
86 | username, password
87 | })
88 | .then((res) => {
89 | this.user = res.data;
90 | return res;
91 | });
92 | }
93 | }
94 |
95 | ```
96 |
97 | ## Routing in components
98 |
99 | ```javascript
100 | class Admin extends Component {
101 |
102 | handleLogout() {
103 | const { auth } = this.props.store;
104 |
105 | auth.logout().then(() => {
106 | this.context.router.push('login'); // take the router from this.context
107 | });
108 | }
109 |
110 |
111 | renderTopNavBar() {
112 | return (
113 |
114 |
115 |
116 |
117 | Hotel CMS
118 |
119 |
120 |
121 |
124 |
125 |
126 | );
127 | }
128 | }
129 |
130 | // Declare the router as a dependency in the context
131 | Admin.contextTypes = {
132 | router: React.PropTypes.object.isRequired
133 | };
134 | ```
135 |
--------------------------------------------------------------------------------
/server/config/express.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Express configuration
3 | *
4 | * @exports app: configured express instance
5 | */
6 |
7 | import express from 'express';
8 | import ejs from 'ejs';
9 | import morgan from 'morgan';
10 | import compression from 'compression';
11 | import bodyParser from 'body-parser';
12 | import methodOverride from 'method-override';
13 | import cookieParser from 'cookie-parser';
14 | import path from 'path';
15 | import expressWinston from 'express-winston';
16 | import config from 'nconf';
17 | import { getTransportsInstance } from './winston';
18 | import passport from 'passport';
19 | import mongoose from 'mongoose';
20 | import MainRouter from '../routes';
21 |
22 | const app = express();
23 | const env = app.get('env');
24 |
25 | export function init(sessionMiddleware){
26 | app.locals.env = env;
27 |
28 | app.disable('x-powered-by');
29 | app.enable('trust proxy');
30 |
31 | app.set('appRootPath', path.join(process.cwd(), 'client'));
32 | app.use(express.static(path.join(process.cwd(), 'client')));
33 |
34 | /**
35 | * Setup for production only
36 | */
37 | if (env === 'production') {
38 | app.use(morgan('combined'));
39 | }
40 |
41 | /**
42 | * Setup for deployed dev and live
43 | */
44 | if (env === 'production' || env === 'development') {
45 | var manifest = JSON.parse(require('fs').readFileSync(path.join(app.get('appRootPath'), 'dist', 'manifest.json')));
46 | app.locals.manifest = manifest;
47 | }
48 |
49 | /**
50 | * Setup for local dev
51 | */
52 | if (env === 'local') {
53 | app.use(morgan('dev'));
54 | app.locals.BS_DEV_PORT = config.get('BS_DEV_PORT');
55 | }
56 |
57 | app.locals.getAsset = ((asset) => {
58 | if (env === 'production' || env === 'development') {
59 | const manifest = JSON.parse(require('fs').readFileSync(path.join(app.get('appRootPath'), 'dist', 'manifest.json')));
60 | return (asset) => `${manifest['/dist/' + asset]}`;
61 | } else {
62 | return (asset) => `http://localhost:${config.get('WEBPACK_DEV_PORT')}/static/${asset}`;
63 | }
64 | })();
65 |
66 | /**
67 | * Setup view engine EJS
68 | */
69 | app.set('views', [
70 | app.get('appRootPath')
71 | ]);
72 |
73 | app.set('view engine', 'ejs');
74 | app.engine('ejs', ejs.renderFile);
75 |
76 | /**
77 | * compress all requests
78 | */
79 | app.use(compression());
80 |
81 | /**
82 | * parse application/x-www-form-urlencoded
83 | */
84 | app.use(bodyParser.urlencoded({
85 | limit: '50mb',
86 | extended: true
87 | }));
88 |
89 | /**
90 | * parse application/json
91 | */
92 | app.use(bodyParser.json({limit: '50mb'}));
93 |
94 | app.use(methodOverride());
95 |
96 | app.use(cookieParser());
97 |
98 | /**
99 | * Intialize express session
100 | */
101 | app.use(sessionMiddleware);
102 |
103 | /**
104 | * Initialize Passport
105 | */
106 | app.use(passport.initialize());
107 | app.use(passport.session());
108 |
109 | /**
110 | * Mount Application routes
111 | */
112 | app.use(MainRouter);
113 |
114 | /**
115 | * Configure express winston logging
116 | */
117 | app.use(expressWinston.errorLogger({
118 | transports: getTransportsInstance()
119 | }));
120 |
121 | /**
122 | * Error handling middleware
123 | */
124 | app.use(function handleErrors(err, req, res, next) {
125 |
126 | if (env === 'local') console.log(err.stack)
127 | else {
128 | delete err.stack;
129 | }
130 |
131 | var statusCode = err.output ? err.output.statusCode : 500;
132 | if (!res.headersSent) {
133 | res.status(err.status || statusCode).json(err);
134 | }
135 | });
136 | }
137 |
138 | export function getAppInstance(){
139 | return app;
140 | }
141 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Node template
2 | # Logs
3 | logs
4 | *.log
5 | npm-debug.log*
6 |
7 | # Runtime data
8 | pids
9 | *.pid
10 | *.seed
11 |
12 | # Directory for instrumented libs generated by jscoverage/JSCover
13 | lib-cov
14 |
15 | # Coverage directory used by tools like istanbul
16 | coverage
17 |
18 | # Report directory (plato code analysis)
19 | report
20 |
21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
22 | .grunt
23 |
24 | # node-waf configuration
25 | .lock-wscript
26 |
27 | # Compiled binary addons (http://nodejs.org/api/addons.html)
28 | build/Release
29 |
30 | # Dependency directory
31 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
32 | node_modules
33 |
34 |
35 | ### Linux template
36 | *~
37 |
38 | # KDE directory preferences
39 | .directory
40 |
41 | # Linux trash folder which might appear on any partition or disk
42 | .Trash-*
43 |
44 |
45 | ### JetBrains template
46 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion
47 |
48 | *.iml
49 |
50 | ## Directory-based project format:
51 | .idea/
52 | # if you remove the above rule, at least ignore the following:
53 |
54 | # User-specific stuff:
55 | # .idea/workspace.xml
56 | # .idea/tasks.xml
57 | # .idea/dictionaries
58 |
59 | # Sensitive or high-churn files:
60 | # .idea/dataSources.ids
61 | # .idea/dataSources.xml
62 | # .idea/sqlDataSources.xml
63 | # .idea/dynamic.xml
64 | # .idea/uiDesigner.xml
65 |
66 | # Gradle:
67 | # .idea/gradle.xml
68 | # .idea/libraries
69 |
70 | # Mongo Explorer plugin:
71 | # .idea/mongoSettings.xml
72 |
73 | ## File-based project format:
74 | *.ipr
75 | *.iws
76 |
77 | ## Plugin-specific files:
78 |
79 | # IntelliJ
80 | /out/
81 |
82 | # mpeltonen/sbt-idea plugin
83 | .idea_modules/
84 |
85 | # JIRA plugin
86 | atlassian-ide-plugin.xml
87 |
88 | # Crashlytics plugin (for Android Studio and IntelliJ)
89 | com_crashlytics_export_strings.xml
90 | crashlytics.properties
91 | crashlytics-build.properties
92 |
93 |
94 | ### Dreamweaver template
95 | # DW Dreamweaver added files
96 | _notes
97 | dwsync.xml
98 |
99 |
100 | ### Eclipse template
101 | *.pydevproject
102 | .metadata
103 | .gradle
104 | bin/
105 | tmp/
106 | *.tmp
107 | *.bak
108 | *.swp
109 | *~.nib
110 | local.properties
111 | .settings/
112 | .loadpath
113 |
114 | # Eclipse Core
115 | .project
116 |
117 | # External tool builders
118 | .externalToolBuilders/
119 |
120 | # Locally stored "Eclipse launch configurations"
121 | *.launch
122 |
123 | # CDT-specific
124 | .cproject
125 |
126 | # JDT-specific (Eclipse Java Development Tools)
127 | .classpath
128 |
129 | # Java annotation processor (APT)
130 | .factorypath
131 |
132 | # PDT-specific
133 | .buildpath
134 |
135 | # sbteclipse plugin
136 | .target
137 |
138 | # TeXlipse plugin
139 | .texlipse
140 |
141 |
142 | ### Gradle template
143 | .gradle
144 | build/
145 |
146 | # Ignore Gradle GUI config
147 | gradle-app.setting
148 |
149 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
150 | !gradle-wrapper.jar
151 |
152 |
153 | ### OSX template
154 | .DS_Store
155 | .AppleDouble
156 | .LSOverride
157 |
158 | # Icon must end with two \r
159 | Icon
160 |
161 | # Thumbnails
162 | ._*
163 |
164 | # Files that might appear in the root of a volume
165 | .DocumentRevisions-V100
166 | .fseventsd
167 | .Spotlight-V100
168 | .TemporaryItems
169 | .Trashes
170 | .VolumeIcon.icns
171 |
172 | # Directories potentially created on remote AFP share
173 | .AppleDB
174 | .AppleDesktop
175 | Network Trash Folder
176 | Temporary Items
177 | .apdisk
178 |
179 |
180 | ### Vagrant template
181 | .vagrant/
182 |
183 |
184 | ### Windows template
185 | # Windows image file caches
186 | Thumbs.db
187 | ehthumbs.db
188 |
189 | # Folder config file
190 | Desktop.ini
191 |
192 | # Recycle Bin used on file shares
193 | $RECYCLE.BIN/
194 |
195 | # Windows Installer files
196 | *.cab
197 | *.msi
198 | *.msm
199 | *.msp
200 |
201 | # Windows shortcuts
202 | *.lnk
203 |
204 |
205 | node_modules
206 | public
207 | .env
208 | .tmp
209 | .sass-cache
210 | .idea
211 | bower_components
212 | dist
213 | /server/config/local.env.js
214 | /client/app/translations.js
215 |
216 | # cache files for sublime text
217 | *.tmlanguage.cache
218 | *.tmPreferences.cache
219 | *.stTheme.cache
220 |
221 | # workspace files are user-specific
222 | *.sublime-workspace
223 |
224 | # project files should be checked into the repository, unless a significant
225 | # proportion of contributors will probably not be using SublimeText
226 | *.sublime-project
227 |
228 | # css files
229 | client/assets/css/*.css
230 |
231 | # generated icon font
232 | client/assets/fonts/icons
233 | client/assets/sass/_icons.sass
234 | _icons.sass
235 |
236 | # translation
237 | client/translation/po/*
238 | !client/translation/po/.gitkeep
239 | client/translation/po/*
240 |
241 | #redis dump
242 | dump.rdb
243 |
244 | #maxmind data
245 | data
246 | sitemap/*.xml
247 |
248 | # auto generated file
249 | client/assets/sass/_scanned.sass
250 | client/assets/sass/_base
251 | client/assets/sass/_base.sass
252 | /client/assets/images
253 |
--------------------------------------------------------------------------------