├── src
├── client
│ ├── styles
│ │ ├── mixins.less
│ │ ├── variables.less
│ │ └── styles.less
│ ├── images
│ │ ├── busy.gif
│ │ ├── icon.png
│ │ ├── logo.png
│ │ └── default-image.jpg
│ ├── app
│ │ ├── widgets
│ │ │ ├── widgets.module.js
│ │ │ ├── widget-header.html
│ │ │ └── ht-widget-header.directive.js
│ │ ├── blocks
│ │ │ ├── logger
│ │ │ │ ├── logger.module.js
│ │ │ │ └── logger.js
│ │ │ ├── exception
│ │ │ │ ├── exception.module.js
│ │ │ │ ├── exception.js
│ │ │ │ ├── exception-handler.provider.spec.js
│ │ │ │ └── exception-handler.provider.js
│ │ │ └── router
│ │ │ │ ├── router.module.js
│ │ │ │ └── router-helper.provider.js
│ │ ├── login
│ │ │ ├── login.module.js
│ │ │ ├── logout.controller.js
│ │ │ ├── login.controller.js
│ │ │ ├── login.route.js
│ │ │ └── login.html
│ │ ├── signup
│ │ │ ├── signup.module.js
│ │ │ ├── signup_activate.controller.js
│ │ │ ├── signup.controller.js
│ │ │ ├── signup_validation_process.controller.js
│ │ │ ├── signup_validation.controller.js
│ │ │ ├── templates
│ │ │ │ ├── signup_validation_process.html
│ │ │ │ ├── signup_activate.html
│ │ │ │ ├── signup_validation.html
│ │ │ │ ├── signup.html
│ │ │ │ └── signup_profile.html
│ │ │ ├── signup_profile.controller.js
│ │ │ └── signup.route.js
│ │ ├── dashboard
│ │ │ ├── dashboard.module.js
│ │ │ ├── dashboard.route.js
│ │ │ ├── dashboard.controller.js
│ │ │ ├── dashboard.route.spec.js
│ │ │ ├── dashboard.controller.spec.js
│ │ │ └── dashboard.html
│ │ ├── layout
│ │ │ ├── layout.module.js
│ │ │ ├── shell.html
│ │ │ ├── shell.controller.js
│ │ │ ├── ht-top-nav.directive.js
│ │ │ ├── shell.controller.spec.js
│ │ │ ├── sidebar.controller.spec.js
│ │ │ ├── ht-top-nav.html
│ │ │ └── sidebar.controller.js
│ │ ├── home
│ │ │ ├── home.module.js
│ │ │ ├── home.route.js
│ │ │ ├── createTaskModalController.js
│ │ │ ├── editTaskModalController.js
│ │ │ ├── createTaskModal.html
│ │ │ ├── editTaskModal.html
│ │ │ ├── home.html
│ │ │ └── homeController.js
│ │ ├── users
│ │ │ ├── users.module.js
│ │ │ ├── controllers
│ │ │ │ ├── editUserModalController.js
│ │ │ │ ├── addUserModalController.js
│ │ │ │ ├── password_recoveryController.js
│ │ │ │ ├── password_resetController.js
│ │ │ │ └── usersListController.js
│ │ │ ├── templates
│ │ │ │ ├── editUserModal.html
│ │ │ │ ├── addUserModal.html
│ │ │ │ ├── password_recovery.html
│ │ │ │ ├── password_reset.html
│ │ │ │ └── usersList.html
│ │ │ └── users.route.js
│ │ ├── core
│ │ │ ├── constants.js
│ │ │ ├── core.module.js
│ │ │ ├── templates
│ │ │ │ ├── alert.html
│ │ │ │ └── 404.html
│ │ │ ├── directives
│ │ │ │ ├── ng-enter.js
│ │ │ │ └── compare-to.js
│ │ │ ├── filters
│ │ │ │ └── boolFilter.js
│ │ │ ├── core.route.js
│ │ │ ├── services
│ │ │ │ ├── alert.js
│ │ │ │ ├── users.js
│ │ │ │ └── tasks.js
│ │ │ ├── controllers
│ │ │ │ └── alert.js
│ │ │ ├── core.route.spec.js
│ │ │ ├── config.js
│ │ │ └── interceptors
│ │ │ │ └── request.js
│ │ └── app.module.js
│ └── test-helpers
│ │ └── mock-data.js
└── server
│ ├── start.js
│ ├── templates
│ ├── recovery-password-email
│ │ ├── style.scss
│ │ ├── text.hbs
│ │ └── html.hbs
│ ├── validate-email
│ │ ├── style.scss
│ │ ├── text.hbs
│ │ └── html.hbs
│ ├── welcome-email
│ │ ├── style.scss
│ │ ├── text.hbs
│ │ └── html.hbs
│ └── _common.scss
│ ├── index.js
│ ├── start_clusters.js
│ ├── tests
│ ├── mocha.opts
│ ├── helpers.js
│ └── routes
│ │ ├── index.js
│ │ ├── users.js
│ │ ├── token.js
│ │ └── signup.js
│ ├── config
│ ├── config.js
│ ├── logger.js
│ ├── env
│ │ ├── test.js
│ │ ├── production.js
│ │ └── development.js
│ ├── db.js
│ ├── acl.js
│ ├── auth.js
│ ├── certs
│ │ ├── mean.cert
│ │ └── mean.key
│ ├── boot.js
│ └── express.js
│ ├── services
│ ├── core.js
│ ├── tokens.js
│ ├── tasks.js
│ ├── sessions.js
│ ├── email.js
│ ├── signup.js
│ └── users.js
│ ├── error.js
│ ├── fixtures
│ ├── user.js
│ └── admins.js
│ ├── routes
│ ├── index.js
│ ├── core.js
│ ├── token.js
│ ├── signup.js
│ ├── social.js
│ └── tasks.js
│ ├── clusters.js
│ └── models
│ ├── tasks.js
│ ├── sessions.js
│ └── users.js
├── .babelrc
├── Procfile
├── .eslintignore
├── .bowerrc
├── .editorconfig
├── .htmlhintrc
├── .gitignore
├── .travis.yml
├── .eslintrc
├── bower.json
├── karma.conf.js
├── README.md
├── .snyk
├── gulp.config.js
└── package.json
/src/client/styles/mixins.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | { "presets": ["es2015"] }
2 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: node node_modules/gulp/bin/gulp serve-build
2 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/**
2 | bower_components/**
3 | *.spec.js
4 |
--------------------------------------------------------------------------------
/src/server/start.js:
--------------------------------------------------------------------------------
1 | require('babel-register');
2 | require('./index.js');
3 |
--------------------------------------------------------------------------------
/src/server/templates/recovery-password-email/style.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
--------------------------------------------------------------------------------
/src/server/index.js:
--------------------------------------------------------------------------------
1 | import app from './config/boot';
2 |
3 | module.exports = app();
4 |
--------------------------------------------------------------------------------
/src/server/start_clusters.js:
--------------------------------------------------------------------------------
1 | require('babel-register');
2 | require('./clusters.js');
3 |
--------------------------------------------------------------------------------
/src/client/images/busy.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toptive/express-angular-starter/HEAD/src/client/images/busy.gif
--------------------------------------------------------------------------------
/src/client/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toptive/express-angular-starter/HEAD/src/client/images/icon.png
--------------------------------------------------------------------------------
/src/client/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toptive/express-angular-starter/HEAD/src/client/images/logo.png
--------------------------------------------------------------------------------
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "bower_components",
3 | "scripts": {
4 | "postinstall": "gulp wiredep"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/src/server/tests/mocha.opts:
--------------------------------------------------------------------------------
1 | --require tests/helpers
2 | --reporter spec
3 | --compilers js:babel-register
4 | --slow 5000
5 |
--------------------------------------------------------------------------------
/src/client/app/widgets/widgets.module.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular.module('app.widgets', []);
5 | }());
6 |
--------------------------------------------------------------------------------
/src/client/images/default-image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/toptive/express-angular-starter/HEAD/src/client/images/default-image.jpg
--------------------------------------------------------------------------------
/src/client/app/blocks/logger/logger.module.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular.module('blocks.logger', []);
5 | }());
6 |
--------------------------------------------------------------------------------
/src/client/app/login/login.module.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular.module('app.login', [
5 | 'app.core',
6 | ]);
7 | }());
8 |
--------------------------------------------------------------------------------
/src/client/app/signup/signup.module.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular.module('app.signup', [
5 | 'app.core',
6 | ]);
7 | }());
8 |
--------------------------------------------------------------------------------
/src/client/app/blocks/exception/exception.module.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular.module('blocks.exception', ['blocks.logger']);
5 | }());
6 |
--------------------------------------------------------------------------------
/src/client/app/dashboard/dashboard.module.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular.module('app.dashboard', [
5 | 'app.core',
6 | ]);
7 | }());
8 |
--------------------------------------------------------------------------------
/src/client/app/layout/layout.module.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular.module('app.layout', ['app.core', 'ui.bootstrap.collapse']);
5 | }());
6 |
--------------------------------------------------------------------------------
/src/client/app/home/home.module.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular.module('app.home', [
5 | 'app.core',
6 | 'ui.bootstrap',
7 | ]);
8 | }());
9 |
--------------------------------------------------------------------------------
/src/client/app/users/users.module.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular.module('app.users', [
5 | 'app.core',
6 | 'ui.bootstrap',
7 | ]);
8 | }());
9 |
--------------------------------------------------------------------------------
/src/server/config/config.js:
--------------------------------------------------------------------------------
1 | const env = (process.env.NODE_ENV) ? process.env.NODE_ENV : 'development';
2 | const config = require('./env/' + env + '.js');
3 |
4 | module.exports = config;
5 |
--------------------------------------------------------------------------------
/src/client/app/blocks/router/router.module.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular.module('blocks.router', [
5 | 'ui.router',
6 | 'blocks.logger',
7 | ]);
8 | }());
9 |
--------------------------------------------------------------------------------
/src/server/templates/validate-email/style.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 |
3 | body {
4 | background-color: #ddd;
5 | color: white;
6 | }
7 |
8 | h1 {
9 | text-align: center;
10 | }
11 |
--------------------------------------------------------------------------------
/src/server/templates/welcome-email/style.scss:
--------------------------------------------------------------------------------
1 | @import '../common';
2 |
3 | body {
4 | background-color: #ddd;
5 | color: white;
6 | }
7 |
8 | h1 {
9 | text-align: center;
10 | }
11 |
--------------------------------------------------------------------------------
/src/server/templates/welcome-email/text.hbs:
--------------------------------------------------------------------------------
1 | Hey {{firstName}} {{lastName}} ({{username}}), welcome to MEAN.
2 |
3 | To start working with us please enter to {{url}}
4 |
5 | Regards, MEAN team.
6 |
--------------------------------------------------------------------------------
/src/server/templates/validate-email/text.hbs:
--------------------------------------------------------------------------------
1 | Hey {{username}}, welcome to MEAN.
2 |
3 | Please follow the link below to complete the validation of the password
4 |
5 | {{url}}
6 |
7 | Regards, MEAN team.
8 |
--------------------------------------------------------------------------------
/src/server/services/core.js:
--------------------------------------------------------------------------------
1 | const service = {};
2 |
3 | service.getIndexMessage = function () {
4 | return Promise.resolve({
5 | status: 'mean API',
6 | });
7 | };
8 |
9 | module.exports = service;
10 |
--------------------------------------------------------------------------------
/src/server/templates/recovery-password-email/text.hbs:
--------------------------------------------------------------------------------
1 | Hey {{username}}, welcome to MEAN.
2 |
3 | Please follow the link below to complete to recover your password
4 |
5 | {{url}}
6 |
7 |
8 | Regards, MEAN team.
9 |
--------------------------------------------------------------------------------
/src/client/app/core/constants.js:
--------------------------------------------------------------------------------
1 | /* global toastr:false, moment:false */
2 | (function () {
3 | 'use strict';
4 |
5 | angular
6 | .module('app.core')
7 | .constant('toastr', toastr)
8 | .constant('moment', moment);
9 | }());
10 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.htmlhintrc:
--------------------------------------------------------------------------------
1 | {
2 | "tagname-lowercase": true,
3 | "attr-lowercase": true,
4 | "attr-value-double-quotes": false,
5 | "tag-pair": true,
6 | "id-unique": true,
7 | "src-not-empty": true,
8 | "attr-no-duplication": true,
9 | "spec-char-escape": true
10 | }
11 |
--------------------------------------------------------------------------------
/src/server/tests/helpers.js:
--------------------------------------------------------------------------------
1 | import supertest from 'supertest';
2 | import chai from 'chai';
3 | import dirtyChai from 'dirty-chai';
4 | import app from '../index.js';
5 |
6 | chai.use(dirtyChai);
7 |
8 | global.app = app;
9 | global.request = supertest(app);
10 | global.expect = chai.expect;
11 |
--------------------------------------------------------------------------------
/src/client/app/app.module.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular.module('app', [
5 | 'app.core',
6 | 'app.widgets',
7 | 'app.login',
8 | 'app.signup',
9 | 'app.layout',
10 | 'app.users',
11 | 'app.home',
12 | 'app.dashboard',
13 | ]);
14 | }());
15 |
--------------------------------------------------------------------------------
/src/client/app/layout/shell.html:
--------------------------------------------------------------------------------
1 |
5 |
6 | Hey {{firstName}} {{lastName}} ({{username}}), welcome to MEAN.
7 |
8 |
9 | To start working with us please click
10 |
11 |
Here
12 |
13 |
14 |
15 |
18 |
--------------------------------------------------------------------------------
/src/client/styles/variables.less:
--------------------------------------------------------------------------------
1 | // Variables
2 |
3 | @gray-darker: lighten(#000, 13.5%);
4 | @gray-dark: lighten(#000, 20%);
5 | @gray: lighten(#000, 33.5%);
6 | @gray-light: lighten(#000, 60%);
7 | @gray-lighter: lighten(#000, 93.5%);
8 | @gray-lightest: lighten(#000, 97.25%);
9 | @brand-primary: #428bca;
10 | @brand-success: #5cb85c;
11 | @brand-info: #5bc0de;
12 | @brand-warning: #f0ad4e;
13 | @brand-danger: #d9534f;
14 |
15 | @color_blue: #1171a3;
16 | @color_important: #fa3031;
17 |
--------------------------------------------------------------------------------
/src/server/templates/validate-email/html.hbs:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | Hey {{username}}, welcome to MEAN.
7 |
8 |
9 | Please follow the link below to complete the validation of the password
10 |
11 |
Here
12 |
13 |
14 |
15 |
18 |
--------------------------------------------------------------------------------
/src/server/templates/recovery-password-email/html.hbs:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 | Hey {{username}}, welcome to MEAN.
7 |
8 |
9 | Please follow the link below to complete to recover your password
10 |
11 |
Here
12 |
13 |
14 |
15 |
18 |
--------------------------------------------------------------------------------
/src/server/config/env/test.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | database: 'mean_relational_test',
3 | username: '',
4 | password: '',
5 | params: {
6 | dialect: 'sqlite',
7 | storage: 'mean_relational_test.sqlite',
8 | logging: false,
9 | define: {
10 | underscored: true,
11 | },
12 | },
13 | jwtSecret: 'Mean-relational-test',
14 | jwtSession: { session: false },
15 | emailService: 'Gmail',
16 | auth: {
17 | user: '',
18 | pass: '',
19 | },
20 | verifyEmail: true,
21 | urlBaseClient: 'https://localhost:9000',
22 | urlBaseApi: 'https://localhost:3000',
23 | };
24 |
--------------------------------------------------------------------------------
/src/client/app/core/directives/ng-enter.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 | /*
4 | This directive allows us to pass a function in on an enter key to do what we want.
5 | */
6 | angular
7 | .module('app.core')
8 | .directive('ngEnter', ngEnter);
9 |
10 | function ngEnter() {
11 | return function (scope, element, attrs) {
12 | element.bind('keydown keypress', (event) => {
13 | if (event.which === 13) {
14 | scope.$apply(() => {
15 | scope.$eval(attrs.ngEnter);
16 | });
17 | event.preventDefault();
18 | }
19 | });
20 | };
21 | }
22 | }());
23 |
--------------------------------------------------------------------------------
/src/server/routes/index.js:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import coreRoutes from './core';
3 | import signupRoutes from './signup';
4 | import socialRoutes from './social';
5 | import tasksRoutes from './tasks';
6 | import tokenRoutes from './token';
7 | import usersRoutes from './users';
8 |
9 | const router = express.Router(); // eslint-disable-line new-cap
10 |
11 | // mount all routes at /
12 | router.use('/', coreRoutes);
13 | router.use('/', signupRoutes);
14 | router.use('/', socialRoutes);
15 | router.use('/', tasksRoutes);
16 | router.use('/', tokenRoutes);
17 | router.use('/', usersRoutes);
18 |
19 | export default router;
20 |
--------------------------------------------------------------------------------
/src/client/app/layout/shell.controller.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('app.layout')
6 | .controller('ShellController', ShellController);
7 |
8 | ShellController.$inject = ['$rootScope', '$timeout', 'config', 'logger'];
9 | /* @ngInject */
10 | function ShellController($rootScope, $timeout, config, logger) {
11 | const vm = this;
12 | vm.busyMessage = 'Please wait ...';
13 | vm.isBusy = true;
14 | $rootScope.showSplash = false;
15 | vm.navline = {
16 | title: config.appTitle,
17 | };
18 |
19 | activate();
20 |
21 | function activate() {
22 | }
23 | }
24 | }());
25 |
--------------------------------------------------------------------------------
/src/server/clusters.js:
--------------------------------------------------------------------------------
1 | import cluster from 'cluster';
2 | import os from 'os';
3 |
4 | const CPUS = os.cpus();
5 | if (cluster.isMaster) {
6 | CPUS.forEach(() => cluster.fork());
7 | cluster.on('listening', worker => {
8 | console.log('Cluster %d connected', worker.process.pid);
9 | });
10 | cluster.on('disconnect', worker => {
11 | console.log('Cluster %d disconnected', worker.process.pid);
12 | });
13 | cluster.on('exit', worker => {
14 | console.log('Cluster %d is dead', worker.process.pid);
15 | cluster.fork();
16 | // Ensure to starts a new cluster if an old one dies
17 | });
18 | } else {
19 | require('./index.js');
20 | }
21 |
--------------------------------------------------------------------------------
/src/client/app/core/directives/compare-to.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('app.core')
6 | .directive('compareTo', compareTo);
7 |
8 | function compareTo() {
9 | return {
10 | require: 'ngModel',
11 | scope: {
12 | otherModelValue: '=compareTo',
13 | },
14 | link: (scope, element, attributes, ngModel) => {
15 | ngModel.$validators.compareTo = (modelValue) => {
16 | return modelValue === scope.otherModelValue;
17 | };
18 |
19 | scope.$watch('otherModelValue', () => {
20 | ngModel.$validate();
21 | });
22 | },
23 | };
24 | }
25 | }());
26 |
--------------------------------------------------------------------------------
/src/client/app/core/filters/boolFilter.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('app.core')
6 | .filter('boolFilter', boolFilter);
7 |
8 | boolFilter.$inject = ['$sce'];
9 | /* @ngInject */
10 | function boolFilter($sce) {
11 | return function (input) {
12 | let output = $sce.trustAsHtml('
16 | const directive = {
17 | scope: {
18 | 'title': '@',
19 | 'subtitle': '@',
20 | 'rightText': '@',
21 | 'allowCollapse': '@',
22 | },
23 | templateUrl: 'app/widgets/widget-header.html',
24 | restrict: 'EA',
25 | link,
26 | };
27 | return directive;
28 |
29 | function link(scope, element, attr) {
30 | scope.toggleContent = function () {
31 | if (scope.allowCollapse === 'true') {
32 | const content = angular.element(element).siblings('.widget-content');
33 | content.toggle();
34 | }
35 | };
36 | }
37 | }
38 | }());
39 |
--------------------------------------------------------------------------------
/src/server/config/auth.js:
--------------------------------------------------------------------------------
1 | import passport from 'passport';
2 | import { Strategy, ExtractJwt } from 'passport-jwt';
3 |
4 | import config from './config';
5 | import sessionsService from './../services/sessions';
6 |
7 |
8 | const params = {
9 | secretOrKey: config.jwtSecret,
10 | jwtFromRequest: ExtractJwt.fromAuthHeader(),
11 | };
12 |
13 | const strategy = new Strategy(params, (payload, done) => {
14 | sessionsService.validateSession(payload)
15 | .then(Session => {
16 | if (!Session) {
17 | return done(null, false);
18 | }
19 |
20 | return Session.getUser()
21 | .then(User => {
22 | if (!User) {
23 | return done(null, false);
24 | }
25 |
26 | return done(null, { User, Session });
27 | });
28 | })
29 | .catch(error => {
30 | done(error, null);
31 | });
32 | });
33 |
34 | passport.use(strategy);
35 |
36 | export default class Auth {
37 | static initialize() {
38 | return passport.initialize();
39 | }
40 |
41 | static authenticate(cb) {
42 | return passport.authenticate('jwt', config.jwtSession, cb);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/client/app/layout/ht-top-nav.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
8 |
28 |
29 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "parser" : "babel-eslint",
4 | "extends" : [
5 | "airbnb/base"
6 | ],
7 | "plugins" : [
8 | "flow-vars"
9 | ],
10 | "env" : {
11 | "es6": true,
12 | "node": true,
13 | "mocha": true
14 | },
15 | globals: {
16 | app: true,
17 | expect: true,
18 | request: true,
19 | by: true,
20 | element: true,
21 | browser: true,
22 | jasmine: true,
23 | inject: true,
24 | angular: true,
25 | ApplicationConfiguration: true,
26 | io: true,
27 | $: true,
28 | moment: true,
29 | },
30 | "rules": {
31 | "prefer-arrow-callback": 1,
32 | "semi" : [2, "always"],
33 | "no-param-reassign": [2, {"props": false}],
34 | "func-names": 0,
35 | "no-unused-expressions": 0,
36 | "no-console": 0,
37 | "one-var": 0,
38 | "new-cap": 0,
39 | "quote-props": 0,
40 | "prefer-template": 0,
41 | "arrow-body-style": 0,
42 | "no-empty-label": 0,
43 | "no-labels": 2,
44 | "no-unused-vars": [2, { "args": "none" }],
45 | "no-use-before-define": [2, { "functions": false, "classes": true }]
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/client/app/core/controllers/alert.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('app.home')
6 | .controller('AlertController', AlertController);
7 |
8 | AlertController.$inject = ['$uibModalInstance', 'options'];
9 | /* @ngInject */
10 | function AlertController($uibModalInstance, options) {
11 | const vm = this;
12 | vm.options = {
13 | title: 'Alert',
14 | body: 'Are you sure?',
15 | warning: null,
16 | };
17 |
18 | vm.accept = accept;
19 | vm.cancel = cancel;
20 |
21 | activate();
22 |
23 | function activate() {
24 | if (options) {
25 | if (options.title) {
26 | vm.options.title = options.title;
27 | }
28 |
29 | if (options.body) {
30 | vm.options.body = options.body;
31 | }
32 |
33 | if (options.warning) {
34 | vm.options.warning = options.warning;
35 | }
36 | }
37 | }
38 |
39 | function accept() {
40 | $uibModalInstance.close();
41 | }
42 |
43 | function cancel() {
44 | $uibModalInstance.dismiss('cancel');
45 | }
46 | }
47 | }());
48 |
--------------------------------------------------------------------------------
/src/client/app/core/templates/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
404 Page Not Found
11 |
12 |
13 |
14 |
15 |
16 |
17 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/client/app/dashboard/dashboard.controller.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('app.dashboard')
6 | .controller('DashboardController', DashboardController);
7 |
8 | DashboardController.$inject = ['taskservice', 'usersservice'];
9 | /* @ngInject */
10 | function DashboardController(taskservice, usersservice) {
11 | const vm = this;
12 | vm.title = 'Dashboard';
13 |
14 | activate();
15 |
16 | function activate() {
17 | // Get users count
18 | usersservice.getCount()
19 | .then(count => {
20 | vm.userCount = count;
21 | });
22 |
23 | // Get all task count
24 | taskservice.getCount()
25 | .then(count => {
26 | vm.taskCount = count;
27 | });
28 |
29 |
30 | // Get complete tasks count
31 | taskservice.getCountDone()
32 | .then(count => {
33 | vm.taskDoneCount = count;
34 | });
35 |
36 | // Get not done tasks count
37 | taskservice.getCountNotDone()
38 | .then(count => {
39 | vm.taskNotDoneCount = count;
40 | });
41 | }
42 | }
43 | }());
44 |
--------------------------------------------------------------------------------
/src/server/config/certs/mean.cert:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIC8zCCAdugAwIBAgIJAMltM6AKqPQxMA0GCSqGSIb3DQEBBQUAMBAxDjAMBgNV
3 | BAMMBW50YXNrMB4XDTE1MTAwNDE2MzIzNVoXDTI1MTAwMTE2MzIzNVowEDEOMAwG
4 | A1UEAwwFbnRhc2swggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDJ6P38
5 | AGoIpHpgOGdEYfh0/Tf5OL0XPFh2PK+wWsCCZzgmL5RUnfV+Gw3wR/rwfHeOJSXy
6 | cSwq7UJe0Rf0EnMULBpp+oeb2iGqbcAke7DTOzaihcp27l+zzNK/BvXq/BtI7Plm
7 | 4mziIuCf3QOeQemkPEiztGZKEJeEE/AFSZmx4J1ugJ0G+fqjkMWTXcYj0RWlqG2y
8 | LBmDFErQANaUeYOrjdSIp96MBFLC8afYnNp/XrItm1XqQ29mUpbwRw2qnR+WdM6S
9 | YZiNcEo/eXLDBnLYiJjSqoiY5hd6CiCO3yMv4IT/LdR1V+FTDg5KecQ7WD5/IKIA
10 | 44844xZzVGkgt3xPAgMBAAGjUDBOMB0GA1UdDgQWBBTAx3g/qsJRyaYpDS6AGTaT
11 | f43bLTAfBgNVHSMEGDAWgBTAx3g/qsJRyaYpDS6AGTaTf43bLTAMBgNVHRMEBTAD
12 | AQH/MA0GCSqGSIb3DQEBBQUAA4IBAQC2Pwv5W/DQ2OFmHsGPVRwGBY0UP0PuTxpX
13 | 4rOe9ZtJDi2QRybA59iEdTT3pe7FX+LPd0DUiJswwYF8+BYDVjJRHiIdcFDC7Jqb
14 | nk3g1JdxzzMdhPpexHkkYzU8Zaxl1wkp3KDlrfqRD2dYSzaToiR8UjkDAwjXcfmA
15 | 489HPKBtZLWydbV/3J9EW/I6zmKgampdIn0qOp6FlCN2dgfarpD8bUxruLgN2wFu
16 | Bb8g3hzQo5ZC7Ws98TIh0B8MFidzMuLjqr1azm147wUr/NfnXS1a4w7/r302sGwa
17 | OuNmGHImnUpAnVpjV482M/DdUy5VYP0M45ImzX+rbJOUffFobx6E
18 | -----END CERTIFICATE-----
19 |
--------------------------------------------------------------------------------
/src/client/app/blocks/logger/logger.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('blocks.logger')
6 | .factory('logger', logger);
7 |
8 | logger.$inject = ['$log', 'toastr'];
9 |
10 | /* @ngInject */
11 | function logger($log, toastr) {
12 | const service = {
13 | showToasts: true,
14 |
15 | error,
16 | info,
17 | success,
18 | warning,
19 |
20 | // straight to console; bypass toastr
21 | log: $log.log,
22 | };
23 |
24 | return service;
25 | // ///////////////////
26 |
27 | function error(message, data, title) {
28 | toastr.error(message, title);
29 | $log.error('Error: ' + message, data);
30 | }
31 |
32 | function info(message, data, title) {
33 | toastr.info(message, title);
34 | $log.info('Info: ' + message, data);
35 | }
36 |
37 | function success(message, data, title) {
38 | toastr.success(message, title);
39 | $log.info('Success: ' + message, data);
40 | }
41 |
42 | function warning(message, data, title) {
43 | toastr.warning(message, title);
44 | $log.warn('Warning: ' + message, data);
45 | }
46 | }
47 | }());
48 |
--------------------------------------------------------------------------------
/src/client/app/core/core.route.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint no-undef: 0*/
2 | describe('core', function () {
3 | describe('state', function () {
4 | const views = {
5 | four0four: 'app/core/templates/404.html',
6 | };
7 |
8 | beforeEach(function () {
9 | module('app.core', bard.fakeToastr);
10 | bard.inject('$location', '$httpBackend', '$rootScope', '$state', '$templateCache');
11 | $templateCache.put(views.four0four, '');
12 | });
13 |
14 | afterEach(() => {
15 | $httpBackend.verifyNoOutstandingExpectation(false);
16 | $httpBackend.verifyNoOutstandingRequest();
17 | });
18 |
19 | it('should map /404 route to 404 View template', function () {
20 | expect($state.get('404').templateUrl).to.equal(views.four0four);
21 | });
22 |
23 | it('of dashboard should work with $state.go', function () {
24 | $location.path('/404');
25 | $rootScope.$apply();
26 | expect($state.is('404'));
27 | });
28 |
29 | it('should route /invalid to the otherwise (404) route', function () {
30 | $location.path('/invalid');
31 | $rootScope.$apply();
32 | expect($state.current.templateUrl).to.equal(views.four0four);
33 | });
34 | });
35 | });
36 |
--------------------------------------------------------------------------------
/src/client/app/users/controllers/password_resetController.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('app.users')
6 | .controller('PasswordResetController', PasswordResetController);
7 |
8 | PasswordResetController.$inject = ['$state', '$stateParams', 'logger', 'authentication'];
9 | /* @ngInject */
10 | function PasswordResetController($state, $stateParams, logger, authentication) {
11 | const vm = this;
12 | vm.title = 'Password Reset';
13 | vm.credentials = {
14 | password: '',
15 | verifyPassword: '',
16 | };
17 | vm.reset = reset;
18 |
19 | activate();
20 |
21 | function activate() {
22 | if (!$stateParams.tokenId) {
23 | logger.error('Invalid reset token');
24 | }
25 | authentication.token($stateParams.tokenId)
26 | .then(data => {
27 | logger.success(data.msg);
28 | })
29 | .catch(err => {
30 | $state.go('login');
31 | });
32 | }
33 |
34 | function reset(form) {
35 | if (form.$valid) {
36 | authentication.reset($stateParams.tokenId, vm.credentials).then(data => {
37 | logger.success(data.msg);
38 | $state.go('login');
39 | });
40 | }
41 | }
42 | }
43 | }());
44 |
--------------------------------------------------------------------------------
/src/client/app/home/editTaskModal.html:
--------------------------------------------------------------------------------
1 |
4 |
25 |
26 |
30 |
--------------------------------------------------------------------------------
/src/client/app/signup/templates/signup_activate.html:
--------------------------------------------------------------------------------
1 |
28 |
32 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mean-relational",
3 | "version": "0.0.2",
4 | "description": "mean-relational",
5 | "authors": [],
6 | "license": "MIT",
7 | "ignore": [
8 | "**/.*",
9 | "node_modules",
10 | "bower_components",
11 | "test",
12 | "tests"
13 | ],
14 | "devDependencies": {
15 | "angular-mocks": "^1.5.3",
16 | "sinon": "http://sinonjs.org/releases/sinon-1.12.1.js",
17 | "bardjs": "^0.1.8"
18 | },
19 | "dependencies": {
20 | "jquery": "^2.2.2",
21 | "angular": "~1.5.3",
22 | "angular-sanitize": "~1.5.3",
23 | "bootstrap": "^3.3.6",
24 | "font-awesome": "^4.5.0",
25 | "moment": "momentjs#^2.15.2",
26 | "angular-ui-router": "^0.2.18",
27 | "toastr": "^2.1.2",
28 | "angular-animate": "^1.5.3",
29 | "angular-bootstrap": "^1.2.5",
30 | "ngstorage": "^0.3.11",
31 | "angular-chart.js": "^1.0.3",
32 | "angular-loading-bar": "^0.9.0",
33 | "angular-wysiwyg": "^1.2.4",
34 | "components-font-awesome": "^4.6.3",
35 | "angular-filepicker": "https://github.com/andreskuver/filepicker-angular.git#1.1.5",
36 | "angular-moment": "^1.0.0"
37 | },
38 | "resolutions": {
39 | "angular": "1.5.3",
40 | "jquery": "^2.2.2"
41 | },
42 | "overrides": {
43 | "bootstrap": {
44 | "main": [
45 | "dist/css/bootstrap.css",
46 | "dist/js/bootstrap.js"
47 | ]
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/client/app/signup/templates/signup_validation.html:
--------------------------------------------------------------------------------
1 |
32 |
36 |
--------------------------------------------------------------------------------
/src/server/services/tokens.js:
--------------------------------------------------------------------------------
1 | import usersService from './../services/users';
2 | import sessionsService from './../services/sessions';
3 |
4 | const service = {};
5 |
6 | /**
7 | * Do user signin, store new session for the logged user
8 | * with Authorization token and expiration date
9 | */
10 | service.signin = (credentials) => {
11 | if (!credentials || !credentials.identification || !credentials.password) {
12 | return Promise.reject(new Error('Incomplete Credentials'));
13 | }
14 |
15 | const where = {
16 | $or: [{
17 | username: credentials.identification,
18 | }, {
19 | email: credentials.identification,
20 | }],
21 | };
22 |
23 | return usersService.findUser(where, true)
24 | .then(User => {
25 | if (!User || !User.isPassword(credentials.password)) {
26 | throw new Error('Invalid Username or Password');
27 | }
28 |
29 | return sessionsService.removeExpiredSessions(User)
30 | .then(() => {
31 | return sessionsService.createNewSession(User);
32 | })
33 | .then(Session => {
34 | return { User, Session };
35 | });
36 | });
37 | };
38 |
39 | /**
40 | * Do user signout, remove current user session from storage
41 | */
42 | service.signout = (Session) => {
43 | return sessionsService.removeSession(Session)
44 | .then(() => {
45 | return { msg: 'Signout Successfully' };
46 | });
47 | };
48 |
49 | /**
50 | * Finish all user sessions
51 | */
52 | service.endSessions = (User) => {
53 | return sessionsService.removeAllSessions(User)
54 | .then(() => {
55 | return { msg: 'Sessions closed' };
56 | });
57 | };
58 |
59 | module.exports = service;
60 |
--------------------------------------------------------------------------------
/src/client/app/core/config.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | const core = angular.module('app.core');
5 |
6 | core.config(toastrConfig);
7 |
8 | toastrConfig.$inject = ['toastr'];
9 | /* @ngInject */
10 | function toastrConfig(toastr) {
11 | toastr.options.timeOut = 4000;
12 | toastr.options.positionClass = 'toast-bottom-right';
13 | }
14 |
15 | core.config(loadingBarConfig);
16 |
17 | loadingBarConfig.$inject = ['cfpLoadingBarProvider'];
18 | /* @ngInject */
19 | function loadingBarConfig(cfpLoadingBarProvider) {
20 | // cfpLoadingBarProvider.includeSpinner = false;
21 | }
22 |
23 | const config = {
24 | appErrorPrefix: '[Mean Relational Error]',
25 | appTitle: 'Mean Relational',
26 | };
27 |
28 | core.value('config', config);
29 |
30 | core.config(configure);
31 |
32 | configure.$inject = [
33 | '$httpProvider', '$logProvider', 'routerHelperProvider', 'exceptionHandlerProvider',
34 | ];
35 | /* @ngInject */
36 | function configure($httpProvider, $logProvider, routerHelperProvider, exceptionHandlerProvider) {
37 | if ($logProvider.debugEnabled) {
38 | $logProvider.debugEnabled(true);
39 | }
40 | exceptionHandlerProvider.configure(config.appErrorPrefix);
41 | routerHelperProvider.configure({ docTitle: config.appTitle + ': ' });
42 | $httpProvider.interceptors.push('RequestInterceptor');
43 | }
44 |
45 | core.config(filePickerConfig);
46 |
47 | filePickerConfig.$inject = ['filepickerProvider'];
48 | /* @ngInject */
49 | function filePickerConfig(filepickerProvider) {
50 | filepickerProvider.setKey('AFOad6qXmRXCJ7qZt0tMez');
51 | }
52 | }());
53 |
--------------------------------------------------------------------------------
/src/server/services/tasks.js:
--------------------------------------------------------------------------------
1 | import Tasks from './../models/tasks';
2 |
3 | const service = {};
4 |
5 | service.getAll = (user) => {
6 | return Tasks.findAll({
7 | where: { user_id: user.id },
8 | });
9 | };
10 |
11 | service.getPaginated = (user, params) => {
12 | const query = {};
13 | query.where = { user_id: user.id };
14 | buildPagination(params, query);
15 | return Tasks.findAndCountAll(query);
16 | };
17 |
18 | service.getCount = (params) => {
19 | const query = {};
20 |
21 | if (params && params.done) {
22 | query.where = {
23 | done: params.done,
24 | };
25 | }
26 |
27 | return Tasks.count(query);
28 | };
29 |
30 | service.create = (task) => {
31 | return Tasks.create(task);
32 | };
33 |
34 | service.findById = (id, user) => {
35 | const query = { where: { id } };
36 |
37 | if (user) {
38 | query.where.user_id = user.id;
39 | }
40 |
41 | return Tasks.findOne(query);
42 | };
43 |
44 | service.update = (id, task, user) => {
45 | const query = { where: { id } };
46 |
47 | if (user) {
48 | query.where.user_id = user.id;
49 | }
50 |
51 | return Tasks.update(task, query);
52 | };
53 |
54 | service.destroy = (id, user) => {
55 | const query = { where: { id } };
56 |
57 | if (user) {
58 | query.where.user_id = user.id;
59 | }
60 |
61 | return Tasks.destroy(query);
62 | };
63 |
64 | function buildPagination(params, query) {
65 | query.limit = 10;
66 | query.offset = 0;
67 |
68 | if (params.offset) {
69 | query.offset = parseInt(params.offset, 10) * parseInt(params.limit, 10);
70 | }
71 |
72 | if (params.limit) {
73 | query.limit = parseInt(params.limit, 10);
74 | }
75 | }
76 |
77 | module.exports = service;
78 |
--------------------------------------------------------------------------------
/src/client/app/layout/sidebar.controller.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 'use strict';
3 |
4 | angular
5 | .module('app.layout')
6 | .controller('SidebarController', SidebarController);
7 |
8 | SidebarController.$inject = ['$scope', '$state', 'routerHelper', 'authentication'];
9 | /* @ngInject */
10 | function SidebarController($scope, $state, routerHelper, authentication) {
11 | const vm = this;
12 | const states = routerHelper.getStates();
13 | let role = authentication.getUser() ? authentication.getUser().role : 'guest';
14 | vm.isCurrent = isCurrent;
15 | vm.logout = authentication.logout;
16 |
17 | activate();
18 |
19 | $scope.$on('user-login', (user) => {
20 | role = authentication.getUser() ? authentication.getUser().role : 'guest';
21 | activate();
22 | });
23 |
24 | $scope.$on('user-logout', () => {
25 | role = 'guest';
26 | activate();
27 | });
28 |
29 | function activate() {
30 | vm.user = authentication.getUser();
31 | getNavRoutes();
32 | }
33 |
34 | function getNavRoutes() {
35 | vm.navRoutes = states.filter(r => {
36 | if (r.settings && !r.settings.roles) {
37 | console.log('Please configure roles', r);
38 | }
39 | return r.settings && r.settings.nav && r.settings.roles.indexOf(role) !== -1;
40 | }).sort((r1, r2) => {
41 | return r1.settings.nav - r2.settings.nav;
42 | });
43 | }
44 |
45 | function isCurrent(route) {
46 | if (!route.title || !$state.current || !$state.current.title) {
47 | return '';
48 | }
49 | return $state.current.title.substr(0, route.title.length) === route.title ? 'current' : '';
50 | }
51 | }
52 | }());
53 |
--------------------------------------------------------------------------------
/src/client/app/users/templates/password_recovery.html:
--------------------------------------------------------------------------------
1 |