From ae0b147c668a4da7d8a8c5ae930d7d134def8722 Mon Sep 17 00:00:00 2001 From: Oleg Polyakov Date: Wed, 24 Jan 2018 14:09:11 +0300 Subject: [PATCH 1/7] Add passport and github authentication --- README.md | 36 +++++++++++++++++++++++- config/index.js | 10 ++++++- controllers/auth.js | 39 ++++++-------------------- controllers/oauth.js | 11 ++++++++ controllers/topic.js | 10 +++++++ index.js | 11 ++++++-- middleware/auth.js | 17 ----------- models/user.js | 8 +++++- package-lock.json | 56 +++++++++++++++++++++++++++++++++++++ package.json | 3 ++ public/css/app.css | 13 +++++++++ routers/auth.js | 18 ++++++++---- services/db.js | 2 +- services/index.js | 12 ++++++++ services/passport/github.js | 20 +++++++++++++ services/passport/index.js | 15 ++++++++++ services/passport/local.js | 34 ++++++++++++++++++++++ styles/base.scss | 4 +++ styles/pages/_index.scss | 3 +- styles/pages/error.scss | 16 +++++++++++ views/auth/login.pug | 6 ++-- views/auth/register.pug | 6 ++-- views/error.pug | 2 -- 23 files changed, 283 insertions(+), 69 deletions(-) create mode 100644 controllers/oauth.js create mode 100644 services/index.js create mode 100644 services/passport/github.js create mode 100644 services/passport/index.js create mode 100644 services/passport/local.js create mode 100644 styles/pages/error.scss diff --git a/README.md b/README.md index 8249961..993f0cb 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,37 @@ # CodeLibrary -Демо-проект для курса "Node.js в действии" \ No newline at end of file +Демо-проект для курса "Node.js в действии". +Приложение представляет из себя библиотеку книг. + +## Основные маршруты + +* `/` - главная страница +* Вход и регистрация + * `/auth/register` - регистрация + * `/auth/login` - вход +* Профиль пользователя + * `/profile` - профиль пользователя + * `/profile/edit` - редактирование профиля +* Книги + * `/books` - книгами + * `/books/new` - новые книги + * `/books/best` - лучшие книги + * `/books/search` - поиск + * `/books/:topic` - книги по теме + * `/books/:book` - книга +* Администратор + * `/admin` - главная страница + * `/admin/login` - вход + * Книги + * `/admin/books` - список книг + * `/admin/books/create` - создание книги + * `/admin/books/:book/update` - редактирование книги + * `/admin/books/:book/delete` - удаление книги + * Темы + * `/admin/topics` - список тем + * `/admin/topics/create` - создание темы + * `/admin/topics/:book/update` - редактирование темы + * `/admin/topics/:book/delete` - удаление темы + * Пользователи + * `/admin/users` - список пользователей + * `/admin/users/:user` - пользователь \ No newline at end of file diff --git a/config/index.js b/config/index.js index 14e9050..b1a0e0d 100644 --- a/config/index.js +++ b/config/index.js @@ -6,12 +6,20 @@ module.exports = { port: process.env.PORT || 3000, sessionSecret: 'HacJmB3ma6crKKtK', mongodbUri: { - development: 'mongodb://localhost:27017/codedojo' + local: 'mongodb://localhost:27017/codedojo', + mlab: process.env.MONGODB_MLAB_URL }, paths: { views: path.resolve(__dirname, '..', 'views'), public: path.resolve(__dirname, '..', 'public'), favicon: path.resolve(__dirname, '..', 'public', 'favicon.ico'), lib: path.resolve(__dirname, '..', 'node_modules') + }, + oauth: { + github: { + clientID: process.env.GITHUB_CLIENT_ID, + clientSecret: process.env.GITHUB_CLIENT_SECRET, + callbackURL: process.env.GITHUB_CALLBACK_URL + } } }; \ No newline at end of file diff --git a/controllers/auth.js b/controllers/auth.js index 91208ef..e34b4c3 100644 --- a/controllers/auth.js +++ b/controllers/auth.js @@ -1,4 +1,4 @@ -const { user: User } = require('../models'); +const { passport } = require('../services'); module.exports = { // GET /auth/register @@ -20,37 +20,16 @@ module.exports = { }, // POST /auth/register - register(req, res, next) { - let { email, password, confirmPassword } = req.body; - - if (!email || !password) return next(new Error('Необходимо ввести email и пароль')); - else if (password !== confirmPassword) return next (new Error('Пароли не совпадают')); - - User.create({ email, password }) - .then(user => { - req.session.userId = user.id; - res.redirect('/profile'); - }) - .catch(next); - }, + register: passport.authenticate('local-register', { + failureRedirect: '/auth/register', + successRedirect: '/profile' + }), // POST /auth/login - login(req, res, next) { - let { email, password } = req.body; - - if (!email || !password) { - let error = new Error('Необходимо ввести логин и пароль'); - error.status = 401; - return next(error); - } - - User.authenticate(email, password) - .then(user => { - req.session.userId = user.id; - res.redirect('/profile'); - }) - .catch(next); - }, + login: passport.authenticate('local-login', { + failureRedirect: '/auth/login', + successRedirect: '/profile' + }), // GET /auth/logout logout(req, res, next) { diff --git a/controllers/oauth.js b/controllers/oauth.js new file mode 100644 index 0000000..c4b9355 --- /dev/null +++ b/controllers/oauth.js @@ -0,0 +1,11 @@ +const { passport } = require('../services'); + +module.exports = { + github: { + authenticate: passport.authenticate('github'), + callback: passport.authenticate('github', { + failureRedirect: '/auth/login', + successRedirect: '/profile' + }) + } +}; \ No newline at end of file diff --git a/controllers/topic.js b/controllers/topic.js index 074bd3a..39834df 100644 --- a/controllers/topic.js +++ b/controllers/topic.js @@ -11,5 +11,15 @@ module.exports = { next(); }) .catch(next); + }, + + findTopic(req, res, next, id) { + Topic.findById(id) + .then(topic => { + topic.req = topic; + + next(); + }) + .catch(next); } }; \ No newline at end of file diff --git a/index.js b/index.js index 55a0f35..6ccf3f5 100644 --- a/index.js +++ b/index.js @@ -4,7 +4,7 @@ const favicon = require('serve-favicon'); const session = require('express-session'); const MongoStore = require('connect-mongo')(session); -const db = require('./services/db'); +const { db, passport } = require('./services'); const config = require('./config'); const { error, auth } = require('./middleware'); const routers = require('./routers'); @@ -44,7 +44,14 @@ app.use(session({ }) })); -app.use(auth.findUser); +app.use(passport.initialize()); +app.use(passport.session()); + +app.use((req, res, next) => { + res.locals.user = req.user; + + next(); +}); app.use('/', routers.main); app.use('/auth', routers.auth); diff --git a/middleware/auth.js b/middleware/auth.js index 61bc81c..d235170 100644 --- a/middleware/auth.js +++ b/middleware/auth.js @@ -1,21 +1,4 @@ -const { user: User } = require('../models'); - module.exports = { - findUser(req, res, next) { - if (req.session) { - User.findById(req.session.userId) - .then(user => { - req.user = user; - res.locals.user = user; - - next(); - }) - .catch(); - } else { - next(); - } - }, - authenticated(req, res, next) { if (req.user) return next(); diff --git a/models/user.js b/models/user.js index 65263d3..fb84975 100644 --- a/models/user.js +++ b/models/user.js @@ -11,7 +11,9 @@ const User = new mongoose.Schema({ maxlength: [256, 'Адрес электронный почты слишком длинный.'], match: [/^[a-zA-Z0-9'._%+-]+@[a-zA-Z0-9-][a-zA-Z0-9.-]*\.[a-zA-Z]{2,63}$/, 'Неверный формат адреса электронной почты.'] }, - password: { type: String, required: true } + password: { type: String, required: true }, + username: String, + photo: String }, { timestamps: true }); @@ -35,6 +37,10 @@ User.post('save', function(error, user, next) { } }); +User.methods.isCorrectPassword = function(password) { + return bcrypt.compare(password, this.password); +}; + User.statics.authenticate = function(email, password) { return this.findOne({ email }) .then(user => { diff --git a/package-lock.json b/package-lock.json index ba65e42..1c94a0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4683,6 +4683,11 @@ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, + "oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE=" + }, "oauth-sign": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", @@ -4984,6 +4989,47 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "passport": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz", + "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=", + "requires": { + "passport-strategy": "1.0.0", + "pause": "0.0.1" + } + }, + "passport-github": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/passport-github/-/passport-github-1.1.0.tgz", + "integrity": "sha1-jOHj/NYa11eOsd9ZWDnkrqEjVdQ=", + "requires": { + "passport-oauth2": "1.4.0" + } + }, + "passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", + "requires": { + "passport-strategy": "1.0.0" + } + }, + "passport-oauth2": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.4.0.tgz", + "integrity": "sha1-9i+BWDy+EmCb585vFguTlaJ7hq0=", + "requires": { + "oauth": "0.9.15", + "passport-strategy": "1.0.0", + "uid2": "0.0.3", + "utils-merge": "1.0.1" + } + }, + "passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" + }, "path-exists": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", @@ -5046,6 +5092,11 @@ "pinkie-promise": "2.0.1" } }, + "pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + }, "pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", @@ -6631,6 +6682,11 @@ "random-bytes": "1.0.0" } }, + "uid2": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz", + "integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I=" + }, "unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", diff --git a/package.json b/package.json index e43846a..40fb900 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,9 @@ "mongoose": "^5.0.1", "morgan": "^1.9.0", "normalize.css": "^7.0.0", + "passport": "^0.4.0", + "passport-github": "^1.1.0", + "passport-local": "^1.0.0", "pug": "^2.0.0-rc.4", "serve-favicon": "^2.4.5" } diff --git a/public/css/app.css b/public/css/app.css index fea5f74..77e3c58 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -396,6 +396,9 @@ template { body { background-color: rgba(0, 0, 0, 0.05); } +.mdc-card { + background-color: white; } + #content { display: -webkit-box; display: -ms-flexbox; @@ -428,3 +431,13 @@ body { display: 100%; max-width: 200px; display: block; } + +#error-page #main { + max-width: 1024px; + margin: 2em auto; + text-align: center; } + #error-page #main .error-icon { + color: grey; + font-size: 5em; } + #error-page #main pre { + text-align: left; } diff --git a/routers/auth.js b/routers/auth.js index 24d4ccf..d398805 100644 --- a/routers/auth.js +++ b/routers/auth.js @@ -1,18 +1,24 @@ const router = require('express').Router(); const { auth: { authenticated, unauthenticated } } = require('../middleware'); -const { auth: controller } = require('../controllers'); +const { + auth: authController, + oauth: oauthController +} = require('../controllers'); router.route('/register') .all(unauthenticated) - .get(controller.showRegisterPage) - .post(controller.register); + .get(authController.showRegisterPage) + .post(authController.register); router.route('/login') .all(unauthenticated) - .get(controller.showLoginPage) - .post(controller.login); + .get(authController.showLoginPage) + .post(authController.login); -router.get('/logout', authenticated, controller.logout); +router.get('/github', oauthController.github.authenticate); +router.get('/github/callback', oauthController.github.callback); + +router.get('/logout', authenticated, authController.logout); module.exports = router; \ No newline at end of file diff --git a/services/db.js b/services/db.js index 7879a15..e53516b 100644 --- a/services/db.js +++ b/services/db.js @@ -4,7 +4,7 @@ const { mongodbUri } = require('../config'); mongoose.Promise = global.Promise; -mongoose.connect(mongodbUri.development); +mongoose.connect(mongodbUri.mlab); mongoose.connection.on('error', console.error.bind(console, 'MongoDB connection error:')); mongoose.connection.once('open', () => console.log('Connected to MongoDB')); diff --git a/services/index.js b/services/index.js new file mode 100644 index 0000000..697d939 --- /dev/null +++ b/services/index.js @@ -0,0 +1,12 @@ +const fs = require('fs'); +const path = require('path'); + +let index = path.basename(__filename); +let files = fs.readdirSync(__dirname); + +for (let file of files) { + if (file !== index) { + let name = path.basename(file, '.js'); + module.exports[name] = require(`./${file}`); + } +} \ No newline at end of file diff --git a/services/passport/github.js b/services/passport/github.js new file mode 100644 index 0000000..69a9d4f --- /dev/null +++ b/services/passport/github.js @@ -0,0 +1,20 @@ +const passport = require('passport'); +const { Strategy: GitHubStrategy } = require('passport-github'); +const { user: User } = require('../../models'); + +const config = require('../../config'); + +passport.use(new GitHubStrategy(config.oauth.github, (accessToken, refreshToken, profile, done) => { + if (!profile.emails) return done(new Error('Для входа необходимо получить от GitHub email')); + + let email = profile.emails[0].value; + let username = profile.username; + let photo = profile.photos[0].value; + + + User.findOneAndUpdate({ email }, { + email, + username, + photo + }, { upsert: true, new: true }, done); +})); \ No newline at end of file diff --git a/services/passport/index.js b/services/passport/index.js new file mode 100644 index 0000000..46fa77f --- /dev/null +++ b/services/passport/index.js @@ -0,0 +1,15 @@ +const passport = require('passport'); + +const { user: User } = require('../../models'); +require('./local'); +require('./github'); + +passport.serializeUser((user, done) => { + done(null, user._id); +}); + +passport.deserializeUser((userId, done) => { + User.findById(userId, done); +}); + +module.exports = passport; \ No newline at end of file diff --git a/services/passport/local.js b/services/passport/local.js new file mode 100644 index 0000000..c2f80cb --- /dev/null +++ b/services/passport/local.js @@ -0,0 +1,34 @@ +const passport = require('passport'); +const { Strategy: LocalStrategy } = require('passport-local'); + +const { user: User } = require('../../models'); + +const options = { + usernameField: 'email', + passwordField: 'password', + passReqToCallback: true +}; + +passport.use('local-register', new LocalStrategy(options, (req, email, password, done) => { + if (password !== req.confirmPassword) return done(new Error('Пароли не совпадают')); + + User.create({ email, password }) + .then(user => done(null, user)) + .catch(done); +})); + +passport.use('local-login', new LocalStrategy(options, (req, email, password, done) => { + User.findOne({ email }) + .then(user => { + if (!user) return done(null, false); + + user.isCorrectPassword(password) + .then(isEqual => { + if (!isEqual) return done(null, false); + + done(null, user); + }) + .catch(done); + }) + .catch(done); +})); \ No newline at end of file diff --git a/styles/base.scss b/styles/base.scss index 5f9c479..17bfcb3 100644 --- a/styles/base.scss +++ b/styles/base.scss @@ -1,3 +1,7 @@ body { background-color: rgba(0, 0, 0, 0.05); +} + +.mdc-card { + background-color: white; } \ No newline at end of file diff --git a/styles/pages/_index.scss b/styles/pages/_index.scss index 5764c53..cbbfd73 100644 --- a/styles/pages/_index.scss +++ b/styles/pages/_index.scss @@ -1 +1,2 @@ -@import 'auth/index'; \ No newline at end of file +@import 'auth/index'; +@import 'error'; \ No newline at end of file diff --git a/styles/pages/error.scss b/styles/pages/error.scss new file mode 100644 index 0000000..561e426 --- /dev/null +++ b/styles/pages/error.scss @@ -0,0 +1,16 @@ +#error-page { + #main { + max-width: 1024px; + margin: 2em auto; + text-align: center; + + .error-icon { + color: grey; + font-size: 5em; + } + + pre { + text-align: left; + } + } +} \ No newline at end of file diff --git a/views/auth/login.pug b/views/auth/login.pug index 939669e..5e83d94 100644 --- a/views/auth/login.pug +++ b/views/auth/login.pug @@ -15,7 +15,5 @@ block content footer.mdc-card__actions button.mdc-button.mdc-card__action.mdc-button--raised.mdc-button--dense(type='submit') Войти - a.mdc-button.mdc-card__action.mdc-button--dense(href='/auth/register') Зарегистрироваться - - section.mdc-card__actions - a.mdc-button.mdc-card__action.mdc-button--dense(href='/auth/reset-password') Восстановить пароль \ No newline at end of file + a.mdc-button.mdc-card__action.mdc-button--dense(href='/auth/github') GitHub + a.mdc-button.mdc-card__action.mdc-button--dense(href='/auth/register') Зарегистрироваться \ No newline at end of file diff --git a/views/auth/register.pug b/views/auth/register.pug index 163a97d..e1782f6 100644 --- a/views/auth/register.pug +++ b/views/auth/register.pug @@ -9,11 +9,11 @@ block content section.mdc-card__supporting-text div.mdc-text-field - input.mdc-text-field__input(type='email' name='email' placeholder='Электронная почта') + input.mdc-text-field__input(type='email' name='email' placeholder='Электронная почта' required) div.mdc-text-field - input.mdc-text-field__input(type='password' name='password' placeholder='Пароль') + input.mdc-text-field__input(type='password' name='password' placeholder='Пароль' required) div.mdc-text-field - input.mdc-text-field__input(type='password' name='confirmPassword' placeholder='Подтвердите пароль') + input.mdc-text-field__input(type='password' name='confirmPassword' placeholder='Подтвердите пароль' required) footer.mdc-card__actions button.mdc-button.mdc-card__action.mdc-button--raised.mdc-button--dense(type='submit') Зарегистрироваться diff --git a/views/error.pug b/views/error.pug index 984cedb..d7fa8fa 100644 --- a/views/error.pug +++ b/views/error.pug @@ -1,7 +1,5 @@ extends _layout -block header - block content main#main i.error-icon.material-icons error From 6f0dd947c1ee140b994331eb7af9f31ade293f80 Mon Sep 17 00:00:00 2001 From: Oleg Polyakov Date: Thu, 8 Feb 2018 00:21:14 +0300 Subject: [PATCH 2/7] Implenet REST API --- README.md | 4 +- admin/controllers/book.js | 19 +- admin/controllers/home.js | 2 +- admin/controllers/index.js | 17 +- admin/controllers/topic.js | 12 +- admin/index.js | 7 +- admin/middleware/auth.js | 9 + admin/middleware/index.js | 3 + admin/routers/book.js | 27 +- admin/routers/home.js | 6 +- admin/routers/index.js | 17 +- admin/routers/topic.js | 18 +- admin/views/_layout.pug | 1 + admin/views/books/delete.pug | 7 + admin/views/books/form.pug | 41 +++ admin/views/books/index.pug | 15 +- admin/views/index.pug | 2 +- admin/views/topics/delete.pug | 2 +- admin/views/topics/form.pug | 7 +- admin/views/topics/index.pug | 5 +- admin/views/users/index.pug | 11 + api/controllers/book.js | 62 ++++ api/controllers/index.js | 3 + api/index.js | 29 ++ api/routers/auth.js | 23 ++ api/routers/book.js | 19 ++ api/routers/index.js | 4 + client/index.html | 20 ++ client/package-lock.json | 165 ++++++++++ client/package.json | 15 + gulpfile.js | 11 +- index.js | 71 ++--- {controllers => main/controllers}/auth.js | 6 +- {controllers => main/controllers}/book.js | 2 +- main/controllers/index.js | 8 + {controllers => main/controllers}/main.js | 2 +- {controllers => main/controllers}/oauth.js | 4 +- {controllers => main/controllers}/search.js | 0 {controllers => main/controllers}/topic.js | 2 +- main/index.js | 29 ++ main/middleware/auth.js | 13 + main/middleware/index.js | 3 + {routers => main/routers}/auth.js | 8 +- {routers => main/routers}/book.js | 0 main/routers/index.js | 8 + {routers => main/routers}/main.js | 0 {routers => main/routers}/search.js | 0 {routers => main/routers}/topic.js | 0 routers/profile.js => main/routers/user.js | 0 {views => main/views}/_includes/sidenav.pug | 0 {views => main/views}/_includes/toolbar.pug | 4 +- main/views/_layout.pug | 13 + {views => main/views}/_mixins/book-card.pug | 0 {views => main/views}/auth/login.pug | 0 {views => main/views}/auth/register.pug | 0 {views => main/views}/books/book.pug | 0 {views => main/views}/books/index.pug | 0 {views => main/views}/index.pug | 0 {views => main/views}/profile/index.pug | 0 middleware/auth.js | 13 - middleware/index.js | 12 - models/index.js | 12 - package-lock.json | 283 +++++++++++++++++- package.json | 7 + routers/index.js | 12 - services/index.js | 12 - {config => shared/config}/index.js | 11 +- shared/data/index.js | 3 + shared/data/languages.json | 4 + {middleware => shared/middleware}/error.js | 11 +- shared/middleware/index.js | 3 + {models => shared/models}/book.js | 22 +- shared/models/index.js | 6 + {models => shared/models}/publisher.js | 0 {models => shared/models}/topic.js | 0 {models => shared/models}/user.js | 31 +- {public => shared/public}/css/app.css | 0 {public => shared/public}/favicon.ico | 0 {public => shared/public}/img/library.jpg | Bin .../public}/img/topics/angular.svg | 0 .../public}/img/topics/angularjs.svg | 0 .../public}/img/topics/aspnet.svg | 0 .../public}/img/topics/babel.svg | 0 .../public}/img/topics/bootstrap.svg | 0 .../public}/img/topics/bower.svg | 0 .../public}/img/topics/csharp.svg | 0 {public => shared/public}/img/topics/css.svg | 0 .../public}/img/topics/dotnet.svg | 0 {public => shared/public}/img/topics/ef.svg | 0 .../public}/img/topics/express.svg | 0 .../public}/img/topics/foundation.svg | 0 {public => shared/public}/img/topics/git.svg | 0 .../public}/img/topics/github.svg | 0 {public => shared/public}/img/topics/gulp.svg | 0 .../public}/img/topics/haskell.svg | 0 .../public}/img/topics/html-css.svg | 0 {public => shared/public}/img/topics/html.svg | 0 {public => shared/public}/img/topics/java.svg | 0 .../public}/img/topics/javascript.svg | 0 .../public}/img/topics/jquery.svg | 0 {public => shared/public}/img/topics/json.svg | 0 .../public}/img/topics/laravel.svg | 0 {public => shared/public}/img/topics/mean.svg | 0 .../public}/img/topics/microsoft.net.svg | 0 .../public}/img/topics/mongodb.svg | 0 .../public}/img/topics/mysql.svg | 0 .../public}/img/topics/nodejs.svg | 0 {public => shared/public}/img/topics/php.svg | 0 .../public}/img/topics/programming.svg | 0 .../public}/img/topics/python.svg | 0 .../public}/img/topics/react.svg | 0 .../public}/img/topics/redux.svg | 0 .../public}/img/topics/regex.svg | 0 {public => shared/public}/img/topics/ruby.svg | 0 {public => shared/public}/img/topics/sass.svg | 0 .../public}/img/topics/sqlserver.svg | 0 .../public}/img/topics/typescript.svg | 0 {public => shared/public}/img/topics/uwp.svg | 0 {public => shared/public}/img/topics/vs.svg | 0 {public => shared/public}/img/topics/vue.svg | 0 .../public}/img/topics/web-apps.svg | 0 .../public}/img/topics/webpack.svg | 0 .../public}/img/topics/windows.svg | 0 .../public}/img/topics/wordpress.svg | 0 {public => shared/public}/img/topics/xaml.svg | 0 {services => shared/services}/db.js | 2 +- {controllers => shared/services}/index.js | 0 .../services}/passport/github.js | 2 +- .../services}/passport/index.js | 3 +- shared/services/passport/jwt.js | 19 ++ .../services}/passport/local.js | 4 +- {styles => shared/styles}/base.scss | 0 .../styles}/components/_index.scss | 0 .../styles}/components/book-card.scss | 0 {styles => shared/styles}/index.scss | 2 - {styles => shared/styles}/layout.scss | 0 {styles => shared/styles}/pages/_index.scss | 0 .../styles}/pages/auth/_index.scss | 0 .../styles}/pages/auth/common.scss | 0 .../styles}/pages/auth/login.scss | 0 .../styles}/pages/auth/register.scss | 0 {styles => shared/styles}/pages/error.scss | 0 shared/utils/error.js | 10 + shared/utils/format.js | 9 + {views => shared/views}/_layout.pug | 7 +- shared/views/_mixins/form.pug | 3 + .../views}/_mixins/mdc/button.pug | 0 {views => shared/views}/_mixins/mdc/index.pug | 0 .../views}/_mixins/mdc/textfield.pug | 2 +- {views => shared/views}/error.pug | 0 utils/format.js | 7 - views/404.pug | 4 - views/dev.pug | 0 views/profile/edit.pug | 0 154 files changed, 1019 insertions(+), 253 deletions(-) create mode 100644 admin/middleware/auth.js create mode 100644 admin/middleware/index.js create mode 100644 admin/views/books/delete.pug create mode 100644 admin/views/books/form.pug create mode 100644 admin/views/users/index.pug create mode 100644 api/controllers/book.js create mode 100644 api/controllers/index.js create mode 100644 api/index.js create mode 100644 api/routers/auth.js create mode 100644 api/routers/book.js create mode 100644 api/routers/index.js create mode 100644 client/index.html create mode 100644 client/package-lock.json create mode 100644 client/package.json rename {controllers => main/controllers}/auth.js (88%) rename {controllers => main/controllers}/book.js (97%) create mode 100644 main/controllers/index.js rename {controllers => main/controllers}/main.js (81%) rename {controllers => main/controllers}/oauth.js (68%) rename {controllers => main/controllers}/search.js (100%) rename {controllers => main/controllers}/topic.js (90%) create mode 100644 main/index.js create mode 100644 main/middleware/auth.js create mode 100644 main/middleware/index.js rename {routers => main/routers}/auth.js (67%) rename {routers => main/routers}/book.js (100%) create mode 100644 main/routers/index.js rename {routers => main/routers}/main.js (100%) rename {routers => main/routers}/search.js (100%) rename {routers => main/routers}/topic.js (100%) rename routers/profile.js => main/routers/user.js (100%) rename {views => main/views}/_includes/sidenav.pug (100%) rename {views => main/views}/_includes/toolbar.pug (83%) create mode 100644 main/views/_layout.pug rename {views => main/views}/_mixins/book-card.pug (100%) rename {views => main/views}/auth/login.pug (100%) rename {views => main/views}/auth/register.pug (100%) rename {views => main/views}/books/book.pug (100%) rename {views => main/views}/books/index.pug (100%) rename {views => main/views}/index.pug (100%) rename {views => main/views}/profile/index.pug (100%) delete mode 100644 middleware/auth.js delete mode 100644 middleware/index.js delete mode 100644 models/index.js delete mode 100644 routers/index.js delete mode 100644 services/index.js rename {config => shared/config}/index.js (62%) create mode 100644 shared/data/index.js create mode 100644 shared/data/languages.json rename {middleware => shared/middleware}/error.js (66%) create mode 100644 shared/middleware/index.js rename {models => shared/models}/book.js (66%) create mode 100644 shared/models/index.js rename {models => shared/models}/publisher.js (100%) rename {models => shared/models}/topic.js (100%) rename {models => shared/models}/user.js (66%) rename {public => shared/public}/css/app.css (100%) rename {public => shared/public}/favicon.ico (100%) rename {public => shared/public}/img/library.jpg (100%) rename {public => shared/public}/img/topics/angular.svg (100%) rename {public => shared/public}/img/topics/angularjs.svg (100%) rename {public => shared/public}/img/topics/aspnet.svg (100%) rename {public => shared/public}/img/topics/babel.svg (100%) rename {public => shared/public}/img/topics/bootstrap.svg (100%) rename {public => shared/public}/img/topics/bower.svg (100%) rename {public => shared/public}/img/topics/csharp.svg (100%) rename {public => shared/public}/img/topics/css.svg (100%) rename {public => shared/public}/img/topics/dotnet.svg (100%) rename {public => shared/public}/img/topics/ef.svg (100%) rename {public => shared/public}/img/topics/express.svg (100%) rename {public => shared/public}/img/topics/foundation.svg (100%) rename {public => shared/public}/img/topics/git.svg (100%) rename {public => shared/public}/img/topics/github.svg (100%) rename {public => shared/public}/img/topics/gulp.svg (100%) rename {public => shared/public}/img/topics/haskell.svg (100%) rename {public => shared/public}/img/topics/html-css.svg (100%) rename {public => shared/public}/img/topics/html.svg (100%) rename {public => shared/public}/img/topics/java.svg (100%) rename {public => shared/public}/img/topics/javascript.svg (100%) rename {public => shared/public}/img/topics/jquery.svg (100%) rename {public => shared/public}/img/topics/json.svg (100%) rename {public => shared/public}/img/topics/laravel.svg (100%) rename {public => shared/public}/img/topics/mean.svg (100%) rename {public => shared/public}/img/topics/microsoft.net.svg (100%) rename {public => shared/public}/img/topics/mongodb.svg (100%) rename {public => shared/public}/img/topics/mysql.svg (100%) rename {public => shared/public}/img/topics/nodejs.svg (100%) rename {public => shared/public}/img/topics/php.svg (100%) rename {public => shared/public}/img/topics/programming.svg (100%) rename {public => shared/public}/img/topics/python.svg (100%) rename {public => shared/public}/img/topics/react.svg (100%) rename {public => shared/public}/img/topics/redux.svg (100%) rename {public => shared/public}/img/topics/regex.svg (100%) rename {public => shared/public}/img/topics/ruby.svg (100%) rename {public => shared/public}/img/topics/sass.svg (100%) rename {public => shared/public}/img/topics/sqlserver.svg (100%) rename {public => shared/public}/img/topics/typescript.svg (100%) rename {public => shared/public}/img/topics/uwp.svg (100%) rename {public => shared/public}/img/topics/vs.svg (100%) rename {public => shared/public}/img/topics/vue.svg (100%) rename {public => shared/public}/img/topics/web-apps.svg (100%) rename {public => shared/public}/img/topics/webpack.svg (100%) rename {public => shared/public}/img/topics/windows.svg (100%) rename {public => shared/public}/img/topics/wordpress.svg (100%) rename {public => shared/public}/img/topics/xaml.svg (100%) rename {services => shared/services}/db.js (94%) rename {controllers => shared/services}/index.js (100%) rename {services => shared/services}/passport/github.js (92%) rename {services => shared/services}/passport/index.js (81%) create mode 100644 shared/services/passport/jwt.js rename {services => shared/services}/passport/local.js (92%) rename {styles => shared/styles}/base.scss (100%) rename {styles => shared/styles}/components/_index.scss (100%) rename {styles => shared/styles}/components/book-card.scss (100%) rename {styles => shared/styles}/index.scss (70%) rename {styles => shared/styles}/layout.scss (100%) rename {styles => shared/styles}/pages/_index.scss (100%) rename {styles => shared/styles}/pages/auth/_index.scss (100%) rename {styles => shared/styles}/pages/auth/common.scss (100%) rename {styles => shared/styles}/pages/auth/login.scss (100%) rename {styles => shared/styles}/pages/auth/register.scss (100%) rename {styles => shared/styles}/pages/error.scss (100%) create mode 100644 shared/utils/error.js create mode 100644 shared/utils/format.js rename {views => shared/views}/_layout.pug (83%) create mode 100644 shared/views/_mixins/form.pug rename {views => shared/views}/_mixins/mdc/button.pug (100%) rename {views => shared/views}/_mixins/mdc/index.pug (100%) rename {views => shared/views}/_mixins/mdc/textfield.pug (79%) rename {views => shared/views}/error.pug (100%) delete mode 100644 utils/format.js delete mode 100644 views/404.pug delete mode 100644 views/dev.pug delete mode 100644 views/profile/edit.pug diff --git a/README.md b/README.md index 993f0cb..3203cf2 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ * `/auth/register` - регистрация * `/auth/login` - вход * Профиль пользователя - * `/profile` - профиль пользователя - * `/profile/edit` - редактирование профиля + * `/user` - страница пользователя + * `/user/edit` - редактирование пользователя * Книги * `/books` - книгами * `/books/new` - новые книги diff --git a/admin/controllers/book.js b/admin/controllers/book.js index 0d47a25..0a86156 100644 --- a/admin/controllers/book.js +++ b/admin/controllers/book.js @@ -1,4 +1,4 @@ -const { book: Book } = require('../../models'); +const { Book} = require('../../shared/models'); module.exports = { findBook(req, res, next, id) { @@ -17,6 +17,7 @@ module.exports = { .catch(next); }, + // GET /admin/books showIndexPage(req, res, next) { Book.find() .then(books => { @@ -27,36 +28,44 @@ module.exports = { .catch(next); }, + // GET /admin/books/create showCreatePage(req, res) { res.render('books/form', { - book: new Book() + book: new Book(), + topics: req.topics }); }, + // GET /admin/books/:book/update showUpdatePage(req, res) { res.render('books/form', { - book: req.book + book: req.book, + topics: req.topics }); }, + // GET /admin/books/:book/update showDeletePage(req, res) { res.render('books/delete', { book: req.book }); }, + // POST /admin/books createBook(req, res, next) { Book.create(req.body) .then(() => res.redirect('/admin/books')) .catch(next); }, + // POST /admin/books/:book/update updateBook(req, res, next) { - Book.findOneAndUpdate({ _id: req.topic.id }, req.body) - .then(topic => res.redirect(`/admin/books/${topic.id}/update`)) + Book.findOneAndUpdate({ _id: req.book.id }, Book.validateBody(req.body), { new: true }) + .then(book => res.redirect(`/admin/books/${book.id}/update`)) .catch(next); }, + // POST /admin/books/:book/update deleteBook(req, res, next) { req.book.remove() .then(() => res.redirect('/admin/books')) diff --git a/admin/controllers/home.js b/admin/controllers/home.js index 31dcaa2..1060439 100644 --- a/admin/controllers/home.js +++ b/admin/controllers/home.js @@ -1,5 +1,5 @@ module.exports = { showIndexPage(req, res) { - + res.render('index'); } }; \ No newline at end of file diff --git a/admin/controllers/index.js b/admin/controllers/index.js index 697d939..dd1ab34 100644 --- a/admin/controllers/index.js +++ b/admin/controllers/index.js @@ -1,12 +1,5 @@ -const fs = require('fs'); -const path = require('path'); - -let index = path.basename(__filename); -let files = fs.readdirSync(__dirname); - -for (let file of files) { - if (file !== index) { - let name = path.basename(file, '.js'); - module.exports[name] = require(`./${file}`); - } -} \ No newline at end of file +module.exports = { + book: require('./book'), + home: require('./home'), + topic: require('./topic') +}; \ No newline at end of file diff --git a/admin/controllers/topic.js b/admin/controllers/topic.js index 88300ab..17cf85e 100644 --- a/admin/controllers/topic.js +++ b/admin/controllers/topic.js @@ -1,6 +1,16 @@ -const { topic: Topic } = require('../../models'); +const { Topic } = require('../../shared/models'); module.exports = { + findTopics(req, res, next) { + Topic.find() + .then(topics => { + req.topics = topics; + + next(); + }) + .catch(next); + }, + findTopic(req, res, next, id) { Topic.findById(id) .then(topic => { diff --git a/admin/index.js b/admin/index.js index 5e6fad9..9268dfe 100644 --- a/admin/index.js +++ b/admin/index.js @@ -1,10 +1,11 @@ const express = require('express'); const path = require('path'); -const admin = express(); - +const middleware = require('./middleware'); const routers = require('./routers'); +const admin = express(); + admin.set('views', path.join(__dirname, 'views')); admin.set('view engine', 'pug'); @@ -12,6 +13,8 @@ admin.on('mount', server => { admin.locals = Object.assign(server.locals, admin.locals); }); +admin.use(middleware.auth.allowAdmin); + admin.use('/', routers.home); admin.use('/books', routers.book); admin.use('/topics', routers.topic); diff --git a/admin/middleware/auth.js b/admin/middleware/auth.js new file mode 100644 index 0000000..3b1bbac --- /dev/null +++ b/admin/middleware/auth.js @@ -0,0 +1,9 @@ +const { NotFoundError } = require('../../shared/utils/error'); + +module.exports = { + allowAdmin(req, res, next) { + if (req.user.isAdmin) return next(); + + next(new NotFoundError()); + } +}; \ No newline at end of file diff --git a/admin/middleware/index.js b/admin/middleware/index.js new file mode 100644 index 0000000..0652a05 --- /dev/null +++ b/admin/middleware/index.js @@ -0,0 +1,3 @@ +module.exports = { + auth: require('./auth') +}; \ No newline at end of file diff --git a/admin/routers/book.js b/admin/routers/book.js index 73056e5..847d77c 100644 --- a/admin/routers/book.js +++ b/admin/routers/book.js @@ -1,21 +1,30 @@ const router = require('express').Router(); -const { book: controller } = require('../controllers'); +const { + book: bookController, + topic: topicController +} = require('../controllers'); -router.param('book', controller.findBook); +router.param('book', bookController.findBook); -router.get('/', controller.showIndexPage); +router.get('/', bookController.showIndexPage); router.route('/create') - .get(controller.showCreatePage) - .post(controller.createBook); + .get( + topicController.findTopics, + bookController.showCreatePage + ) + .post(bookController.createBook); router.route('/:book/update') - .get(controller.showUpdatePage) - .post(controller.updateBook); + .get( + topicController.findTopics, + bookController.showUpdatePage + ) + .post(bookController.updateBook); router.route('/:book/delete') - .get(controller.showDeletePage) - .post(controller.deleteBook); + .get(bookController.showDeletePage) + .post(bookController.deleteBook); module.exports = router; \ No newline at end of file diff --git a/admin/routers/home.js b/admin/routers/home.js index 2fc5adf..f42d3b3 100644 --- a/admin/routers/home.js +++ b/admin/routers/home.js @@ -1,7 +1,7 @@ const router = require('express').Router(); -router.get('/', (req, res) => { - res.render('index', {}); -}); +const { home: homeController } = require('../controllers'); + +router.get('/', homeController.showIndexPage); module.exports = router; \ No newline at end of file diff --git a/admin/routers/index.js b/admin/routers/index.js index 697d939..dd1ab34 100644 --- a/admin/routers/index.js +++ b/admin/routers/index.js @@ -1,12 +1,5 @@ -const fs = require('fs'); -const path = require('path'); - -let index = path.basename(__filename); -let files = fs.readdirSync(__dirname); - -for (let file of files) { - if (file !== index) { - let name = path.basename(file, '.js'); - module.exports[name] = require(`./${file}`); - } -} \ No newline at end of file +module.exports = { + book: require('./book'), + home: require('./home'), + topic: require('./topic') +}; \ No newline at end of file diff --git a/admin/routers/topic.js b/admin/routers/topic.js index c888057..4588095 100644 --- a/admin/routers/topic.js +++ b/admin/routers/topic.js @@ -1,21 +1,21 @@ const router = require('express').Router(); -const { topic: controller } = require('../controllers'); +const { topic: topicController } = require('../controllers'); -router.param('topic', controller.findTopic); +router.param('topic', topicController.findTopic); -router.get('/', controller.showIndexPage); +router.get('/', topicController.showIndexPage); router.route('/create') - .get(controller.showCreatePage) - .post(controller.createTopic); + .get(topicController.showCreatePage) + .post(topicController.createTopic); router.route('/:topic/update') - .get(controller.showUpdatePage) - .post(controller.updateTopic); + .get(topicController.showUpdatePage) + .post(topicController.updateTopic); router.route('/:topic/delete') - .get(controller.showDeletePage) - .post(controller.deleteTopic); + .get(topicController.showDeletePage) + .post(topicController.deleteTopic); module.exports = router; \ No newline at end of file diff --git a/admin/views/_layout.pug b/admin/views/_layout.pug index 4eb238e..4403539 100644 --- a/admin/views/_layout.pug +++ b/admin/views/_layout.pug @@ -1,5 +1,6 @@ extends /_layout +include /_mixins/form include /_mixins/mdc/index block content diff --git a/admin/views/books/delete.pug b/admin/views/books/delete.pug new file mode 100644 index 0000000..4ca0b6d --- /dev/null +++ b/admin/views/books/delete.pug @@ -0,0 +1,7 @@ +extends ../_layout + +block main + +form(method='post') + p Вы уверены что хотите удалить книгу "#{book.title}" + +mdc-button('Нет')(href='/admin/books') + +mdc-button('Да')(type='submit') \ No newline at end of file diff --git a/admin/views/books/form.pug b/admin/views/books/form.pug new file mode 100644 index 0000000..36a0bce --- /dev/null +++ b/admin/views/books/form.pug @@ -0,0 +1,41 @@ +extends ../_layout + +block main + +form(method='post') + +mdc-text-field('title', 'Название', book.title)(required) + +mdc-text-field('slug', 'Слаг', book.slug)(required) + +mdc-text-field('authors', 'Авторы', book.authors.join(', '))(required) + + select(name='publishers') + option(value='orielly') O'Reilly + option(value='manning') Manning + option(value='microsoft') Microsoft Press + + +mdc-text-field('date', 'Дата', book.date.format('YYYY-MM-DD'))(type='date') + +mdc-text-field('edition', 'Издание', book.edition)(type='number') + +mdc-text-field('pages', 'Страницы', book.pages)(type='number') + + select(name='language') + for language in LANGUAGES + option(value=language.value selected=(book.language === language.value))= language.title + + select(name='level') + option(value='beg' selected=(book.level === 'beg')) Легкий + option(value='int' selected=(book.level === 'int')) Средний + option(value='adv' selected=(book.level === 'adv')) Сложный + + select(name='topics' multiple) + for topic in topics + option(value=topic.id selected=(book.topics.includes(topic.id)))= topic.title + + +mdc-text-field('tags', 'Теги', book.tags.join(', ')) + + +mdc-text-field('url', 'Ссылка', book.url) + +mdc-text-field('imageUrl', 'Ссылка', book.imageUrl) + + textarea(name='description' placeholder='Описание')= book.description + textarea(name='contents' placeholder='Описание')= book.contents + + +mdc-button(book._id ? 'Сохранить' : 'Создать')(type='submit' raised) + if book._id + +mdc-button('Удалить')(href=`/admin/books/${book._id}/delete`) \ No newline at end of file diff --git a/admin/views/books/index.pug b/admin/views/books/index.pug index 47e8c71..f39fc5a 100644 --- a/admin/views/books/index.pug +++ b/admin/views/books/index.pug @@ -1 +1,14 @@ -extends ../_layout \ No newline at end of file +extends ../_layout + +block main + ul.mdc-list + for book in books + li.mdc-list-item + = book.title + a.mdc-list-item__meta.material-icons(href=`/admin/books/${book.id}/update`) edit + a.mdc-list-item__meta.material-icons(href=`/admin/books/${book.id}/delete`) delete + else + p Нет книг + + a(href='/admin/books/create').mdc-fab + i.mdc-fab__icon.material-icons add \ No newline at end of file diff --git a/admin/views/index.pug b/admin/views/index.pug index ab7c278..3952ac3 100644 --- a/admin/views/index.pug +++ b/admin/views/index.pug @@ -1 +1 @@ -extends _layout \ No newline at end of file +extends /_layout \ No newline at end of file diff --git a/admin/views/topics/delete.pug b/admin/views/topics/delete.pug index f47b7b5..7755b98 100644 --- a/admin/views/topics/delete.pug +++ b/admin/views/topics/delete.pug @@ -1,7 +1,7 @@ extends ../_layout block main - form(method='post') + +form(method='post') p Вы уверены что хотите удалить тему "#{topic.title}" +mdc-button('Нет')(href='/admin/topics') +mdc-button('Да')(type='submit') \ No newline at end of file diff --git a/admin/views/topics/form.pug b/admin/views/topics/form.pug index bee41be..a5a926a 100644 --- a/admin/views/topics/form.pug +++ b/admin/views/topics/form.pug @@ -1,11 +1,10 @@ extends ../_layout block main - form(method='post') - input(type='hidden' name='secret_string' value='qwerty') + +form(method='post') if !topic._id - +mdc-text-field('id', topic._id)(placeholder='ID' required) - +mdc-text-field('title', topic.title)(placeholder='Название' required) + +mdc-text-field('id', 'ID', topic._id)(required) + +mdc-text-field('title', 'Название', topic.title)(required) +mdc-button(topic._id ? 'Сохранить' : 'Создать')(type='submit' raised) if topic._id diff --git a/admin/views/topics/index.pug b/admin/views/topics/index.pug index b6dcbbe..9d6bbef 100644 --- a/admin/views/topics/index.pug +++ b/admin/views/topics/index.pug @@ -8,4 +8,7 @@ block main = topic.title a.mdc-list-item__meta.material-icons(href=`/admin/topics/${topic._id}/update`) edit else - p Нет топиков \ No newline at end of file + p Нет топиков + + a(href='/admin/topics/create').mdc-fab + i.mdc-fab__icon.material-icons add \ No newline at end of file diff --git a/admin/views/users/index.pug b/admin/views/users/index.pug new file mode 100644 index 0000000..692a88e --- /dev/null +++ b/admin/views/users/index.pug @@ -0,0 +1,11 @@ +extends ../_layout + +block main + ul.mdc-list.mdc-list--avatar-list + for user in users + li.mdc-list-item + img.mdc-list-item__graphic(src=user.image) + = user.title + a.mdc-list-item__meta.material-icons(href=`/admin/users/${user._id}/delete`) delete + else + p Нет топиков \ No newline at end of file diff --git a/api/controllers/book.js b/api/controllers/book.js new file mode 100644 index 0000000..8414706 --- /dev/null +++ b/api/controllers/book.js @@ -0,0 +1,62 @@ +const { Book } = require('../../shared/models'); + +module.exports = { + books: { + // GET /api/books + get(req, res, next) { + Book.find() + .then(books => res.status(200).json(books)) + .catch(next); + }, + + // POST /api/books + post(req, res, next) { + Book.create(req.body) + .then(book => res.status(201).json(book)) + .catch(next); + } + }, + + book: { + find(req, res, next, id) { + Book.findById(id) + .then(book => { + if (!book) return res.sendStatus(404); + req.book = book; + next(); + }) + .catch(next); + }, + + // GET /api/books/:id + get(req, res) { + res.send(req.book); + }, + + // PUT /api/books/:id + put(req, res, next) { + req.book = Object.assign(req.book, req.body); + + req.book.save() + .then(book => res.status(201).json(book)) + .catch(next); + }, + + // DELETE /api/books/:id + delete(req, res, next) { + req.book.remove() + .then(() => res.sendStatus(204)) + .catch(next); + }, + + likes: { + put(req, res, next) { + req.book.likes += 1; + + req.book.save() + .then(book => res.status(201).json(book.likes)) + .catch(next); + } + } + } +}; \ No newline at end of file diff --git a/api/controllers/index.js b/api/controllers/index.js new file mode 100644 index 0000000..dd2a9b7 --- /dev/null +++ b/api/controllers/index.js @@ -0,0 +1,3 @@ +module.exports = { + book: require('./book') +}; \ No newline at end of file diff --git a/api/index.js b/api/index.js new file mode 100644 index 0000000..f4075e6 --- /dev/null +++ b/api/index.js @@ -0,0 +1,29 @@ +const express = require('express'); +const passport = require('passport'); +const cache = require('apicache').middleware; +const RateLimit = require('express-rate-limit'); +const cors = require('cors'); + +const api = express(); + +const limit = new RateLimit({ + windowMs: 60 * 1000, // 15 minutes + max: 1, // limit each IP to 100 requests per windowMs + delayMs: 0 // disable delaying - full speed until the max limit is reached +}); + +const routers = require('./routers'); + +api.use(cors()); + +api.use(routers.auth); +api.use(passport.authenticate('jwt', { session: false })); +api.use(limit); +api.use(cache('1 minute')); +api.use('/books', cache('1 minute'), routers.book); + +api.use((error, req, res, next) => { + res.status(500).json(error); +}); + +module.exports = api; \ No newline at end of file diff --git a/api/routers/auth.js b/api/routers/auth.js new file mode 100644 index 0000000..782830c --- /dev/null +++ b/api/routers/auth.js @@ -0,0 +1,23 @@ +const router = require('express').Router(); +const jwt = require('jwt-simple'); + +const { jwtSecret } = require('../../shared/config'); +const { User } = require('../../shared/models'); + +router.post('/token', (req, res, next) => { + if (!req.body.email || !req.body.password) return res.sendStatus(401); + + User.findOne({ email: req.body.email }) + .then(user => { + if (!user) return res.sendStatus(401); + if (!user.isCorrectPassword(req.body.password)) return res.sendStatus(201); + + let payload = { id: user.id }; + let token = jwt.encode(payload, jwtSecret); + + res.json({ token }); + }) + .catch(next); +}); + +module.exports = router; \ No newline at end of file diff --git a/api/routers/book.js b/api/routers/book.js new file mode 100644 index 0000000..11f59d8 --- /dev/null +++ b/api/routers/book.js @@ -0,0 +1,19 @@ +const router = require('express').Router(); + +const { book: bookController } = require('../controllers'); + +router.param('id', bookController.book.find); + +router.route('/') + .get(bookController.books.get) + .post(bookController.books.post); + +router.route('/:id') + .get(bookController.book.get) + .put(bookController.book.put) + .delete(bookController.book.delete); + +router.route('/:id/likes') + .put(bookController.book.likes.put); + +module.exports = router; \ No newline at end of file diff --git a/api/routers/index.js b/api/routers/index.js new file mode 100644 index 0000000..b4a9295 --- /dev/null +++ b/api/routers/index.js @@ -0,0 +1,4 @@ +module.exports = { + auth: require('./auth'), + book: require('./book') +}; \ No newline at end of file diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..f3552b7 --- /dev/null +++ b/client/index.html @@ -0,0 +1,20 @@ + + + + + + + Document + + + + + \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json new file mode 100644 index 0000000..0194da5 --- /dev/null +++ b/client/package-lock.json @@ -0,0 +1,165 @@ +{ + "name": "client", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" + }, + "corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ecstatic": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.2.0.tgz", + "integrity": "sha512-Goilx/2cfU9vvfQjgtNgc2VmJAD8CasQ6rZDqCd2u4Hsyd/qFET6nBf60jiHodevR3nl3IGzNKtrzPXWP88utQ==", + "requires": { + "he": "1.1.1", + "mime": "1.6.0", + "minimist": "1.2.0", + "url-join": "2.0.5" + } + }, + "eventemitter3": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", + "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=" + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" + }, + "http-proxy": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", + "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", + "requires": { + "eventemitter3": "1.2.0", + "requires-port": "1.0.0" + } + }, + "http-server": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.11.1.tgz", + "integrity": "sha512-6JeGDGoujJLmhjiRGlt8yK8Z9Kl0vnl/dQoQZlc4oeqaUoAKQg94NILLfrY3oWzSyFaQCVNTcKE5PZ3cH8VP9w==", + "requires": { + "colors": "1.0.3", + "corser": "2.0.1", + "ecstatic": "3.2.0", + "http-proxy": "1.16.2", + "opener": "1.4.3", + "optimist": "0.6.1", + "portfinder": "1.0.13", + "union": "0.4.6" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "opener": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.4.3.tgz", + "integrity": "sha1-XG2ixdflgx6P+jlklQ+NZnSskLg=" + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "requires": { + "minimist": "0.0.10", + "wordwrap": "0.0.3" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" + } + } + }, + "portfinder": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", + "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=", + "requires": { + "async": "1.5.2", + "debug": "2.6.9", + "mkdirp": "0.5.1" + } + }, + "qs": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz", + "integrity": "sha1-6eha2+ddoLvkyOBHaghikPhjtAQ=" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "union": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/union/-/union-0.4.6.tgz", + "integrity": "sha1-GY+9rrolTniLDvy2MLwR8kopWeA=", + "requires": { + "qs": "2.3.3" + } + }, + "url-join": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", + "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=" + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + } + } +} diff --git a/client/package.json b/client/package.json new file mode 100644 index 0000000..d4273c1 --- /dev/null +++ b/client/package.json @@ -0,0 +1,15 @@ +{ + "name": "client", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "http-server" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "http-server": "^0.11.1" + } +} diff --git a/gulpfile.js b/gulpfile.js index e1647d8..d655ae0 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -5,14 +5,14 @@ const rename = require('gulp-rename'); const paths = { vendor: './node_modules/', - src: './styles/', - dist: './public/css/' + src: './shared/styles/', + dist: './shared/public/css/' }; gulp.task('sass', () => { gulp.src(`${paths.src}index.scss`) .pipe(sass({ - includePaths: [paths.vendor] + includePaths: [paths.vendor, './shared/styles'] }).on('error', sass.logError)) .pipe(autoprefixer({ browsers: ['last 2 versions', 'ie >= 9'] @@ -21,5 +21,6 @@ gulp.task('sass', () => { .pipe(gulp.dest(paths.dist)); }); -gulp.task('watch:sass', () => - gulp.watch(paths.src + '**/*.scss', ['sass'])); \ No newline at end of file +gulp.task('watch:sass', () => { + gulp.watch(paths.src + '**/*.scss', ['sass']); +}); \ No newline at end of file diff --git a/index.js b/index.js index 6ccf3f5..b2626c3 100644 --- a/index.js +++ b/index.js @@ -4,29 +4,32 @@ const favicon = require('serve-favicon'); const session = require('express-session'); const MongoStore = require('connect-mongo')(session); -const { db, passport } = require('./services'); -const config = require('./config'); -const { error, auth } = require('./middleware'); -const routers = require('./routers'); -const admin = require('./admin'); +const data = require('./shared/data'); +const config = require('./shared/config'); +const services = require('./shared/services'); +const middleware = require('./shared/middleware'); -const app = express(); +const main = require('./main'); +const admin = require('./admin'); +const api = require('./api'); -app.set('view engine', 'pug'); -app.set('views', config.paths.views); -app.set('config', config); +const server = express(); -app.locals.version = config.version; -app.locals.basedir = config.paths.views; +server.set('view engine', 'pug'); +server.set('views', config.paths.views); +server.set('config', config); -app.use(express.static(config.paths.public)); -app.use('/lib', express.static(config.paths.lib)); -app.use(express.urlencoded({ extended: false })); +server.locals.basedir = config.paths.views; +server.locals.VERSION = config.version; +server.locals.LANGUAGES = data.languages; -app.use(favicon(config.paths.favicon)); -app.use(logger('dev')); -app.use(express.urlencoded({ extended: false })); -app.use(session({ +server.use(express.static(config.paths.public)); +server.use('/lib', express.static(config.paths.lib)); +server.use(favicon(config.paths.favicon)); +server.use(express.urlencoded({ extended: false })); +server.use(express.json()); +server.use(logger('dev')); +server.use(session({ name: 'sessionId', secret: config.sessionSecret, resave: false, @@ -34,36 +37,24 @@ app.use(session({ cookie: { httpOnly: true, // secure: true, - signed: true, + //signed: true, maxAge: 1000 * 60 * 60 * 24 * 3 // 3 days }, store: new MongoStore({ - mongooseConnection: db.connection, + mongooseConnection: services.db.connection, ttl: 60 * 60 * 24 * 3, // 3 days touchAfter: 60 * 60 * 24 // 1 day }) })); -app.use(passport.initialize()); -app.use(passport.session()); - -app.use((req, res, next) => { - res.locals.user = req.user; - - next(); -}); - -app.use('/', routers.main); -app.use('/auth', routers.auth); +server.use(services.passport.initialize()); +server.use(services.passport.session()); -app.use(auth.authenticated); -app.use('/profile', routers.profile); -app.use('/books', routers.book); -app.use('/topics', routers.topic); -app.use('/search', routers.search); -app.use('/admin', admin); +server.use('/', main); +server.use('/admin', admin); +server.use('/api', api); -app.use(error.notFound); -app.use(app.get('env') === 'development' ? error.development : error.production); +server.use(middleware.error.notFound); +server.use(server.get('env') === 'development' ? middleware.error.development : middleware.error.production); -app.listen(config.port, () => console.log('Express:', config.port)); \ No newline at end of file +server.listen(config.port, () => console.log('Express:', config.port)); \ No newline at end of file diff --git a/controllers/auth.js b/main/controllers/auth.js similarity index 88% rename from controllers/auth.js rename to main/controllers/auth.js index e34b4c3..c864deb 100644 --- a/controllers/auth.js +++ b/main/controllers/auth.js @@ -1,4 +1,4 @@ -const { passport } = require('../services'); +const { passport } = require('../../shared/services'); module.exports = { // GET /auth/register @@ -22,13 +22,13 @@ module.exports = { // POST /auth/register register: passport.authenticate('local-register', { failureRedirect: '/auth/register', - successRedirect: '/profile' + successRedirect: '/user' }), // POST /auth/login login: passport.authenticate('local-login', { failureRedirect: '/auth/login', - successRedirect: '/profile' + successRedirect: '/user' }), // GET /auth/logout diff --git a/controllers/book.js b/main/controllers/book.js similarity index 97% rename from controllers/book.js rename to main/controllers/book.js index bd3f632..66d6ab7 100644 --- a/controllers/book.js +++ b/main/controllers/book.js @@ -1,4 +1,4 @@ -const { book: Book, topic: Topic } = require('../models'); +const { Book, Topic } = require('../../shared/models'); module.exports = { findBook(req, res, next, slug) { diff --git a/main/controllers/index.js b/main/controllers/index.js new file mode 100644 index 0000000..78530b0 --- /dev/null +++ b/main/controllers/index.js @@ -0,0 +1,8 @@ +module.exports = { + auth: require('./auth'), + book: require('./book'), + main: require('./main'), + oauth: require('./oauth'), + search: require('./search'), + topic: require('./topic') +}; \ No newline at end of file diff --git a/controllers/main.js b/main/controllers/main.js similarity index 81% rename from controllers/main.js rename to main/controllers/main.js index 8d7005b..ce7f3f0 100644 --- a/controllers/main.js +++ b/main/controllers/main.js @@ -1,6 +1,6 @@ module.exports = { // GET / - showMain(req, res, next) { + showMain(req, res) { res.render('index', { id: 'main', title: 'CodeLibrary' diff --git a/controllers/oauth.js b/main/controllers/oauth.js similarity index 68% rename from controllers/oauth.js rename to main/controllers/oauth.js index c4b9355..6cdd9ff 100644 --- a/controllers/oauth.js +++ b/main/controllers/oauth.js @@ -1,11 +1,11 @@ -const { passport } = require('../services'); +const { passport } = require('../../shared/services'); module.exports = { github: { authenticate: passport.authenticate('github'), callback: passport.authenticate('github', { failureRedirect: '/auth/login', - successRedirect: '/profile' + successRedirect: '/user' }) } }; \ No newline at end of file diff --git a/controllers/search.js b/main/controllers/search.js similarity index 100% rename from controllers/search.js rename to main/controllers/search.js diff --git a/controllers/topic.js b/main/controllers/topic.js similarity index 90% rename from controllers/topic.js rename to main/controllers/topic.js index 39834df..f59266b 100644 --- a/controllers/topic.js +++ b/main/controllers/topic.js @@ -1,4 +1,4 @@ -const { topic: Topic } = require('../models'); +const { Topic } = require('../../shared/models'); module.exports = { findTopics(req, res, next) { diff --git a/main/index.js b/main/index.js new file mode 100644 index 0000000..6fa63d9 --- /dev/null +++ b/main/index.js @@ -0,0 +1,29 @@ +const express = require('express'); +const path = require('path'); + +const middleware = require('./middleware'); +const routers = require('./routers'); + +const main = express(); + +main.set('views', path.join(__dirname, 'views')); +main.set('view engine', 'pug'); + +main.on('mount', server => { + main.locals = Object.assign(server.locals, main.locals); +}); + +main.use((req, res, next) => { + res.locals.user = req.user; + + next(); +}); + +main.use('/', routers.main); +main.use('/auth', routers.auth); +main.use('/user', middleware.auth.allowAuthenticated, routers.user); +main.use('/books', routers.book); +main.use('/topics', routers.topic); +main.use('/search', routers.search); + +module.exports = main; \ No newline at end of file diff --git a/main/middleware/auth.js b/main/middleware/auth.js new file mode 100644 index 0000000..19c3dc9 --- /dev/null +++ b/main/middleware/auth.js @@ -0,0 +1,13 @@ +module.exports = { + allowAuthenticated(req, res, next) { + if (req.isAuthenticated()) return next(); + + res.status(403).redirect('/auth/login'); + }, + + allowUnauthenticated(req, res, next) { + if (req.isUnauthenticated()) return next(); + + res.redirect('/books'); + } +}; \ No newline at end of file diff --git a/main/middleware/index.js b/main/middleware/index.js new file mode 100644 index 0000000..0652a05 --- /dev/null +++ b/main/middleware/index.js @@ -0,0 +1,3 @@ +module.exports = { + auth: require('./auth') +}; \ No newline at end of file diff --git a/routers/auth.js b/main/routers/auth.js similarity index 67% rename from routers/auth.js rename to main/routers/auth.js index d398805..8a354b1 100644 --- a/routers/auth.js +++ b/main/routers/auth.js @@ -1,24 +1,24 @@ const router = require('express').Router(); -const { auth: { authenticated, unauthenticated } } = require('../middleware'); +const { auth: authMiddleware } = require('../middleware'); const { auth: authController, oauth: oauthController } = require('../controllers'); router.route('/register') - .all(unauthenticated) + .all(authMiddleware.allowUnauthenticated) .get(authController.showRegisterPage) .post(authController.register); router.route('/login') - .all(unauthenticated) + .all(authMiddleware.allowUnauthenticated) .get(authController.showLoginPage) .post(authController.login); router.get('/github', oauthController.github.authenticate); router.get('/github/callback', oauthController.github.callback); -router.get('/logout', authenticated, authController.logout); +router.get('/logout', authMiddleware.allowAuthenticated, authController.logout); module.exports = router; \ No newline at end of file diff --git a/routers/book.js b/main/routers/book.js similarity index 100% rename from routers/book.js rename to main/routers/book.js diff --git a/main/routers/index.js b/main/routers/index.js new file mode 100644 index 0000000..bd956d7 --- /dev/null +++ b/main/routers/index.js @@ -0,0 +1,8 @@ +module.exports = { + auth: require('./auth'), + book: require('./book'), + main: require('./main'), + search: require('./search'), + topic: require('./topic'), + user: require('./user') +}; \ No newline at end of file diff --git a/routers/main.js b/main/routers/main.js similarity index 100% rename from routers/main.js rename to main/routers/main.js diff --git a/routers/search.js b/main/routers/search.js similarity index 100% rename from routers/search.js rename to main/routers/search.js diff --git a/routers/topic.js b/main/routers/topic.js similarity index 100% rename from routers/topic.js rename to main/routers/topic.js diff --git a/routers/profile.js b/main/routers/user.js similarity index 100% rename from routers/profile.js rename to main/routers/user.js diff --git a/views/_includes/sidenav.pug b/main/views/_includes/sidenav.pug similarity index 100% rename from views/_includes/sidenav.pug rename to main/views/_includes/sidenav.pug diff --git a/views/_includes/toolbar.pug b/main/views/_includes/toolbar.pug similarity index 83% rename from views/_includes/toolbar.pug rename to main/views/_includes/toolbar.pug index cd4af84..08c8f92 100644 --- a/views/_includes/toolbar.pug +++ b/main/views/_includes/toolbar.pug @@ -5,9 +5,11 @@ header.mdc-toolbar section.mdc-toolbar__section.mdc-toolbar__serction--align-left.mdc-toolbar__section--shrink-to-fit nav.mdc-tab-bar + a.mdc-tab(href='/books') Книги + unless user a.mdc-tab(href='/auth/login') Войти a.mdc-tab(href='/auth/register') Зарегистрироваться else - a.mdc-tab(href='/profile')= user.email + a.mdc-tab(href='/user')= user.email a.mdc-tab(href='/auth/logout') Выйти \ No newline at end of file diff --git a/main/views/_layout.pug b/main/views/_layout.pug new file mode 100644 index 0000000..4ee72f8 --- /dev/null +++ b/main/views/_layout.pug @@ -0,0 +1,13 @@ +extends /_layout + +include /_mixins/mdc/index + +block header + include _includes/toolbar + +block content + section#content + block sidenav + include _includes/sidenav + + block main \ No newline at end of file diff --git a/views/_mixins/book-card.pug b/main/views/_mixins/book-card.pug similarity index 100% rename from views/_mixins/book-card.pug rename to main/views/_mixins/book-card.pug diff --git a/views/auth/login.pug b/main/views/auth/login.pug similarity index 100% rename from views/auth/login.pug rename to main/views/auth/login.pug diff --git a/views/auth/register.pug b/main/views/auth/register.pug similarity index 100% rename from views/auth/register.pug rename to main/views/auth/register.pug diff --git a/views/books/book.pug b/main/views/books/book.pug similarity index 100% rename from views/books/book.pug rename to main/views/books/book.pug diff --git a/views/books/index.pug b/main/views/books/index.pug similarity index 100% rename from views/books/index.pug rename to main/views/books/index.pug diff --git a/views/index.pug b/main/views/index.pug similarity index 100% rename from views/index.pug rename to main/views/index.pug diff --git a/views/profile/index.pug b/main/views/profile/index.pug similarity index 100% rename from views/profile/index.pug rename to main/views/profile/index.pug diff --git a/middleware/auth.js b/middleware/auth.js deleted file mode 100644 index d235170..0000000 --- a/middleware/auth.js +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = { - authenticated(req, res, next) { - if (req.user) return next(); - - res.status(403).redirect('/auth/login'); - }, - - unauthenticated(req, res, next) { - if (!req.user) return next(); - - res.redirect('/books'); - } -}; \ No newline at end of file diff --git a/middleware/index.js b/middleware/index.js deleted file mode 100644 index 697d939..0000000 --- a/middleware/index.js +++ /dev/null @@ -1,12 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -let index = path.basename(__filename); -let files = fs.readdirSync(__dirname); - -for (let file of files) { - if (file !== index) { - let name = path.basename(file, '.js'); - module.exports[name] = require(`./${file}`); - } -} \ No newline at end of file diff --git a/models/index.js b/models/index.js deleted file mode 100644 index 697d939..0000000 --- a/models/index.js +++ /dev/null @@ -1,12 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -let index = path.basename(__filename); -let files = fs.readdirSync(__dirname); - -for (let file of files) { - if (file !== index) { - let name = path.basename(file, '.js'); - module.exports[name] = require(`./${file}`); - } -} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1c94a0e..9bbd8e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -164,6 +164,11 @@ "normalize-path": "2.1.1" } }, + "apicache": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/apicache/-/apicache-1.2.0.tgz", + "integrity": "sha512-SwA6mpYhEEQ0cbDXFKxuVKcW9d1LS2AX3aJSX0gaWMWK8y3bXIQXcj77BqVlYKIlS3VY+PE+veyG7/AJA2x6yg==" + }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", @@ -411,6 +416,11 @@ } } }, + "base64url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", + "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=" + }, "basic-auth": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz", @@ -555,6 +565,11 @@ "resolved": "https://registry.npmjs.org/bson/-/bson-1.0.4.tgz", "integrity": "sha1-k8ENOeqltYQVy8QFLz5T5WKwtyw=" }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "buffer-shims": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", @@ -835,8 +850,7 @@ "clone": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.3.tgz", - "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=", - "dev": true + "integrity": "sha1-KY1+IjFmD0DAA8LtMUDezz9TCF8=" }, "clone-stats": { "version": "0.0.1", @@ -885,6 +899,12 @@ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "dev": true + }, "combined-stream": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", @@ -1032,6 +1052,21 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cors": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz", + "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=", + "requires": { + "object-assign": "4.1.1", + "vary": "1.1.2" + } + }, + "corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", + "dev": true + }, "crc": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz", @@ -1137,7 +1172,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dev": true, "requires": { "clone": "1.0.3" } @@ -1282,6 +1316,35 @@ "jsbn": "0.1.1" } }, + "ecdsa-sig-formatter": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz", + "integrity": "sha1-S8kmJ07Dtau1AW5+HWCSGsJisqE=", + "requires": { + "base64url": "2.0.0", + "safe-buffer": "5.1.1" + } + }, + "ecstatic": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.2.0.tgz", + "integrity": "sha512-Goilx/2cfU9vvfQjgtNgc2VmJAD8CasQ6rZDqCd2u4Hsyd/qFET6nBf60jiHodevR3nl3IGzNKtrzPXWP88utQ==", + "dev": true, + "requires": { + "he": "1.1.1", + "mime": "1.4.1", + "minimist": "1.2.0", + "url-join": "2.0.5" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1474,6 +1537,12 @@ "through": "2.3.8" } }, + "eventemitter3": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", + "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=", + "dev": true + }, "execa": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", @@ -1563,6 +1632,14 @@ } } }, + "express-rate-limit": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-2.11.0.tgz", + "integrity": "sha512-KMZayDxj3Wr7zYuwTuDZj5hMW0nhnyJVBVCwMEVKwMdW6CkYh4vnfnUbRJYhKC0v6UuIbPerwKY0dqWmEzFjKA==", + "requires": { + "defaults": "1.0.3" + } + }, "express-session": { "version": "1.15.6", "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.15.6.tgz", @@ -2758,11 +2835,16 @@ "sntp": "1.0.9" } }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, "hoek": { "version": "2.16.3", "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" }, "homedir-polyfill": { "version": "1.0.1", @@ -2797,6 +2879,32 @@ } } }, + "http-proxy": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", + "integrity": "sha1-Bt/ykpUr9k2+hHH6nfcwZtTzd0I=", + "dev": true, + "requires": { + "eventemitter3": "1.2.0", + "requires-port": "1.0.0" + } + }, + "http-server": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.11.1.tgz", + "integrity": "sha512-6JeGDGoujJLmhjiRGlt8yK8Z9Kl0vnl/dQoQZlc4oeqaUoAKQg94NILLfrY3oWzSyFaQCVNTcKE5PZ3cH8VP9w==", + "dev": true, + "requires": { + "colors": "1.0.3", + "corser": "2.0.1", + "ecstatic": "3.2.0", + "http-proxy": "1.16.2", + "opener": "1.4.3", + "optimist": "0.6.1", + "portfinder": "1.0.13", + "union": "0.4.6" + } + }, "http-signature": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", @@ -3252,6 +3360,11 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "isemail": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz", + "integrity": "sha1-vgPfjMPineTSxd9lASY/H6RZXpo=" + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3272,6 +3385,17 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "joi": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-6.10.1.tgz", + "integrity": "sha1-TVDDGAeRIgAP5fFq8f+OGRe3fgY=", + "requires": { + "hoek": "2.16.3", + "isemail": "1.2.0", + "moment": "2.20.1", + "topo": "1.1.0" + } + }, "js-base64": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.0.tgz", @@ -3332,6 +3456,18 @@ "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", "dev": true }, + "jsonwebtoken": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.4.3.tgz", + "integrity": "sha1-d/UCHeBYtgWheD+hKD6ZgS5kVjg=", + "requires": { + "joi": "6.10.1", + "jws": "3.1.4", + "lodash.once": "4.1.1", + "ms": "2.0.0", + "xtend": "4.0.1" + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -3359,6 +3495,32 @@ "promise": "7.3.1" } }, + "jwa": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz", + "integrity": "sha1-oFUs4CIHQs1S4VN3SjKQXDDnVuU=", + "requires": { + "base64url": "2.0.0", + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.9", + "safe-buffer": "5.1.1" + } + }, + "jws": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz", + "integrity": "sha1-+ei5M46KhHJ31kRLFGT2GIDgUKI=", + "requires": { + "base64url": "2.0.0", + "jwa": "1.1.5", + "safe-buffer": "5.1.1" + } + }, + "jwt-simple": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/jwt-simple/-/jwt-simple-0.5.1.tgz", + "integrity": "sha1-eeoBiRth3mto4T5nwLS1vak3spQ=" + }, "kareem": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.0.1.tgz", @@ -3559,6 +3721,11 @@ "integrity": "sha1-FQzwoWeR9ZA7iJHqsVRgknS96lU=", "dev": true }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, "lodash.restparam": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", @@ -4132,6 +4299,11 @@ "minimist": "0.0.8" } }, + "moment": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz", + "integrity": "sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg==" + }, "mongodb": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.0.1.tgz", @@ -4863,6 +5035,30 @@ "mimic-fn": "1.1.0" } }, + "opener": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.4.3.tgz", + "integrity": "sha1-XG2ixdflgx6P+jlklQ+NZnSskLg=", + "dev": true + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "0.0.8", + "wordwrap": "0.0.3" + }, + "dependencies": { + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + } + } + }, "optionator": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", @@ -5006,6 +5202,15 @@ "passport-oauth2": "1.4.0" } }, + "passport-jwt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-3.0.1.tgz", + "integrity": "sha1-5Pcnba2L0lHUPG/DiIMTC5YycvY=", + "requires": { + "jsonwebtoken": "7.4.3", + "passport-strategy": "1.0.0" + } + }, "passport-local": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", @@ -5138,6 +5343,34 @@ "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", "dev": true }, + "portfinder": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", + "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=", + "dev": true, + "requires": { + "async": "1.5.2", + "debug": "2.6.9", + "mkdirp": "0.5.1" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -5688,6 +5921,12 @@ } } }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, "resolve": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", @@ -6563,6 +6802,14 @@ "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-0.0.1.tgz", "integrity": "sha1-zu78cXp2xDFvEm0LnbqlXX598Bo=" }, + "topo": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/topo/-/topo-1.1.0.tgz", + "integrity": "sha1-6ddRYV0buH3IZdsYL6HKCl71NtU=", + "requires": { + "hoek": "2.16.3" + } + }, "touch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", @@ -6699,6 +6946,23 @@ "integrity": "sha1-7Mo6A+VrmvFzhbqsgSrIO5lKli8=", "dev": true }, + "union": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/union/-/union-0.4.6.tgz", + "integrity": "sha1-GY+9rrolTniLDvy2MLwR8kopWeA=", + "dev": true, + "requires": { + "qs": "2.3.3" + }, + "dependencies": { + "qs": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz", + "integrity": "sha1-6eha2+ddoLvkyOBHaghikPhjtAQ=", + "dev": true + } + } + }, "union-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", @@ -6820,6 +7084,12 @@ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", "dev": true }, + "url-join": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz", + "integrity": "sha1-WvIvGMBSoACkjXuCxenC4v7tpyg=", + "dev": true + }, "url-parse-lax": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", @@ -7217,8 +7487,7 @@ "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, "y18n": { "version": "3.2.1", diff --git a/package.json b/package.json index 40fb900..9361c20 100644 --- a/package.json +++ b/package.json @@ -15,20 +15,27 @@ "gulp-autoprefixer": "^4.0.0", "gulp-rename": "^1.2.2", "gulp-sass": "^3.1.0", + "http-server": "^0.11.1", "nodemon": "^1.14.1" }, "dependencies": { + "apicache": "^1.2.0", "bcrypt": "^1.0.3", "connect-mongo": "^2.0.1", + "cors": "^2.8.4", "dotenv": "^4.0.0", "express": "^4.16.2", + "express-rate-limit": "^2.11.0", "express-session": "^1.15.6", + "jwt-simple": "^0.5.1", "material-components-web": "^0.28.0", + "moment": "^2.20.1", "mongoose": "^5.0.1", "morgan": "^1.9.0", "normalize.css": "^7.0.0", "passport": "^0.4.0", "passport-github": "^1.1.0", + "passport-jwt": "^3.0.1", "passport-local": "^1.0.0", "pug": "^2.0.0-rc.4", "serve-favicon": "^2.4.5" diff --git a/routers/index.js b/routers/index.js deleted file mode 100644 index 697d939..0000000 --- a/routers/index.js +++ /dev/null @@ -1,12 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -let index = path.basename(__filename); -let files = fs.readdirSync(__dirname); - -for (let file of files) { - if (file !== index) { - let name = path.basename(file, '.js'); - module.exports[name] = require(`./${file}`); - } -} \ No newline at end of file diff --git a/services/index.js b/services/index.js deleted file mode 100644 index 697d939..0000000 --- a/services/index.js +++ /dev/null @@ -1,12 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -let index = path.basename(__filename); -let files = fs.readdirSync(__dirname); - -for (let file of files) { - if (file !== index) { - let name = path.basename(file, '.js'); - module.exports[name] = require(`./${file}`); - } -} \ No newline at end of file diff --git a/config/index.js b/shared/config/index.js similarity index 62% rename from config/index.js rename to shared/config/index.js index b1a0e0d..3b9f286 100644 --- a/config/index.js +++ b/shared/config/index.js @@ -1,19 +1,22 @@ const path = require('path'); +const ROOT_PATH = path.resolve(__dirname, '..', '..'); + module.exports = { version: process.env.APP_VERSION, env: process.env.NODE_ENV, port: process.env.PORT || 3000, sessionSecret: 'HacJmB3ma6crKKtK', + jwtSecret: '8UG^LmgL!!N#42vq', mongodbUri: { local: 'mongodb://localhost:27017/codedojo', mlab: process.env.MONGODB_MLAB_URL }, paths: { - views: path.resolve(__dirname, '..', 'views'), - public: path.resolve(__dirname, '..', 'public'), - favicon: path.resolve(__dirname, '..', 'public', 'favicon.ico'), - lib: path.resolve(__dirname, '..', 'node_modules') + views: path.join(ROOT_PATH, 'shared', 'views'), + public: path.join(ROOT_PATH, 'shared', 'public'), + favicon: path.join(ROOT_PATH, 'shared', 'public', 'favicon.ico'), + lib: path.join(ROOT_PATH, 'node_modules') }, oauth: { github: { diff --git a/shared/data/index.js b/shared/data/index.js new file mode 100644 index 0000000..6c93f7d --- /dev/null +++ b/shared/data/index.js @@ -0,0 +1,3 @@ +module.exports = { + languages: require('./languages') +}; \ No newline at end of file diff --git a/shared/data/languages.json b/shared/data/languages.json new file mode 100644 index 0000000..2d2b92e --- /dev/null +++ b/shared/data/languages.json @@ -0,0 +1,4 @@ +[ + { "value": "en", "title": "Английский" }, + { "value": "ru", "title": "Русский" } +] \ No newline at end of file diff --git a/middleware/error.js b/shared/middleware/error.js similarity index 66% rename from middleware/error.js rename to shared/middleware/error.js index 922db5d..69611c4 100644 --- a/middleware/error.js +++ b/shared/middleware/error.js @@ -1,15 +1,14 @@ +const { NotFoundError } = require('../utils/error'); + module.exports = { notFound(req, res, next) { - let error = new Error('Не найдено'); - error.status = 404; - - next(error); + next(new NotFoundError()); }, development(error, req, res, next) { console.error(error); - res.render('error', { + res.status(error.status).render('error', { id: 'error', title: 'Ошибка', error @@ -17,7 +16,7 @@ module.exports = { }, production(error, req, res, next) { - res.render('error', { + res.status(error.status).render('error', { id: 'error', title: 'Ошибка', message: error.message diff --git a/shared/middleware/index.js b/shared/middleware/index.js new file mode 100644 index 0000000..61509f0 --- /dev/null +++ b/shared/middleware/index.js @@ -0,0 +1,3 @@ +module.exports = { + error: require('./error') +}; \ No newline at end of file diff --git a/models/book.js b/shared/models/book.js similarity index 66% rename from models/book.js rename to shared/models/book.js index 1c72da2..68314c9 100644 --- a/models/book.js +++ b/shared/models/book.js @@ -1,4 +1,7 @@ const mongoose = require('mongoose'); +const moment = require('moment'); + +const splitByComma = require('../utils/format').splitBy(','); const Schema = mongoose.Schema; @@ -7,13 +10,12 @@ const Book = new Schema({ slug: { type: String, required: true, unique: true, trim: true, lowercase: true }, authors: { type: [String], required: true }, publisher: { type: String, ref: 'Publisher' }, - date: { type: Date, default: Date.now }, + date: { type: Date, default: Date.now, get: value => moment(value) }, edition: { type: Number, default: 1, min: 1 }, pages: { type: Number, min: 0, default: 0 }, language: { type: String, enum: ['en', 'ru'] }, level: { type: String, enum: ['beg', 'int', 'adv'] }, topics: [{ type: String, ref: 'Topic' }], - subtopics: [{ type: String }], tags: [{ type: String }], likes: { type: Number, default: 0 }, url: String, @@ -22,10 +24,22 @@ const Book = new Schema({ githubUrl: String, description: { type: String, default: '', trim: true }, contents: { type: String, default: '', trim: true } -},{ +}, { toObject: { getters: false, virtuals: false }, - toJSON: { versionKey: false, getters: true }, + toJSON: { versionKey: false, getters: false }, timestamps: true }); +Book.options.toJSON.transform = function(doc, obj) { + delete obj._id; + return obj; +}; + +Book.statics.validateBody = function(body) { + return Object.assign(body, { + authors: splitByComma(body.authors), + tags: splitByComma(body.tags) + }); +}; + module.exports = mongoose.model('Book', Book); \ No newline at end of file diff --git a/shared/models/index.js b/shared/models/index.js new file mode 100644 index 0000000..30dfcd5 --- /dev/null +++ b/shared/models/index.js @@ -0,0 +1,6 @@ +module.exports = { + Book: require('./book'), + Publisher: require('./publisher'), + Topic: require('./topic'), + User: require('./user') +}; \ No newline at end of file diff --git a/models/publisher.js b/shared/models/publisher.js similarity index 100% rename from models/publisher.js rename to shared/models/publisher.js diff --git a/models/topic.js b/shared/models/topic.js similarity index 100% rename from models/topic.js rename to shared/models/topic.js diff --git a/models/user.js b/shared/models/user.js similarity index 66% rename from models/user.js rename to shared/models/user.js index fb84975..28c62a0 100644 --- a/models/user.js +++ b/shared/models/user.js @@ -12,12 +12,17 @@ const User = new mongoose.Schema({ match: [/^[a-zA-Z0-9'._%+-]+@[a-zA-Z0-9-][a-zA-Z0-9.-]*\.[a-zA-Z]{2,63}$/, 'Неверный формат адреса электронной почты.'] }, password: { type: String, required: true }, - username: String, - photo: String + username: { type: String, unique: true, sparse: true }, + photo: String, + role: { type: String, default: 'user', enum: ['user', 'admin'] } }, { timestamps: true }); +User.virtual('isAdmin').get(function() { + return this.role === 'admin'; +}); + User.pre('save', function(next) { if (!this.isModified('password')) return next(); @@ -41,26 +46,4 @@ User.methods.isCorrectPassword = function(password) { return bcrypt.compare(password, this.password); }; -User.statics.authenticate = function(email, password) { - return this.findOne({ email }) - .then(user => { - if (!user) { - let error = new Error('Пользователь не найден'); - error.status = 401; - throw error; - } - - return bcrypt.compare(password, user.password) - .then(isEqual => { - if (!isEqual) { - let error = new Error('Неверный пароль'); - error.status = 401; - throw error; - } - - return user; - }); - }); -}; - module.exports = mongoose.model('User', User); \ No newline at end of file diff --git a/public/css/app.css b/shared/public/css/app.css similarity index 100% rename from public/css/app.css rename to shared/public/css/app.css diff --git a/public/favicon.ico b/shared/public/favicon.ico similarity index 100% rename from public/favicon.ico rename to shared/public/favicon.ico diff --git a/public/img/library.jpg b/shared/public/img/library.jpg similarity index 100% rename from public/img/library.jpg rename to shared/public/img/library.jpg diff --git a/public/img/topics/angular.svg b/shared/public/img/topics/angular.svg similarity index 100% rename from public/img/topics/angular.svg rename to shared/public/img/topics/angular.svg diff --git a/public/img/topics/angularjs.svg b/shared/public/img/topics/angularjs.svg similarity index 100% rename from public/img/topics/angularjs.svg rename to shared/public/img/topics/angularjs.svg diff --git a/public/img/topics/aspnet.svg b/shared/public/img/topics/aspnet.svg similarity index 100% rename from public/img/topics/aspnet.svg rename to shared/public/img/topics/aspnet.svg diff --git a/public/img/topics/babel.svg b/shared/public/img/topics/babel.svg similarity index 100% rename from public/img/topics/babel.svg rename to shared/public/img/topics/babel.svg diff --git a/public/img/topics/bootstrap.svg b/shared/public/img/topics/bootstrap.svg similarity index 100% rename from public/img/topics/bootstrap.svg rename to shared/public/img/topics/bootstrap.svg diff --git a/public/img/topics/bower.svg b/shared/public/img/topics/bower.svg similarity index 100% rename from public/img/topics/bower.svg rename to shared/public/img/topics/bower.svg diff --git a/public/img/topics/csharp.svg b/shared/public/img/topics/csharp.svg similarity index 100% rename from public/img/topics/csharp.svg rename to shared/public/img/topics/csharp.svg diff --git a/public/img/topics/css.svg b/shared/public/img/topics/css.svg similarity index 100% rename from public/img/topics/css.svg rename to shared/public/img/topics/css.svg diff --git a/public/img/topics/dotnet.svg b/shared/public/img/topics/dotnet.svg similarity index 100% rename from public/img/topics/dotnet.svg rename to shared/public/img/topics/dotnet.svg diff --git a/public/img/topics/ef.svg b/shared/public/img/topics/ef.svg similarity index 100% rename from public/img/topics/ef.svg rename to shared/public/img/topics/ef.svg diff --git a/public/img/topics/express.svg b/shared/public/img/topics/express.svg similarity index 100% rename from public/img/topics/express.svg rename to shared/public/img/topics/express.svg diff --git a/public/img/topics/foundation.svg b/shared/public/img/topics/foundation.svg similarity index 100% rename from public/img/topics/foundation.svg rename to shared/public/img/topics/foundation.svg diff --git a/public/img/topics/git.svg b/shared/public/img/topics/git.svg similarity index 100% rename from public/img/topics/git.svg rename to shared/public/img/topics/git.svg diff --git a/public/img/topics/github.svg b/shared/public/img/topics/github.svg similarity index 100% rename from public/img/topics/github.svg rename to shared/public/img/topics/github.svg diff --git a/public/img/topics/gulp.svg b/shared/public/img/topics/gulp.svg similarity index 100% rename from public/img/topics/gulp.svg rename to shared/public/img/topics/gulp.svg diff --git a/public/img/topics/haskell.svg b/shared/public/img/topics/haskell.svg similarity index 100% rename from public/img/topics/haskell.svg rename to shared/public/img/topics/haskell.svg diff --git a/public/img/topics/html-css.svg b/shared/public/img/topics/html-css.svg similarity index 100% rename from public/img/topics/html-css.svg rename to shared/public/img/topics/html-css.svg diff --git a/public/img/topics/html.svg b/shared/public/img/topics/html.svg similarity index 100% rename from public/img/topics/html.svg rename to shared/public/img/topics/html.svg diff --git a/public/img/topics/java.svg b/shared/public/img/topics/java.svg similarity index 100% rename from public/img/topics/java.svg rename to shared/public/img/topics/java.svg diff --git a/public/img/topics/javascript.svg b/shared/public/img/topics/javascript.svg similarity index 100% rename from public/img/topics/javascript.svg rename to shared/public/img/topics/javascript.svg diff --git a/public/img/topics/jquery.svg b/shared/public/img/topics/jquery.svg similarity index 100% rename from public/img/topics/jquery.svg rename to shared/public/img/topics/jquery.svg diff --git a/public/img/topics/json.svg b/shared/public/img/topics/json.svg similarity index 100% rename from public/img/topics/json.svg rename to shared/public/img/topics/json.svg diff --git a/public/img/topics/laravel.svg b/shared/public/img/topics/laravel.svg similarity index 100% rename from public/img/topics/laravel.svg rename to shared/public/img/topics/laravel.svg diff --git a/public/img/topics/mean.svg b/shared/public/img/topics/mean.svg similarity index 100% rename from public/img/topics/mean.svg rename to shared/public/img/topics/mean.svg diff --git a/public/img/topics/microsoft.net.svg b/shared/public/img/topics/microsoft.net.svg similarity index 100% rename from public/img/topics/microsoft.net.svg rename to shared/public/img/topics/microsoft.net.svg diff --git a/public/img/topics/mongodb.svg b/shared/public/img/topics/mongodb.svg similarity index 100% rename from public/img/topics/mongodb.svg rename to shared/public/img/topics/mongodb.svg diff --git a/public/img/topics/mysql.svg b/shared/public/img/topics/mysql.svg similarity index 100% rename from public/img/topics/mysql.svg rename to shared/public/img/topics/mysql.svg diff --git a/public/img/topics/nodejs.svg b/shared/public/img/topics/nodejs.svg similarity index 100% rename from public/img/topics/nodejs.svg rename to shared/public/img/topics/nodejs.svg diff --git a/public/img/topics/php.svg b/shared/public/img/topics/php.svg similarity index 100% rename from public/img/topics/php.svg rename to shared/public/img/topics/php.svg diff --git a/public/img/topics/programming.svg b/shared/public/img/topics/programming.svg similarity index 100% rename from public/img/topics/programming.svg rename to shared/public/img/topics/programming.svg diff --git a/public/img/topics/python.svg b/shared/public/img/topics/python.svg similarity index 100% rename from public/img/topics/python.svg rename to shared/public/img/topics/python.svg diff --git a/public/img/topics/react.svg b/shared/public/img/topics/react.svg similarity index 100% rename from public/img/topics/react.svg rename to shared/public/img/topics/react.svg diff --git a/public/img/topics/redux.svg b/shared/public/img/topics/redux.svg similarity index 100% rename from public/img/topics/redux.svg rename to shared/public/img/topics/redux.svg diff --git a/public/img/topics/regex.svg b/shared/public/img/topics/regex.svg similarity index 100% rename from public/img/topics/regex.svg rename to shared/public/img/topics/regex.svg diff --git a/public/img/topics/ruby.svg b/shared/public/img/topics/ruby.svg similarity index 100% rename from public/img/topics/ruby.svg rename to shared/public/img/topics/ruby.svg diff --git a/public/img/topics/sass.svg b/shared/public/img/topics/sass.svg similarity index 100% rename from public/img/topics/sass.svg rename to shared/public/img/topics/sass.svg diff --git a/public/img/topics/sqlserver.svg b/shared/public/img/topics/sqlserver.svg similarity index 100% rename from public/img/topics/sqlserver.svg rename to shared/public/img/topics/sqlserver.svg diff --git a/public/img/topics/typescript.svg b/shared/public/img/topics/typescript.svg similarity index 100% rename from public/img/topics/typescript.svg rename to shared/public/img/topics/typescript.svg diff --git a/public/img/topics/uwp.svg b/shared/public/img/topics/uwp.svg similarity index 100% rename from public/img/topics/uwp.svg rename to shared/public/img/topics/uwp.svg diff --git a/public/img/topics/vs.svg b/shared/public/img/topics/vs.svg similarity index 100% rename from public/img/topics/vs.svg rename to shared/public/img/topics/vs.svg diff --git a/public/img/topics/vue.svg b/shared/public/img/topics/vue.svg similarity index 100% rename from public/img/topics/vue.svg rename to shared/public/img/topics/vue.svg diff --git a/public/img/topics/web-apps.svg b/shared/public/img/topics/web-apps.svg similarity index 100% rename from public/img/topics/web-apps.svg rename to shared/public/img/topics/web-apps.svg diff --git a/public/img/topics/webpack.svg b/shared/public/img/topics/webpack.svg similarity index 100% rename from public/img/topics/webpack.svg rename to shared/public/img/topics/webpack.svg diff --git a/public/img/topics/windows.svg b/shared/public/img/topics/windows.svg similarity index 100% rename from public/img/topics/windows.svg rename to shared/public/img/topics/windows.svg diff --git a/public/img/topics/wordpress.svg b/shared/public/img/topics/wordpress.svg similarity index 100% rename from public/img/topics/wordpress.svg rename to shared/public/img/topics/wordpress.svg diff --git a/public/img/topics/xaml.svg b/shared/public/img/topics/xaml.svg similarity index 100% rename from public/img/topics/xaml.svg rename to shared/public/img/topics/xaml.svg diff --git a/services/db.js b/shared/services/db.js similarity index 94% rename from services/db.js rename to shared/services/db.js index e53516b..ee0f2fc 100644 --- a/services/db.js +++ b/shared/services/db.js @@ -4,7 +4,7 @@ const { mongodbUri } = require('../config'); mongoose.Promise = global.Promise; -mongoose.connect(mongodbUri.mlab); +mongoose.connect(mongodbUri.local); mongoose.connection.on('error', console.error.bind(console, 'MongoDB connection error:')); mongoose.connection.once('open', () => console.log('Connected to MongoDB')); diff --git a/controllers/index.js b/shared/services/index.js similarity index 100% rename from controllers/index.js rename to shared/services/index.js diff --git a/services/passport/github.js b/shared/services/passport/github.js similarity index 92% rename from services/passport/github.js rename to shared/services/passport/github.js index 69a9d4f..87b78db 100644 --- a/services/passport/github.js +++ b/shared/services/passport/github.js @@ -1,6 +1,6 @@ const passport = require('passport'); const { Strategy: GitHubStrategy } = require('passport-github'); -const { user: User } = require('../../models'); +const { User } = require('../../models'); const config = require('../../config'); diff --git a/services/passport/index.js b/shared/services/passport/index.js similarity index 81% rename from services/passport/index.js rename to shared/services/passport/index.js index 46fa77f..a4a8bf3 100644 --- a/services/passport/index.js +++ b/shared/services/passport/index.js @@ -1,8 +1,9 @@ const passport = require('passport'); -const { user: User } = require('../../models'); +const { User } = require('../../models'); require('./local'); require('./github'); +require('./jwt'); passport.serializeUser((user, done) => { done(null, user._id); diff --git a/shared/services/passport/jwt.js b/shared/services/passport/jwt.js new file mode 100644 index 0000000..03b91b6 --- /dev/null +++ b/shared/services/passport/jwt.js @@ -0,0 +1,19 @@ +const passport = require('passport'); +const { Strategy, ExtractJwt } = require('passport-jwt'); +const { User } = require('../../models'); + +const config = require('../../config'); +const options = { + secretOrKey: config.jwtSecret, + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken() +}; + +passport.use(new Strategy(options, (payload, done) => { + User.findById(payload.id) + .then(user => { + if (!user) return done(null, false); + + done(null, user); + }) + .catch(done); +})); \ No newline at end of file diff --git a/services/passport/local.js b/shared/services/passport/local.js similarity index 92% rename from services/passport/local.js rename to shared/services/passport/local.js index c2f80cb..9008d02 100644 --- a/services/passport/local.js +++ b/shared/services/passport/local.js @@ -1,7 +1,7 @@ const passport = require('passport'); const { Strategy: LocalStrategy } = require('passport-local'); -const { user: User } = require('../../models'); +const { User } = require('../../models'); const options = { usernameField: 'email', @@ -25,7 +25,7 @@ passport.use('local-login', new LocalStrategy(options, (req, email, password, do user.isCorrectPassword(password) .then(isEqual => { if (!isEqual) return done(null, false); - + console.log(user) done(null, user); }) .catch(done); diff --git a/styles/base.scss b/shared/styles/base.scss similarity index 100% rename from styles/base.scss rename to shared/styles/base.scss diff --git a/styles/components/_index.scss b/shared/styles/components/_index.scss similarity index 100% rename from styles/components/_index.scss rename to shared/styles/components/_index.scss diff --git a/styles/components/book-card.scss b/shared/styles/components/book-card.scss similarity index 100% rename from styles/components/book-card.scss rename to shared/styles/components/book-card.scss diff --git a/styles/index.scss b/shared/styles/index.scss similarity index 70% rename from styles/index.scss rename to shared/styles/index.scss index bf04e3c..b1be336 100644 --- a/styles/index.scss +++ b/shared/styles/index.scss @@ -1,5 +1,3 @@ -@import 'normalize.css/normalize'; - @import 'base'; @import 'layout'; @import 'components/index'; diff --git a/styles/layout.scss b/shared/styles/layout.scss similarity index 100% rename from styles/layout.scss rename to shared/styles/layout.scss diff --git a/styles/pages/_index.scss b/shared/styles/pages/_index.scss similarity index 100% rename from styles/pages/_index.scss rename to shared/styles/pages/_index.scss diff --git a/styles/pages/auth/_index.scss b/shared/styles/pages/auth/_index.scss similarity index 100% rename from styles/pages/auth/_index.scss rename to shared/styles/pages/auth/_index.scss diff --git a/styles/pages/auth/common.scss b/shared/styles/pages/auth/common.scss similarity index 100% rename from styles/pages/auth/common.scss rename to shared/styles/pages/auth/common.scss diff --git a/styles/pages/auth/login.scss b/shared/styles/pages/auth/login.scss similarity index 100% rename from styles/pages/auth/login.scss rename to shared/styles/pages/auth/login.scss diff --git a/styles/pages/auth/register.scss b/shared/styles/pages/auth/register.scss similarity index 100% rename from styles/pages/auth/register.scss rename to shared/styles/pages/auth/register.scss diff --git a/styles/pages/error.scss b/shared/styles/pages/error.scss similarity index 100% rename from styles/pages/error.scss rename to shared/styles/pages/error.scss diff --git a/shared/utils/error.js b/shared/utils/error.js new file mode 100644 index 0000000..9595c6a --- /dev/null +++ b/shared/utils/error.js @@ -0,0 +1,10 @@ +module.exports = { + NotFoundError: class NotFoundError extends Error { + constructor(message = 'Не найдено') { + super(message); + + this.name = 'NotFoundError'; + this.status = 404; + } + } +}; \ No newline at end of file diff --git a/shared/utils/format.js b/shared/utils/format.js new file mode 100644 index 0000000..dd4bd2d --- /dev/null +++ b/shared/utils/format.js @@ -0,0 +1,9 @@ +module.exports = { + splitBy: symbol => value => { + if (typeof value !== 'string') { + return value; + } else { + return value.split(symbol).map(value => value.trim()); + } + } +}; \ No newline at end of file diff --git a/views/_layout.pug b/shared/views/_layout.pug similarity index 83% rename from views/_layout.pug rename to shared/views/_layout.pug index 2bdb7e9..5c386f4 100644 --- a/views/_layout.pug +++ b/shared/views/_layout.pug @@ -9,6 +9,7 @@ html(lang='ru') link(rel='stylesheet' href='https://fonts.googleapis.com/css?family=Roboto:300,400,400i,700&subset=cyrillic') link(rel='stylesheet' href='https://fonts.googleapis.com/icon?family=Material+Icons') + link(rel='stylesheet' href='/lib/normalize.css/normalize.css') link(rel='stylesheet' href='/lib/material-components-web/dist/material-components-web.min.css') link(rel='stylesheet' href=`/css/app.css?v=${version}`) @@ -17,13 +18,7 @@ html(lang='ru') block head body(id=`${id}-page` class=className).mdc-typography block header - include _includes/toolbar block content - section#content - block sidenav - include _includes/sidenav - - block main block scripts \ No newline at end of file diff --git a/shared/views/_mixins/form.pug b/shared/views/_mixins/form.pug new file mode 100644 index 0000000..5036765 --- /dev/null +++ b/shared/views/_mixins/form.pug @@ -0,0 +1,3 @@ +mixin form(method='post', action) + form&attributes(attributes) + block \ No newline at end of file diff --git a/views/_mixins/mdc/button.pug b/shared/views/_mixins/mdc/button.pug similarity index 100% rename from views/_mixins/mdc/button.pug rename to shared/views/_mixins/mdc/button.pug diff --git a/views/_mixins/mdc/index.pug b/shared/views/_mixins/mdc/index.pug similarity index 100% rename from views/_mixins/mdc/index.pug rename to shared/views/_mixins/mdc/index.pug diff --git a/views/_mixins/mdc/textfield.pug b/shared/views/_mixins/mdc/textfield.pug similarity index 79% rename from views/_mixins/mdc/textfield.pug rename to shared/views/_mixins/mdc/textfield.pug index 3685298..077f99e 100644 --- a/views/_mixins/mdc/textfield.pug +++ b/shared/views/_mixins/mdc/textfield.pug @@ -1,4 +1,4 @@ mixin mdc-text-field(name, label, value) div.mdc-text-field - input.mdc-text-field__input(type=attributes.type || 'text' name=name placeholder=label) + input.mdc-text-field__input(type=attributes.type || 'text' name=name value=value placeholder=label) div.mdc-text-field__bottom-line \ No newline at end of file diff --git a/views/error.pug b/shared/views/error.pug similarity index 100% rename from views/error.pug rename to shared/views/error.pug diff --git a/utils/format.js b/utils/format.js deleted file mode 100644 index 36b9fd8..0000000 --- a/utils/format.js +++ /dev/null @@ -1,7 +0,0 @@ -function split(value) { - if (typeof value !== 'string') { - return value; - } else { - return value.split(',').map(value => value.trim()); - } -} \ No newline at end of file diff --git a/views/404.pug b/views/404.pug deleted file mode 100644 index c8e20a2..0000000 --- a/views/404.pug +++ /dev/null @@ -1,4 +0,0 @@ -extends _layout - -block content - h1 Не найдено \ No newline at end of file diff --git a/views/dev.pug b/views/dev.pug deleted file mode 100644 index e69de29..0000000 diff --git a/views/profile/edit.pug b/views/profile/edit.pug deleted file mode 100644 index e69de29..0000000 From 5e566fbfd16837983bc9be66af94aa35d1f8bf13 Mon Sep 17 00:00:00 2001 From: Oleg Polyakov Date: Wed, 14 Feb 2018 00:39:48 +0300 Subject: [PATCH 3/7] Implement search --- main/controllers/book.js | 26 ++++++++++++++++++++++++++ main/controllers/index.js | 2 +- main/controllers/search.js | 6 ------ main/index.js | 2 +- main/routers/book.js | 1 + main/routers/search.js | 7 ------- main/views/_includes/search-form.pug | 3 +++ main/views/_layout.pug | 9 +-------- main/views/_mixins/book-card.pug | 4 ++++ main/views/books/_layout.pug | 10 ++++++++++ main/views/books/book.pug | 2 +- main/views/books/index.pug | 2 +- shared/models/book.js | 1 + 13 files changed, 50 insertions(+), 25 deletions(-) delete mode 100644 main/controllers/search.js delete mode 100644 main/routers/search.js create mode 100644 main/views/_includes/search-form.pug create mode 100644 main/views/books/_layout.pug diff --git a/main/controllers/book.js b/main/controllers/book.js index 66d6ab7..f929bd2 100644 --- a/main/controllers/book.js +++ b/main/controllers/book.js @@ -75,6 +75,32 @@ module.exports = { .catch(next); }, + // GET /books/search?query=value + showResults(req, res, next) { + let regex = new RegExp(req.query.query, 'gi'); + let { skip = 0, limit = 0 } = req.query; + + Book.find({ + $or: [ + { title: regex }, + { authors: regex }, + { topics: regex }, + { tags: regex } + ] + }) + .skip(Number(skip)) + .limit(Number(limit)) + .then(books => { + res.render('books', { + id: 'book-search', + title: `Результаты поиска по запрос: ${req.query.query}`, + query: req.query.query, + books + }); + }) + .catch(next); + }, + // GET /books/:book showBook(req, res) { res.render('books/book', { diff --git a/main/controllers/index.js b/main/controllers/index.js index 78530b0..deeeb7c 100644 --- a/main/controllers/index.js +++ b/main/controllers/index.js @@ -1,8 +1,8 @@ module.exports = { auth: require('./auth'), book: require('./book'), + cart: require('./cart'), main: require('./main'), oauth: require('./oauth'), - search: require('./search'), topic: require('./topic') }; \ No newline at end of file diff --git a/main/controllers/search.js b/main/controllers/search.js deleted file mode 100644 index db32917..0000000 --- a/main/controllers/search.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - // GET /search?query=value - showResults(req, res) { - res.render('search', {}); - } -}; \ No newline at end of file diff --git a/main/index.js b/main/index.js index 6fa63d9..3ddfdd5 100644 --- a/main/index.js +++ b/main/index.js @@ -24,6 +24,6 @@ main.use('/auth', routers.auth); main.use('/user', middleware.auth.allowAuthenticated, routers.user); main.use('/books', routers.book); main.use('/topics', routers.topic); -main.use('/search', routers.search); +main.use('/cart', routers.cart); module.exports = main; \ No newline at end of file diff --git a/main/routers/book.js b/main/routers/book.js index 3b10d19..d83f88d 100644 --- a/main/routers/book.js +++ b/main/routers/book.js @@ -9,6 +9,7 @@ router.param('book', bookController.findBook); router.get('/', bookController.showLatestBooks); router.get('/new', bookController.showNewBooks); router.get('/best', bookController.showBestBooks); +router.get('/search', bookController.showResults); router.get('/:book', bookController.showBook); module.exports = router; \ No newline at end of file diff --git a/main/routers/search.js b/main/routers/search.js deleted file mode 100644 index dd6ca31..0000000 --- a/main/routers/search.js +++ /dev/null @@ -1,7 +0,0 @@ -const router = require('express').Router(); - -const { search } = require('../controllers'); - -router.get('/', search.showResults); - -module.exports = router; \ No newline at end of file diff --git a/main/views/_includes/search-form.pug b/main/views/_includes/search-form.pug new file mode 100644 index 0000000..067fe67 --- /dev/null +++ b/main/views/_includes/search-form.pug @@ -0,0 +1,3 @@ +form(method='get' action='/books/search') + input(type='search' name='query' value=query) + button(type='submit') Найти \ No newline at end of file diff --git a/main/views/_layout.pug b/main/views/_layout.pug index 4ee72f8..bb0fb31 100644 --- a/main/views/_layout.pug +++ b/main/views/_layout.pug @@ -3,11 +3,4 @@ extends /_layout include /_mixins/mdc/index block header - include _includes/toolbar - -block content - section#content - block sidenav - include _includes/sidenav - - block main \ No newline at end of file + include _includes/toolbar \ No newline at end of file diff --git a/main/views/_mixins/book-card.pug b/main/views/_mixins/book-card.pug index 9bcb394..8b58a98 100644 --- a/main/views/_mixins/book-card.pug +++ b/main/views/_mixins/book-card.pug @@ -14,5 +14,9 @@ mixin book-card(book) button.mdc-button.mdc-button--compact.mdc-card__action i.mdc-button__icon.material-icons thumb_up span= book.likes + form(method='post' action='/cart') + input(type='hidden' name='productId' value=book.id) + button(type='submit').mdc-button.mdc-button--compact.mdc-card__action + i.mdc-button__icon.material-icons shopping_basket a.mdc-button.mdc-button--compact.mdc-card__action(href=`/books/${book.slug}`) Подробнее \ No newline at end of file diff --git a/main/views/books/_layout.pug b/main/views/books/_layout.pug new file mode 100644 index 0000000..e996dc3 --- /dev/null +++ b/main/views/books/_layout.pug @@ -0,0 +1,10 @@ +extends ../_layout + +block content + section#content + include ../_includes/sidenav + + div + include ../_includes/search-form + + block main \ No newline at end of file diff --git a/main/views/books/book.pug b/main/views/books/book.pug index 4584c57..dfe999c 100644 --- a/main/views/books/book.pug +++ b/main/views/books/book.pug @@ -1,4 +1,4 @@ -extends ../_layout +extends _layout block main main#main diff --git a/main/views/books/index.pug b/main/views/books/index.pug index 3c4b956..ee02a81 100644 --- a/main/views/books/index.pug +++ b/main/views/books/index.pug @@ -1,4 +1,4 @@ -extends ../_layout +extends _layout include ../_mixins/book-card diff --git a/shared/models/book.js b/shared/models/book.js index 68314c9..4830f8d 100644 --- a/shared/models/book.js +++ b/shared/models/book.js @@ -20,6 +20,7 @@ const Book = new Schema({ likes: { type: Number, default: 0 }, url: String, imageUrl: String, + price: { type: Number, default: 1000 }, codeUrl: String, githubUrl: String, description: { type: String, default: '', trim: true }, From 2256ab9173d24a1bd2c4c60887f0921e727c739e Mon Sep 17 00:00:00 2001 From: Oleg Polyakov Date: Wed, 14 Feb 2018 00:40:44 +0300 Subject: [PATCH 4/7] Implement flash messages --- shared/middleware/flash.js | 15 +++++++++++++++ shared/views/_layout.pug | 5 ++++- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 shared/middleware/flash.js diff --git a/shared/middleware/flash.js b/shared/middleware/flash.js new file mode 100644 index 0000000..81a4faa --- /dev/null +++ b/shared/middleware/flash.js @@ -0,0 +1,15 @@ +module.exports = function createFlashMiddleware(options) { + return function flash(req, res, next) { + req.flash = function(level = 'info', message) { + req.session.flash = { level, message }; + }; + + let flash = req.session.flash; + req.session.flash = null; + delete req.session.flash; + + res.locals.flash = flash; + + next(); + }; +}; \ No newline at end of file diff --git a/shared/views/_layout.pug b/shared/views/_layout.pug index 5c386f4..dac4b3a 100644 --- a/shared/views/_layout.pug +++ b/shared/views/_layout.pug @@ -18,7 +18,10 @@ html(lang='ru') block head body(id=`${id}-page` class=className).mdc-typography block header - + + if flash + div#flash(class=`flash-${flash.level}`)= flash.message + block content block scripts \ No newline at end of file From f89879d9e4a2b218f7f83e9473eafef491e88657 Mon Sep 17 00:00:00 2001 From: Oleg Polyakov Date: Wed, 14 Feb 2018 00:40:52 +0300 Subject: [PATCH 5/7] Implement cart --- index.js | 2 ++ main/controllers/cart.js | 31 +++++++++++++++++++++++++++++++ main/routers/cart.js | 9 +++++++++ main/routers/index.js | 2 +- main/views/cart/index.pug | 11 +++++++++++ shared/middleware/cart.js | 24 ++++++++++++++++++++++++ shared/middleware/index.js | 4 +++- shared/models/cart.js | 28 ++++++++++++++++++++++++++++ shared/models/index.js | 1 + 9 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 main/controllers/cart.js create mode 100644 main/routers/cart.js create mode 100644 main/views/cart/index.pug create mode 100644 shared/middleware/cart.js create mode 100644 shared/models/cart.js diff --git a/index.js b/index.js index b2626c3..e2e99fe 100644 --- a/index.js +++ b/index.js @@ -49,6 +49,8 @@ server.use(session({ server.use(services.passport.initialize()); server.use(services.passport.session()); +server.use(middleware.flash()); +server.use(middleware.cart); server.use('/', main); server.use('/admin', admin); diff --git a/main/controllers/cart.js b/main/controllers/cart.js new file mode 100644 index 0000000..c903995 --- /dev/null +++ b/main/controllers/cart.js @@ -0,0 +1,31 @@ +module.exports = { + // GET / + showCart(req, res, next) { + req.cart.populate('items') + .execPopulate() + .then(cart => { + res.render('cart', { + id: 'cart', + title: 'Корзина', + cart + }); + }) + .catch(next); + }, + + // POST /cart + addProduct(req, res) { + req.cart.addProduct(req.body.productId); + req.flash('success', 'Товар добавлен'); + + res.redirect('back'); + }, + + // GET /cart/remove?productId=value + removeProduct(req, res) { + req.cart.removeProduct(req.query.productId); + req.flash('success', 'Товар удален'); + + res.redirect('back'); + } +}; \ No newline at end of file diff --git a/main/routers/cart.js b/main/routers/cart.js new file mode 100644 index 0000000..40f5730 --- /dev/null +++ b/main/routers/cart.js @@ -0,0 +1,9 @@ +const router = require('express').Router(); + +const { cart } = require('../controllers'); + +router.get('/', cart.showCart); +router.post('/', cart.addProduct); +router.get('/remove', cart.removeProduct); + +module.exports = router; \ No newline at end of file diff --git a/main/routers/index.js b/main/routers/index.js index bd956d7..cdaa7e9 100644 --- a/main/routers/index.js +++ b/main/routers/index.js @@ -1,8 +1,8 @@ module.exports = { auth: require('./auth'), book: require('./book'), + cart: require('./cart'), main: require('./main'), - search: require('./search'), topic: require('./topic'), user: require('./user') }; \ No newline at end of file diff --git a/main/views/cart/index.pug b/main/views/cart/index.pug new file mode 100644 index 0000000..cf1667b --- /dev/null +++ b/main/views/cart/index.pug @@ -0,0 +1,11 @@ +extends ../_layout + +block content + section#content + ul + for item in cart.items + li + span= item.title + | + a(href=`/cart/remove?productId=${item.id}`) Удалить + li Итого #{cart.calculateTotal()} руб. \ No newline at end of file diff --git a/shared/middleware/cart.js b/shared/middleware/cart.js new file mode 100644 index 0000000..d5aa29f --- /dev/null +++ b/shared/middleware/cart.js @@ -0,0 +1,24 @@ +const { Cart } = require('../../shared/models'); + +module.exports = function cart(req, res, next) { + if (!req.session) throw new Error('Session is required. Try installing `npm install express-session`'); + + if (req.session.cartId) { + Cart.findById(req.session.cartId) + .then(cart => { + req.cart = cart; + + next(); + }) + .catch(next); + } else { + Cart.create({}) + .then(cart => { + req.session.cartId = cart.id; + req.cart = cart; + + next(); + }) + .catch(next); + } +}; \ No newline at end of file diff --git a/shared/middleware/index.js b/shared/middleware/index.js index 61509f0..8949fbe 100644 --- a/shared/middleware/index.js +++ b/shared/middleware/index.js @@ -1,3 +1,5 @@ module.exports = { - error: require('./error') + cart: require('./cart'), + error: require('./error'), + flash: require('./flash') }; \ No newline at end of file diff --git a/shared/models/cart.js b/shared/models/cart.js new file mode 100644 index 0000000..0b1c535 --- /dev/null +++ b/shared/models/cart.js @@ -0,0 +1,28 @@ +const mongoose = require('mongoose'); + +const Schema = mongoose.Schema; + +const Cart = new Schema({ + user: { type: Schema.Types.ObjectId, ref: 'User' }, + items: [{ type: Schema.Types.ObjectId, ref: 'Book' }] +}, { + timestamps: true +}); + +Cart.methods.calculateTotal = function() { + return this.items.reduce((total, item) => item.price + total, 0); +}; + +Cart.methods.addProduct = function(productId) { + this.items.addToSet(productId); + + return this.save(); +}; + +Cart.methods.removeProduct = function(productId) { + this.items.pull(productId); + + return this.save(); +}; + +module.exports = mongoose.model('Cart', Cart); \ No newline at end of file diff --git a/shared/models/index.js b/shared/models/index.js index 30dfcd5..ed77e1c 100644 --- a/shared/models/index.js +++ b/shared/models/index.js @@ -1,5 +1,6 @@ module.exports = { Book: require('./book'), + Cart: require('./cart'), Publisher: require('./publisher'), Topic: require('./topic'), User: require('./user') From ba06a807543d963478a5544a2a0fe81157f560ce Mon Sep 17 00:00:00 2001 From: Oleg Polyakov Date: Mon, 5 Mar 2018 11:25:42 +0300 Subject: [PATCH 6/7] Chores --- admin/controllers/home.js | 4 +- main/middleware/auth.js | 2 + main/routers/user.js | 4 +- main/views/_includes/search-form.pug | 6 +- main/views/auth/login.pug | 25 +- main/views/auth/register.pug | 27 +- main/views/books/book.pug | 3 +- main/views/index.pug | 11 +- main/views/profile/index.pug | 7 - main/views/user/index.pug | 8 + shared/middleware/flash.js | 16 +- shared/models/user.js | 8 +- shared/public/css/app.css | 460 +++--------------- shared/public/img/bg.jpg | Bin 0 -> 248635 bytes shared/services/db.js | 2 +- shared/services/passport/github.js | 11 +- shared/styles/components/_index.scss | 4 +- shared/styles/components/flash.scss | 9 + shared/styles/components/search-form.scss | 3 + shared/styles/pages/_index.scss | 4 +- shared/styles/pages/auth/common.scss | 13 +- shared/styles/pages/main.scss | 32 ++ shared/styles/pages/user.scss | 21 + shared/views/_layout.pug | 7 +- shared/views/_mixins/mdc/index.pug | 2 +- .../mdc/{textfield.pug => text-field.pug} | 0 26 files changed, 216 insertions(+), 473 deletions(-) delete mode 100644 main/views/profile/index.pug create mode 100644 main/views/user/index.pug create mode 100644 shared/public/img/bg.jpg create mode 100644 shared/styles/components/flash.scss create mode 100644 shared/styles/components/search-form.scss create mode 100644 shared/styles/pages/main.scss create mode 100644 shared/styles/pages/user.scss rename shared/views/_mixins/mdc/{textfield.pug => text-field.pug} (100%) diff --git a/admin/controllers/home.js b/admin/controllers/home.js index 1060439..172a2eb 100644 --- a/admin/controllers/home.js +++ b/admin/controllers/home.js @@ -1,5 +1,7 @@ module.exports = { showIndexPage(req, res) { - res.render('index'); + res.render('index', { + id: 'admin' + }); } }; \ No newline at end of file diff --git a/main/middleware/auth.js b/main/middleware/auth.js index 19c3dc9..20fe157 100644 --- a/main/middleware/auth.js +++ b/main/middleware/auth.js @@ -2,6 +2,8 @@ module.exports = { allowAuthenticated(req, res, next) { if (req.isAuthenticated()) return next(); + req.flash('error', 'Для продолжения необходимо войти или зарегистрироваться'); + res.status(403).redirect('/auth/login'); }, diff --git a/main/routers/user.js b/main/routers/user.js index 3ed96c1..43ff37f 100644 --- a/main/routers/user.js +++ b/main/routers/user.js @@ -1,8 +1,8 @@ const router = require('express').Router(); router.get('/', (req, res) => { - res.render('profile', { - id: 'profile', + res.render('user', { + id: 'user', title: 'Профиль', user: req.user }); diff --git a/main/views/_includes/search-form.pug b/main/views/_includes/search-form.pug index 067fe67..5d71bc5 100644 --- a/main/views/_includes/search-form.pug +++ b/main/views/_includes/search-form.pug @@ -1,3 +1,3 @@ -form(method='get' action='/books/search') - input(type='search' name='query' value=query) - button(type='submit') Найти \ No newline at end of file +form#search-form(method='get' action='/books/search') + +mdc-text-field('query', 'Поиск', value)(type='search') + +mdc-button('Найти')(type='submit') \ No newline at end of file diff --git a/main/views/auth/login.pug b/main/views/auth/login.pug index 5e83d94..d7b13b2 100644 --- a/main/views/auth/login.pug +++ b/main/views/auth/login.pug @@ -3,17 +3,18 @@ extends ../_layout block header block content - form(id='login-form' class='auth-form' method='POST' action='/auth/login').mdc-card - header.mdc-card__primary - h1.mdc-card__title.mdc-card__title--large= title + main#main + form(id='login-form' class='auth-form' method='POST' action='/auth/login').mdc-card + header.mdc-card__primary + h1.mdc-card__title.mdc-card__title--large= title - section.mdc-card__supporting-text - div.mdc-text-field - input.mdc-text-field__input(type='email' name='email' placeholder='Электронная почта' required) - div.mdc-text-field - input.mdc-text-field__input(type='password' name='password' placeholder='Пароль' required) + section.mdc-card__supporting-text + div.mdc-text-field + input.mdc-text-field__input(type='email' name='email' placeholder='Электронная почта' required) + div.mdc-text-field + input.mdc-text-field__input(type='password' name='password' placeholder='Пароль' required) - footer.mdc-card__actions - button.mdc-button.mdc-card__action.mdc-button--raised.mdc-button--dense(type='submit') Войти - a.mdc-button.mdc-card__action.mdc-button--dense(href='/auth/github') GitHub - a.mdc-button.mdc-card__action.mdc-button--dense(href='/auth/register') Зарегистрироваться \ No newline at end of file + footer.mdc-card__actions + button.mdc-button.mdc-card__action.mdc-button--raised.mdc-button--dense(type='submit') Войти + a.mdc-button.mdc-card__action.mdc-button--dense(href='/auth/github') GitHub + a.mdc-button.mdc-card__action.mdc-button--dense(href='/auth/register') Зарегистрироваться \ No newline at end of file diff --git a/main/views/auth/register.pug b/main/views/auth/register.pug index e1782f6..30910f1 100644 --- a/main/views/auth/register.pug +++ b/main/views/auth/register.pug @@ -3,18 +3,19 @@ extends ../_layout block header block content - form(id='register-form' class='auth-form' method='POST' action='/auth/register').mdc-card - header.mdc-card__primary - h1.mdc-card__title.mdc-card__title--large= title + main#main + form(id='register-form' class='auth-form' method='POST' action='/auth/register').mdc-card + header.mdc-card__primary + h1.mdc-card__title.mdc-card__title--large= title - section.mdc-card__supporting-text - div.mdc-text-field - input.mdc-text-field__input(type='email' name='email' placeholder='Электронная почта' required) - div.mdc-text-field - input.mdc-text-field__input(type='password' name='password' placeholder='Пароль' required) - div.mdc-text-field - input.mdc-text-field__input(type='password' name='confirmPassword' placeholder='Подтвердите пароль' required) + section.mdc-card__supporting-text + div.mdc-text-field + input.mdc-text-field__input(type='email' name='email' placeholder='Электронная почта' required) + div.mdc-text-field + input.mdc-text-field__input(type='password' name='password' placeholder='Пароль' required) + div.mdc-text-field + input.mdc-text-field__input(type='password' name='confirmPassword' placeholder='Подтвердите пароль' required) - footer.mdc-card__actions - button.mdc-button.mdc-card__action.mdc-button--raised.mdc-button--dense(type='submit') Зарегистрироваться - a.mdc-button.mdc-card__action.mdc-button--dense(href='/auth/login') Войти \ No newline at end of file + footer.mdc-card__actions + button.mdc-button.mdc-card__action.mdc-button--raised.mdc-button--dense(type='submit') Зарегистрироваться + a.mdc-button.mdc-card__action.mdc-button--dense(href='/auth/login') Войти \ No newline at end of file diff --git a/main/views/books/book.pug b/main/views/books/book.pug index dfe999c..928e472 100644 --- a/main/views/books/book.pug +++ b/main/views/books/book.pug @@ -4,7 +4,7 @@ block main main#main div.mdc-layout-grid div.mdc-layout-grid__inner - div.mdc-layout-grid__cell.mdc-layout-grid__cell--span-6 + div.mdc-layout-grid__cell.mdc-layout-grid__cell--span-12 div.mdc-card.book-card div.mdc-card__horizontal-block header.mdc-card__primary @@ -36,6 +36,7 @@ block main section.mdc-card__supporting-text h2.mdc-typography--title Описание + != book.description section.mdc-card__supporting-text diff --git a/main/views/index.pug b/main/views/index.pug index 4196241..1998a08 100644 --- a/main/views/index.pug +++ b/main/views/index.pug @@ -1,4 +1,13 @@ extends _layout +block header + block content - h1 Главная страница \ No newline at end of file + main#main + i.material-icons book + h1 CodeLibrary + p Библиотека разработчика + + a.mdc-button.mdc-button--raised(href='/auth/login') Войти + | или + a.mdc-button.mdc-button--raised(href='/auth/register') Зарегистрироваться \ No newline at end of file diff --git a/main/views/profile/index.pug b/main/views/profile/index.pug deleted file mode 100644 index 70245e8..0000000 --- a/main/views/profile/index.pug +++ /dev/null @@ -1,7 +0,0 @@ -extends ../_layout - -block sidenav - -block main - main#main - h1=user.email \ No newline at end of file diff --git a/main/views/user/index.pug b/main/views/user/index.pug new file mode 100644 index 0000000..79e6b23 --- /dev/null +++ b/main/views/user/index.pug @@ -0,0 +1,8 @@ +extends ../_layout + +block content + main#main + if user.photo + img.user-photo(src=user.photo) + h1.user-name= user.displayName + p.user-email= user.email \ No newline at end of file diff --git a/shared/middleware/flash.js b/shared/middleware/flash.js index 81a4faa..38438ef 100644 --- a/shared/middleware/flash.js +++ b/shared/middleware/flash.js @@ -1,15 +1,5 @@ -module.exports = function createFlashMiddleware(options) { - return function flash(req, res, next) { - req.flash = function(level = 'info', message) { - req.session.flash = { level, message }; - }; +module.exports = function flash(req, res, next) { + res.locals.errors = req.flash('error'); - let flash = req.session.flash; - req.session.flash = null; - delete req.session.flash; - - res.locals.flash = flash; - - next(); - }; + next(); }; \ No newline at end of file diff --git a/shared/models/user.js b/shared/models/user.js index 28c62a0..2cb0cf5 100644 --- a/shared/models/user.js +++ b/shared/models/user.js @@ -13,12 +13,18 @@ const User = new mongoose.Schema({ }, password: { type: String, required: true }, username: { type: String, unique: true, sparse: true }, - photo: String, + firstname: { type: String }, + lastname: { type: String }, + photo: { type: String }, role: { type: String, default: 'user', enum: ['user', 'admin'] } }, { timestamps: true }); +User.virtual('displayName').get(function() { + return `${this.firstname} ${this.lastname}`; +}); + User.virtual('isAdmin').get(function() { return this.role === 'admin'; }); diff --git a/shared/public/css/app.css b/shared/public/css/app.css index 77e3c58..bcf15f4 100644 --- a/shared/public/css/app.css +++ b/shared/public/css/app.css @@ -1,398 +1,3 @@ -/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */ -/* Document - ========================================================================== */ -/** - * 1. Correct the line height in all browsers. - * 2. Prevent adjustments of font size after orientation changes in - * IE on Windows Phone and in iOS. - */ -html { - line-height: 1.15; - /* 1 */ - -ms-text-size-adjust: 100%; - /* 2 */ - -webkit-text-size-adjust: 100%; - /* 2 */ } - -/* Sections - ========================================================================== */ -/** - * Remove the margin in all browsers (opinionated). - */ -body { - margin: 0; } - -/** - * Add the correct display in IE 9-. - */ -article, -aside, -footer, -header, -nav, -section { - display: block; } - -/** - * Correct the font size and margin on `h1` elements within `section` and - * `article` contexts in Chrome, Firefox, and Safari. - */ -h1 { - font-size: 2em; - margin: 0.67em 0; } - -/* Grouping content - ========================================================================== */ -/** - * Add the correct display in IE 9-. - * 1. Add the correct display in IE. - */ -figcaption, -figure, -main { - /* 1 */ - display: block; } - -/** - * Add the correct margin in IE 8. - */ -figure { - margin: 1em 40px; } - -/** - * 1. Add the correct box sizing in Firefox. - * 2. Show the overflow in Edge and IE. - */ -hr { - -webkit-box-sizing: content-box; - box-sizing: content-box; - /* 1 */ - height: 0; - /* 1 */ - overflow: visible; - /* 2 */ } - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ -pre { - font-family: monospace, monospace; - /* 1 */ - font-size: 1em; - /* 2 */ } - -/* Text-level semantics - ========================================================================== */ -/** - * 1. Remove the gray background on active links in IE 10. - * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. - */ -a { - background-color: transparent; - /* 1 */ - -webkit-text-decoration-skip: objects; - /* 2 */ } - -/** - * 1. Remove the bottom border in Chrome 57- and Firefox 39-. - * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. - */ -abbr[title] { - border-bottom: none; - /* 1 */ - text-decoration: underline; - /* 2 */ - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; - /* 2 */ } - -/** - * Prevent the duplicate application of `bolder` by the next rule in Safari 6. - */ -b, -strong { - font-weight: inherit; } - -/** - * Add the correct font weight in Chrome, Edge, and Safari. - */ -b, -strong { - font-weight: bolder; } - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ -code, -kbd, -samp { - font-family: monospace, monospace; - /* 1 */ - font-size: 1em; - /* 2 */ } - -/** - * Add the correct font style in Android 4.3-. - */ -dfn { - font-style: italic; } - -/** - * Add the correct background and color in IE 9-. - */ -mark { - background-color: #ff0; - color: #000; } - -/** - * Add the correct font size in all browsers. - */ -small { - font-size: 80%; } - -/** - * Prevent `sub` and `sup` elements from affecting the line height in - * all browsers. - */ -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; } - -sub { - bottom: -0.25em; } - -sup { - top: -0.5em; } - -/* Embedded content - ========================================================================== */ -/** - * Add the correct display in IE 9-. - */ -audio, -video { - display: inline-block; } - -/** - * Add the correct display in iOS 4-7. - */ -audio:not([controls]) { - display: none; - height: 0; } - -/** - * Remove the border on images inside links in IE 10-. - */ -img { - border-style: none; } - -/** - * Hide the overflow in IE. - */ -svg:not(:root) { - overflow: hidden; } - -/* Forms - ========================================================================== */ -/** - * 1. Change the font styles in all browsers (opinionated). - * 2. Remove the margin in Firefox and Safari. - */ -button, -input, -optgroup, -select, -textarea { - font-family: sans-serif; - /* 1 */ - font-size: 100%; - /* 1 */ - line-height: 1.15; - /* 1 */ - margin: 0; - /* 2 */ } - -/** - * Show the overflow in IE. - * 1. Show the overflow in Edge. - */ -button, -input { - /* 1 */ - overflow: visible; } - -/** - * Remove the inheritance of text transform in Edge, Firefox, and IE. - * 1. Remove the inheritance of text transform in Firefox. - */ -button, -select { - /* 1 */ - text-transform: none; } - -/** - * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` - * controls in Android 4. - * 2. Correct the inability to style clickable types in iOS and Safari. - */ -button, -html [type="button"], -[type="reset"], -[type="submit"] { - -webkit-appearance: button; - /* 2 */ } - -/** - * Remove the inner border and padding in Firefox. - */ -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - border-style: none; - padding: 0; } - -/** - * Restore the focus styles unset by the previous rule. - */ -button:-moz-focusring, -[type="button"]:-moz-focusring, -[type="reset"]:-moz-focusring, -[type="submit"]:-moz-focusring { - outline: 1px dotted ButtonText; } - -/** - * Correct the padding in Firefox. - */ -fieldset { - padding: 0.35em 0.75em 0.625em; } - -/** - * 1. Correct the text wrapping in Edge and IE. - * 2. Correct the color inheritance from `fieldset` elements in IE. - * 3. Remove the padding so developers are not caught out when they zero out - * `fieldset` elements in all browsers. - */ -legend { - -webkit-box-sizing: border-box; - box-sizing: border-box; - /* 1 */ - color: inherit; - /* 2 */ - display: table; - /* 1 */ - max-width: 100%; - /* 1 */ - padding: 0; - /* 3 */ - white-space: normal; - /* 1 */ } - -/** - * 1. Add the correct display in IE 9-. - * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. - */ -progress { - display: inline-block; - /* 1 */ - vertical-align: baseline; - /* 2 */ } - -/** - * Remove the default vertical scrollbar in IE. - */ -textarea { - overflow: auto; } - -/** - * 1. Add the correct box sizing in IE 10-. - * 2. Remove the padding in IE 10-. - */ -[type="checkbox"], -[type="radio"] { - -webkit-box-sizing: border-box; - box-sizing: border-box; - /* 1 */ - padding: 0; - /* 2 */ } - -/** - * Correct the cursor style of increment and decrement buttons in Chrome. - */ -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; } - -/** - * 1. Correct the odd appearance in Chrome and Safari. - * 2. Correct the outline style in Safari. - */ -[type="search"] { - -webkit-appearance: textfield; - /* 1 */ - outline-offset: -2px; - /* 2 */ } - -/** - * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. - */ -[type="search"]::-webkit-search-cancel-button, -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; } - -/** - * 1. Correct the inability to style clickable types in iOS and Safari. - * 2. Change font properties to `inherit` in Safari. - */ -::-webkit-file-upload-button { - -webkit-appearance: button; - /* 1 */ - font: inherit; - /* 2 */ } - -/* Interactive - ========================================================================== */ -/* - * Add the correct display in IE 9-. - * 1. Add the correct display in Edge, IE, and Firefox. - */ -details, -menu { - display: block; } - -/* - * Add the correct display in all browsers. - */ -summary { - display: list-item; } - -/* Scripting - ========================================================================== */ -/** - * Add the correct display in IE 9-. - */ -canvas { - display: inline-block; } - -/** - * Add the correct display in IE. - */ -template { - display: none; } - -/* Hidden - ========================================================================== */ -/** - * Add the correct display in IE 10-. - */ -[hidden] { - display: none; } - body { background-color: rgba(0, 0, 0, 0.05); } @@ -413,17 +18,33 @@ body { .book-card .mdc-card__action:last-child { margin-left: auto; } +.flash { + padding: 16px; + text-align: center; } + .flash.flash--error { + background-color: tomato; + color: white; } + +#search-form { + padding: 4px 24px 0px; } + .auth-page { - display: -webkit-box; - display: -ms-flexbox; - display: flex; - height: 100vh; - -webkit-box-pack: center; - -ms-flex-pack: center; - justify-content: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; } + background-image: url("../img/bg.jpg"); } + .auth-page #main { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + height: 100vh; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; } .auth-page .auth-form { width: 100%; max-width: 400px; } @@ -441,3 +62,32 @@ body { font-size: 5em; } #error-page #main pre { text-align: left; } + +#main-page { + background-image: url("../img/bg.jpg"); } + #main-page .mdc-toolbar { + background-color: transparent; } + #main-page #main { + color: white; + text-align: center; + margin-top: 128px; } + #main-page #main i { + font-size: 72px; } + #main-page #main h1 { + font-size: 64px; + margin: .25em; } + #main-page #main p { + font-size: 36px; + margin: .25em 0 1em; } + #main-page #main a { + display: inline-block; + margin: .5em; } + +#user-page #main { + text-align: center; + padding: 64px 16px 0px; } + +#user-page .user-photo { + max-width: 128px; + border-radius: 100%; + display: inline-block; } diff --git a/shared/public/img/bg.jpg b/shared/public/img/bg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..799b45fb70e87c8e59aa9731e065be416aaf3480 GIT binary patch literal 248635 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

e|G)TL{=oP--bhKfhOQ-vdwE#Al@T<|Q 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 literal 0 HcmV?d00001 diff --git a/shared/services/db.js b/shared/services/db.js index ee0f2fc..4b1cf6a 100644 --- a/shared/services/db.js +++ b/shared/services/db.js @@ -4,7 +4,7 @@ const { mongodbUri } = require('../config'); mongoose.Promise = global.Promise; -mongoose.connect(mongodbUri.local); +mongoose.connect(mongodbUri); mongoose.connection.on('error', console.error.bind(console, 'MongoDB connection error:')); mongoose.connection.once('open', () => console.log('Connected to MongoDB')); diff --git a/shared/services/passport/github.js b/shared/services/passport/github.js index 87b78db..7dcf3f5 100644 --- a/shared/services/passport/github.js +++ b/shared/services/passport/github.js @@ -5,16 +5,21 @@ const { User } = require('../../models'); const config = require('../../config'); passport.use(new GitHubStrategy(config.oauth.github, (accessToken, refreshToken, profile, done) => { - if (!profile.emails) return done(new Error('Для входа необходимо получить от GitHub email')); + if (!profile.emails) return done(null, false, { message: 'Для входа необходимо получить от GitHub email' }); + let [firstname, lastname] = profile.displayName ? profile.displayName.split(' ') : []; let email = profile.emails[0].value; let username = profile.username; let photo = profile.photos[0].value; - User.findOneAndUpdate({ email }, { + firstname, + lastname, email, username, photo - }, { upsert: true, new: true }, done); + }, { + upsert: true, + new: true + }, done); })); \ No newline at end of file diff --git a/shared/styles/components/_index.scss b/shared/styles/components/_index.scss index 1c81bd4..176c642 100644 --- a/shared/styles/components/_index.scss +++ b/shared/styles/components/_index.scss @@ -1 +1,3 @@ -@import 'book-card'; \ No newline at end of file +@import 'book-card'; +@import 'flash'; +@import 'search-form'; \ No newline at end of file diff --git a/shared/styles/components/flash.scss b/shared/styles/components/flash.scss new file mode 100644 index 0000000..b36b28a --- /dev/null +++ b/shared/styles/components/flash.scss @@ -0,0 +1,9 @@ +.flash { + padding: 16px; + text-align: center; + + &.flash--error { + background-color: tomato; + color: white; + } +} \ No newline at end of file diff --git a/shared/styles/components/search-form.scss b/shared/styles/components/search-form.scss new file mode 100644 index 0000000..1d45ccd --- /dev/null +++ b/shared/styles/components/search-form.scss @@ -0,0 +1,3 @@ +#search-form { + padding: 4px 24px 0px; +} \ No newline at end of file diff --git a/shared/styles/pages/_index.scss b/shared/styles/pages/_index.scss index cbbfd73..22dfe57 100644 --- a/shared/styles/pages/_index.scss +++ b/shared/styles/pages/_index.scss @@ -1,2 +1,4 @@ @import 'auth/index'; -@import 'error'; \ No newline at end of file +@import 'error'; +@import 'main'; +@import 'user'; \ No newline at end of file diff --git a/shared/styles/pages/auth/common.scss b/shared/styles/pages/auth/common.scss index fb41315..e2929af 100644 --- a/shared/styles/pages/auth/common.scss +++ b/shared/styles/pages/auth/common.scss @@ -1,8 +1,13 @@ .auth-page { - display: flex; - height: 100vh; - justify-content: center; - align-items: center; + background-image: url('../img/bg.jpg'); + + #main { + display: flex; + flex-direction: column; + height: 100vh; + justify-content: center; + align-items: center; + } .auth-form { width: 100%; diff --git a/shared/styles/pages/main.scss b/shared/styles/pages/main.scss new file mode 100644 index 0000000..252ba9f --- /dev/null +++ b/shared/styles/pages/main.scss @@ -0,0 +1,32 @@ +#main-page { + background-image: url('../img/bg.jpg'); + + .mdc-toolbar { + background-color: transparent; + } + + #main { + color: white; + text-align: center; + margin-top: 128px; + + i { + font-size: 72px; + } + + h1 { + font-size: 64px; + margin: .25em; + } + + p { + font-size: 36px; + margin: .25em 0 1em; + } + + a { + display: inline-block; + margin: .5em; + } + } +} \ No newline at end of file diff --git a/shared/styles/pages/user.scss b/shared/styles/pages/user.scss new file mode 100644 index 0000000..7137934 --- /dev/null +++ b/shared/styles/pages/user.scss @@ -0,0 +1,21 @@ +#user-page { + + #main { + text-align: center; + padding: 64px 16px 0px; + } + + .user-photo { + max-width: 128px; + border-radius: 100%; + display: inline-block; + } + + .user-name { + + } + + .user-email { + + } +} \ No newline at end of file diff --git a/shared/views/_layout.pug b/shared/views/_layout.pug index dac4b3a..c94cdc3 100644 --- a/shared/views/_layout.pug +++ b/shared/views/_layout.pug @@ -17,10 +17,11 @@ html(lang='ru') block head body(id=`${id}-page` class=className).mdc-typography + if errors + for error in errors + div.flash.flash--error= error + block header - - if flash - div#flash(class=`flash-${flash.level}`)= flash.message block content diff --git a/shared/views/_mixins/mdc/index.pug b/shared/views/_mixins/mdc/index.pug index f718893..e6f8654 100644 --- a/shared/views/_mixins/mdc/index.pug +++ b/shared/views/_mixins/mdc/index.pug @@ -1,2 +1,2 @@ include button -include textfield \ No newline at end of file +include text-field \ No newline at end of file diff --git a/shared/views/_mixins/mdc/textfield.pug b/shared/views/_mixins/mdc/text-field.pug similarity index 100% rename from shared/views/_mixins/mdc/textfield.pug rename to shared/views/_mixins/mdc/text-field.pug From 6466942856ccdc98e7ebf5c5870ffc4d01e803b0 Mon Sep 17 00:00:00 2001 From: Oleg Polyakov Date: Mon, 5 Mar 2018 11:28:27 +0300 Subject: [PATCH 7/7] Add security features --- .gitignore | 3 +- index.js | 78 +- main/index.js | 7 +- main/views/auth/login.pug | 2 + main/views/auth/register.pug | 2 + package-lock.json | 1380 ++++++++++++++++++++++++++++++---- package.json | 10 +- server.js | 73 ++ shared/config/index.js | 10 +- shared/middleware/csrf.js | 7 + shared/middleware/error.js | 8 + shared/middleware/https.js | 11 + shared/middleware/index.js | 4 +- shared/middleware/www.js | 11 + 14 files changed, 1370 insertions(+), 236 deletions(-) create mode 100644 server.js create mode 100644 shared/middleware/csrf.js create mode 100644 shared/middleware/https.js create mode 100644 shared/middleware/www.js diff --git a/.gitignore b/.gitignore index 1dcef2d..75a2ede 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules -.env \ No newline at end of file +.env +ssl \ No newline at end of file diff --git a/index.js b/index.js index e2e99fe..fd3f1c2 100644 --- a/index.js +++ b/index.js @@ -1,62 +1,16 @@ -const express = require('express'); -const logger = require('morgan'); -const favicon = require('serve-favicon'); -const session = require('express-session'); -const MongoStore = require('connect-mongo')(session); - -const data = require('./shared/data'); -const config = require('./shared/config'); -const services = require('./shared/services'); -const middleware = require('./shared/middleware'); - -const main = require('./main'); -const admin = require('./admin'); -const api = require('./api'); - -const server = express(); - -server.set('view engine', 'pug'); -server.set('views', config.paths.views); -server.set('config', config); - -server.locals.basedir = config.paths.views; -server.locals.VERSION = config.version; -server.locals.LANGUAGES = data.languages; - -server.use(express.static(config.paths.public)); -server.use('/lib', express.static(config.paths.lib)); -server.use(favicon(config.paths.favicon)); -server.use(express.urlencoded({ extended: false })); -server.use(express.json()); -server.use(logger('dev')); -server.use(session({ - name: 'sessionId', - secret: config.sessionSecret, - resave: false, - saveUninitialized: false, - cookie: { - httpOnly: true, - // secure: true, - //signed: true, - maxAge: 1000 * 60 * 60 * 24 * 3 // 3 days - }, - store: new MongoStore({ - mongooseConnection: services.db.connection, - ttl: 60 * 60 * 24 * 3, // 3 days - touchAfter: 60 * 60 * 24 // 1 day - }) -})); - -server.use(services.passport.initialize()); -server.use(services.passport.session()); -server.use(middleware.flash()); -server.use(middleware.cart); - -server.use('/', main); -server.use('/admin', admin); -server.use('/api', api); - -server.use(middleware.error.notFound); -server.use(server.get('env') === 'development' ? middleware.error.development : middleware.error.production); - -server.listen(config.port, () => console.log('Express:', config.port)); \ No newline at end of file +const server = require('./server'); +const port = server.get('port'); + +if (server.get('env') === 'development') { + const fs = require('fs'); + const https = require('https'); + + const ssl = { + key: fs.readFileSync(__dirname + '/ssl/key.pem'), + cert: fs.readFileSync(__dirname + '/ssl/cert.crt') + }; + + https.createServer(ssl, server).listen(port, () => console.log('Express:', port)); +} else { + server.listen(port, () => console.log('Express:', port)); +} \ No newline at end of file diff --git a/main/index.js b/main/index.js index 3ddfdd5..2f2b71f 100644 --- a/main/index.js +++ b/main/index.js @@ -1,6 +1,8 @@ const express = require('express'); const path = require('path'); +const csurf = require('csurf'); +const { csrf } = require('../shared/middleware'); const middleware = require('./middleware'); const routers = require('./routers'); @@ -13,6 +15,8 @@ main.on('mount', server => { main.locals = Object.assign(server.locals, main.locals); }); +main.use(csurf(), csrf); + main.use((req, res, next) => { res.locals.user = req.user; @@ -21,7 +25,8 @@ main.use((req, res, next) => { main.use('/', routers.main); main.use('/auth', routers.auth); -main.use('/user', middleware.auth.allowAuthenticated, routers.user); +main.use(middleware.auth.allowAuthenticated); +main.use('/user', routers.user); main.use('/books', routers.book); main.use('/topics', routers.topic); main.use('/cart', routers.cart); diff --git a/main/views/auth/login.pug b/main/views/auth/login.pug index d7b13b2..566d0bf 100644 --- a/main/views/auth/login.pug +++ b/main/views/auth/login.pug @@ -5,6 +5,8 @@ block header block content main#main form(id='login-form' class='auth-form' method='POST' action='/auth/login').mdc-card + input(type='hidden' name='_csrf' value=csrfToken) + header.mdc-card__primary h1.mdc-card__title.mdc-card__title--large= title diff --git a/main/views/auth/register.pug b/main/views/auth/register.pug index 30910f1..e830983 100644 --- a/main/views/auth/register.pug +++ b/main/views/auth/register.pug @@ -5,6 +5,8 @@ block header block content main#main form(id='register-form' class='auth-form' method='POST' action='/auth/register').mdc-card + input(type='hidden' name='_csrf' value=csrfToken) + header.mdc-card__primary h1.mdc-card__title.mdc-card__title--large= title diff --git a/package-lock.json b/package-lock.json index 9bbd8e7..45d70b4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -113,6 +113,19 @@ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" }, + "amp": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/amp/-/amp-0.3.1.tgz", + "integrity": "sha1-at+NWKdPNh6CwfqNOJwHnhOfxH0=" + }, + "amp-message": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/amp-message/-/amp-message-0.1.2.tgz", + "integrity": "sha1-p48cmJlQh602GSpBKY5NtJ49/EU=", + "requires": { + "amp": "0.3.1" + } + }, "ansi-align": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", @@ -145,8 +158,7 @@ "ansi-styles": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, "ansi-wrap": { "version": "0.1.0", @@ -193,7 +205,6 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", - "dev": true, "requires": { "sprintf-js": "1.0.3" } @@ -210,14 +221,12 @@ "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" }, "arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" }, "array-differ": { "version": "1.0.0", @@ -294,8 +303,7 @@ "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, "async": { "version": "2.1.4", @@ -308,8 +316,7 @@ "async-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "dev": true + "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=" }, "async-foreach": { "version": "0.1.3", @@ -317,6 +324,15 @@ "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", "dev": true }, + "async-listener": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.9.tgz", + "integrity": "sha512-E7Z2/QMs0EPt/o9wpYO/J3hmMCDdr1aVDS3ttlur5D5JlZtxhfuOwi4e7S8zbYIxA5qOOYdxfqGj97XAfdNvkQ==", + "requires": { + "semver": "5.4.1", + "shimmer": "1.2.0" + } + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -325,8 +341,7 @@ "atob": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/atob/-/atob-2.0.3.tgz", - "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=", - "dev": true + "integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=" }, "autoprefixer": { "version": "7.2.3", @@ -397,7 +412,6 @@ "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, "requires": { "cache-base": "1.0.1", "class-utils": "0.3.5", @@ -411,8 +425,7 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, @@ -463,8 +476,12 @@ "binary-extensions": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", - "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", - "dev": true + "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=" + }, + "blessed": { + "version": "0.1.81", + "resolved": "https://registry.npmjs.org/blessed/-/blessed-0.1.81.tgz", + "integrity": "sha1-+WLWh+wsNpVwrnGvhDJW5tDKESk=" }, "block-stream": { "version": "0.0.9", @@ -502,6 +519,13 @@ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } } } @@ -590,7 +614,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, "requires": { "collection-visit": "1.0.0", "component-emitter": "1.2.1", @@ -606,8 +629,7 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, @@ -650,6 +672,11 @@ } } }, + "camelize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", + "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" + }, "caniuse-lite": { "version": "1.0.30000784", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000784.tgz", @@ -722,6 +749,11 @@ "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", "dev": true }, + "charm": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/charm/-/charm-0.1.2.tgz", + "integrity": "sha1-BsIe7RobBq62dVPNxT4jJ0usIpY=" + }, "chokidar": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", @@ -748,7 +780,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.5.tgz", "integrity": "sha1-F+eTEDdQ+WJ7IXbqNM/RtWWQPIA=", - "dev": true, "requires": { "arr-union": "3.1.0", "define-property": "0.2.5", @@ -761,7 +792,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -770,7 +800,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -780,20 +809,17 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" }, "lazy-cache": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", - "dev": true, "requires": { "set-getter": "0.1.0" } @@ -824,6 +850,36 @@ "restore-cursor": "2.0.0" } }, + "cli-table-redemption": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cli-table-redemption/-/cli-table-redemption-1.0.1.tgz", + "integrity": "sha512-SjVCciRyx01I4azo2K2rcc0NP/wOceXGzG1ZpYkEulbbIxDA/5YWv0oxG2HtQ4v8zPC6bgbRI7SbNaTZCxMNkg==", + "requires": { + "chalk": "1.1.3" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + } + } + }, "cli-width": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", @@ -872,7 +928,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, "requires": { "map-visit": "1.0.0", "object-visit": "1.0.1" @@ -924,8 +979,7 @@ "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" }, "concat-map": { "version": "0.0.1", @@ -957,6 +1011,11 @@ "xdg-basedir": "3.0.0" } }, + "connect-flash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", + "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA=" + }, "connect-mongo": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/connect-mongo/-/connect-mongo-2.0.1.tgz", @@ -1026,11 +1085,25 @@ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" }, + "content-security-policy-builder": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-2.0.0.tgz", + "integrity": "sha512-j+Nhmj1yfZAikJLImCvPJFE29x/UuBi+/MWqggGGc515JKaZrjuei2RhULJmy0MsstW3E3htl002bwmBNMKr7w==" + }, "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, + "continuation-local-storage": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz", + "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==", + "requires": { + "async-listener": "0.6.9", + "emitter-listener": "1.1.1" + } + }, "cookie": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", @@ -1044,8 +1117,7 @@ "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" }, "core-util-is": { "version": "1.0.2", @@ -1081,6 +1153,14 @@ "capture-stack-trace": "1.0.0" } }, + "cron": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/cron/-/cron-1.3.0.tgz", + "integrity": "sha512-K/SF7JlgMmNjcThWxkKvsHhey2EDB4CeOEWJ9aXWj3fbQJppsvTPIeyLdHfNq5IbbsMUUjRW1nr5dSO95f2E4w==", + "requires": { + "moment-timezone": "0.5.14" + } + }, "cross-spawn": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", @@ -1107,6 +1187,54 @@ "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", "dev": true }, + "csrf": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/csrf/-/csrf-3.0.6.tgz", + "integrity": "sha1-thEg3c7q/JHnbtUxO7XAsmZ7cQo=", + "requires": { + "rndm": "1.2.0", + "tsscmp": "1.0.5", + "uid-safe": "2.1.4" + }, + "dependencies": { + "uid-safe": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.4.tgz", + "integrity": "sha1-Otbzg2jG1MjHXsF2I/t5qh0HHYE=", + "requires": { + "random-bytes": "1.0.0" + } + } + } + }, + "csurf": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/csurf/-/csurf-1.9.0.tgz", + "integrity": "sha1-SdLGkl/87Ht95VlZfBU/pTM2QTM=", + "requires": { + "cookie": "0.3.1", + "cookie-signature": "1.0.6", + "csrf": "3.0.6", + "http-errors": "1.5.1" + }, + "dependencies": { + "http-errors": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.5.1.tgz", + "integrity": "sha1-eIwNLB3iyBuebowBhDtrl+uSB1A=", + "requires": { + "inherits": "2.0.3", + "setprototypeof": "1.0.2", + "statuses": "1.3.1" + } + }, + "setprototypeof": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.2.tgz", + "integrity": "sha1-gaVSFB7BBLiOic44MQOtXGZWTQg=" + } + } + }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -1131,6 +1259,11 @@ } } }, + "dasherize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz", + "integrity": "sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg=" + }, "dateformat": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", @@ -1141,9 +1274,15 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, "requires": { "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "decamelize": { @@ -1154,8 +1293,7 @@ "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, "deep-extend": { "version": "0.4.2", @@ -1168,6 +1306,14 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "deep-metrics": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/deep-metrics/-/deep-metrics-0.0.1.tgz", + "integrity": "sha512-732WmZgCWxOkf4QBvrCjPPuT6wTEzaGye/4JqYsU/sO0J53UNX4PBwK0JV262BZ5cxgLmKhU+NlrtKdPDgybkg==", + "requires": { + "semver": "5.4.1" + } + }, "defaults": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", @@ -1180,7 +1326,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, "requires": { "is-descriptor": "1.0.1" } @@ -1232,6 +1377,11 @@ "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", "dev": true }, + "dns-prefetch-control": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/dns-prefetch-control/-/dns-prefetch-control-0.1.0.tgz", + "integrity": "sha1-YN20V3dOF48flBXwyrsOhbCzALI=" + }, "doctrine": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.2.tgz", @@ -1246,6 +1396,11 @@ "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz", "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=" }, + "dont-sniff-mimetype": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dont-sniff-mimetype/-/dont-sniff-mimetype-1.0.0.tgz", + "integrity": "sha1-WTKJDcn04vGeXrAqIAJuXl78j1g=" + }, "dot-prop": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", @@ -1365,6 +1520,14 @@ "electron-releases": "2.1.0" } }, + "emitter-listener": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.1.tgz", + "integrity": "sha1-6Lu+gkS8jg0LTvcc0UKUx/JBx+w=", + "requires": { + "shimmer": "1.2.0" + } + }, "encodeurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", @@ -1409,11 +1572,15 @@ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, + "escape-regexp": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/escape-regexp/-/escape-regexp-0.0.1.tgz", + "integrity": "sha1-9EvaEtRbvfnLf4Yu5+SCez3TIlQ=" + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { "version": "4.13.1", @@ -1537,6 +1704,11 @@ "through": "2.3.8" } }, + "eventemitter2": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-1.0.5.tgz", + "integrity": "sha1-+YNhBRexc3wLncZDvsqTiTwE3xg=" + }, "eventemitter3": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.2.0.tgz", @@ -1585,6 +1757,11 @@ "homedir-polyfill": "1.0.1" } }, + "expect-ct": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/expect-ct/-/expect-ct-0.1.0.tgz", + "integrity": "sha1-UnNWeN4YUwiQ2Ne5XwrGNkCVgJQ=" + }, "express": { "version": "4.16.2", "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", @@ -1628,6 +1805,13 @@ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } } } @@ -1662,6 +1846,13 @@ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } } } @@ -1675,7 +1866,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, "requires": { "is-extendable": "0.1.1" } @@ -1732,6 +1922,11 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fclone": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fclone/-/fclone-1.0.11.tgz", + "integrity": "sha1-EOhdo4v+p/xZk0HClu4ddyZu5kA=" + }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -1790,6 +1985,13 @@ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } } } @@ -1860,6 +2062,14 @@ "dev": true, "requires": { "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "expand-brackets": { @@ -2053,8 +2263,7 @@ "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" }, "for-own": { "version": "0.1.5", @@ -2090,11 +2299,15 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, "requires": { "map-cache": "0.2.2" } }, + "frameguard": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/frameguard/-/frameguard-3.0.0.tgz", + "integrity": "sha1-e8rUae57lukdEs6zlZx4I1qScuk=" + }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -2231,8 +2444,7 @@ "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" }, "getpass": { "version": "0.1.7", @@ -2249,6 +2461,11 @@ } } }, + "gkt": { + "version": "https://tgz.pm2.io/gkt-1.0.0.tgz", + "integrity": "sha1-QFUCsAfzGcP0cXXER0UnMA8qta0=", + "optional": true + }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -2738,7 +2955,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, "requires": { "ansi-regex": "2.1.1" } @@ -2767,7 +2983,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, "requires": { "get-value": "2.0.6", "has-values": "1.0.0", @@ -2777,8 +2992,7 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, @@ -2786,7 +3000,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, "requires": { "is-number": "3.0.0", "kind-of": "4.0.0" @@ -2796,7 +3009,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, "requires": { "kind-of": "3.2.2" }, @@ -2805,7 +3017,6 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -2816,7 +3027,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, "requires": { "is-buffer": "1.1.6" } @@ -2841,6 +3051,42 @@ "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "dev": true }, + "helmet": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.11.0.tgz", + "integrity": "sha512-Xqf6VXmjpZoyH4reGyeBCO5nHH0NVeRQnx23LFj6AK9ocPRgZJfSH6zZ8SvNO2tB+fKhsqy1RSjIjWHVvH1X+w==", + "requires": { + "dns-prefetch-control": "0.1.0", + "dont-sniff-mimetype": "1.0.0", + "expect-ct": "0.1.0", + "frameguard": "3.0.0", + "helmet-csp": "2.7.0", + "hide-powered-by": "1.0.0", + "hpkp": "2.0.0", + "hsts": "2.1.0", + "ienoopen": "1.0.0", + "nocache": "2.0.0", + "referrer-policy": "1.1.0", + "x-xss-protection": "1.0.0" + } + }, + "helmet-csp": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.7.0.tgz", + "integrity": "sha512-IGIAkWnxjRbgMXFA2/kmDqSIrIaSfZ6vhMHlSHw7jm7Gm9nVVXqwJ2B1YEpYrJsLrqY+w2Bbimk7snux9+sZAw==", + "requires": { + "camelize": "1.0.0", + "content-security-policy-builder": "2.0.0", + "dasherize": "2.0.0", + "lodash.reduce": "4.6.0", + "platform": "1.3.5" + } + }, + "hide-powered-by": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.0.0.tgz", + "integrity": "sha1-SoWtZYgfYoV/xwr3F0oRhNzM4ys=" + }, "hoek": { "version": "2.16.3", "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", @@ -2861,6 +3107,16 @@ "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg==", "dev": true }, + "hpkp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hpkp/-/hpkp-2.0.0.tgz", + "integrity": "sha1-EOFCJk52IVpdMMROxD3mTe5tFnI=" + }, + "hsts": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.1.0.tgz", + "integrity": "sha512-zXhh/DqgrTXJ7erTN6Fh5k/xjMhDGXCqdYN3wvxUvGUQvnxcFfUd8E+6vLg/nk3ss1TYMb+DhRl25fYABioTvA==" + }, "http-errors": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", @@ -2921,6 +3177,11 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" }, + "ienoopen": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ienoopen/-/ienoopen-1.0.0.tgz", + "integrity": "sha1-NGpCj0dKrI9QzzeE6i0PFvYr2ms=" + }, "ignore": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.7.tgz", @@ -3004,8 +3265,7 @@ "interpret": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", - "dev": true + "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=" }, "invert-kv": { "version": "1.0.0", @@ -3018,6 +3278,11 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=" }, + "is": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is/-/is-3.2.1.tgz", + "integrity": "sha1-0Kwq1V63sL7JJqUmb2xmKqqD3KU=" + }, "is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", @@ -3032,7 +3297,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, "requires": { "kind-of": "3.2.2" } @@ -3047,7 +3311,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, "requires": { "binary-extensions": "1.11.0" } @@ -3070,7 +3333,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, "requires": { "kind-of": "3.2.2" } @@ -3079,7 +3341,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.1.tgz", "integrity": "sha512-G3fFVFTqfaqu7r4YuSBHKBAuOaLz8Sy7ekklUpFEliaLMP1Y2ZjoN9jS62YWCAPQrQpMUQSitRlrzibbuCZjdA==", - "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -3089,8 +3350,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" } } }, @@ -3128,8 +3388,7 @@ "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" }, "is-extglob": { "version": "1.0.0", @@ -3252,7 +3511,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, "requires": { "isobject": "3.0.1" }, @@ -3260,8 +3518,7 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, @@ -3464,7 +3721,7 @@ "joi": "6.10.1", "jws": "3.1.4", "lodash.once": "4.1.1", - "ms": "2.0.0", + "ms": "2.1.1", "xtend": "4.0.1" } }, @@ -3543,6 +3800,11 @@ "package-json": "4.0.1" } }, + "lazy": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/lazy/-/lazy-1.0.11.tgz", + "integrity": "sha1-2qBoIGKCVCwIgojpdcKXwa53tpA=" + }, "lazy-cache": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", @@ -3678,6 +3940,11 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", "dev": true }, + "lodash.endswith": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.endswith/-/lodash.endswith-4.2.1.tgz", + "integrity": "sha1-/tWawXOO0+I27dcGTsRWRIs3vAk=" + }, "lodash.escape": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", @@ -3687,6 +3954,11 @@ "lodash._root": "3.0.1" } }, + "lodash.findindex": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.findindex/-/lodash.findindex-4.6.0.tgz", + "integrity": "sha1-oyRd7mH7m24GJLU1ElYku2nBEQY=" + }, "lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", @@ -3704,6 +3976,21 @@ "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", "dev": true }, + "lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + }, + "lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + }, "lodash.keys": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", @@ -3715,6 +4002,11 @@ "lodash.isarray": "3.0.4" } }, + "lodash.merge": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", + "integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==" + }, "lodash.mergewith": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz", @@ -3726,12 +4018,22 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, + "lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" + }, "lodash.restparam": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", "dev": true }, + "lodash.startswith": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.startswith/-/lodash.startswith-4.2.1.tgz", + "integrity": "sha1-xZjErc4YiiflMUVzHNxsDnF3YAw=" + }, "lodash.template": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", @@ -3819,8 +4121,7 @@ "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" }, "map-obj": { "version": "1.0.1", @@ -3838,7 +4139,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, "requires": { "object-visit": "1.0.1" } @@ -4274,7 +4574,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.0.tgz", "integrity": "sha512-dgaCvoh6i1nosAUBKb0l0pfJ78K8+S9fluyIR2YvAeUD/QuMahnFnF3xYty5eYXMjhGSsB0DsW6A0uAZyetoAg==", - "dev": true, "requires": { "for-in": "1.0.2", "is-extendable": "1.0.1" @@ -4284,7 +4583,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, "requires": { "is-plain-object": "2.0.4" } @@ -4304,6 +4602,14 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz", "integrity": "sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg==" }, + "moment-timezone": { + "version": "0.5.14", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.14.tgz", + "integrity": "sha1-TrOP+VOLgBCLpGekWPPtQmjM/LE=", + "requires": { + "moment": "2.20.1" + } + }, "mongodb": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.0.1.tgz", @@ -4337,6 +4643,13 @@ "ms": "2.0.0", "regexp-clone": "0.0.1", "sliced": "1.0.1" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "mongoose-legacy-pluralize": { @@ -4362,6 +4675,13 @@ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } } } @@ -4388,6 +4708,13 @@ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "sliced": { @@ -4398,9 +4725,9 @@ } }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, "multipipe": { "version": "0.1.2", @@ -4414,8 +4741,7 @@ "mute-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" }, "nan": { "version": "2.8.0", @@ -4474,11 +4800,43 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "needle": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.0.tgz", + "integrity": "sha512-eFagy6c+TYayorXw/qtAdSvaUpEbBsDwDyxYFgLZ0lTojfH7K+OdBqAF7TAFwDokJaGpubpSGG0wO3iC0XPi8w==", + "requires": { + "debug": "2.6.9", + "iconv-lite": "0.4.19", + "sax": "1.2.4" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + } + } + }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, + "nocache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.0.0.tgz", + "integrity": "sha1-ICtIAhoMTL3i34DeFaF0Q8i0OYA=" + }, "node-gyp": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.6.2.tgz", @@ -4779,6 +5137,14 @@ "dev": true, "requires": { "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } } } @@ -4808,7 +5174,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, "requires": { "remove-trailing-separator": "1.1.0" } @@ -4844,8 +5209,24 @@ "set-blocking": "2.0.0" } }, - "num2fraction": { - "version": "1.2.2", + "nssocket": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/nssocket/-/nssocket-0.6.0.tgz", + "integrity": "sha1-Wflvb/MhVm8zxw99vu7N/cBxVPo=", + "requires": { + "eventemitter2": "0.4.14", + "lazy": "1.0.11" + }, + "dependencies": { + "eventemitter2": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=" + } + } + }, + "num2fraction": { + "version": "1.2.2", "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", "dev": true @@ -4874,7 +5255,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, "requires": { "copy-descriptor": "0.1.1", "define-property": "0.2.5", @@ -4885,7 +5265,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -4894,7 +5273,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -4904,8 +5282,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" } } } @@ -4915,7 +5292,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, "requires": { "isobject": "3.0.1" }, @@ -4923,8 +5299,7 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, @@ -4992,7 +5367,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, "requires": { "isobject": "3.0.1" }, @@ -5000,8 +5374,7 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, @@ -5182,8 +5555,7 @@ "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" }, "passport": { "version": "0.4.0", @@ -5235,6 +5607,11 @@ "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, "path-exists": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", @@ -5316,6 +5693,11 @@ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, + "pidusage": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-1.2.0.tgz", + "integrity": "sha512-OGo+iSOk44HRJ8q15AyG570UYxcm5u+R99DI8Khu8P3tKGkVu5EZX4ywHglWSTMNNXQ274oeGpYrvFEhDIFGPg==" + }, "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", @@ -5337,12 +5719,550 @@ "pinkie": "2.0.4" } }, + "platform": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz", + "integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q==" + }, "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", "dev": true }, + "pm2": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/pm2/-/pm2-2.10.1.tgz", + "integrity": "sha512-l5U3Hh908TMw7qyOVAlCCoRlgonpnEw5pr2YnKT8+8fuay9tAysPTccU+SLPDjI6hQ9Q03p4lfNOtJUczzTrPA==", + "requires": { + "async": "2.6.0", + "blessed": "0.1.81", + "chalk": "1.1.3", + "chokidar": "2.0.2", + "cli-table-redemption": "1.0.1", + "commander": "2.13.0", + "cron": "1.3.0", + "debug": "3.1.0", + "eventemitter2": "1.0.5", + "fclone": "1.0.11", + "gkt": "https://tgz.pm2.io/gkt-1.0.0.tgz", + "mkdirp": "0.5.1", + "moment": "2.20.1", + "needle": "2.2.0", + "nssocket": "0.6.0", + "pidusage": "1.2.0", + "pm2-axon": "3.1.0", + "pm2-axon-rpc": "0.5.0", + "pm2-deploy": "0.3.9", + "pm2-multimeter": "0.1.2", + "pmx": "1.6.4", + "promptly": "2.2.0", + "semver": "5.4.1", + "shelljs": "0.7.8", + "source-map-support": "0.5.3", + "sprintf-js": "1.1.1", + "v8-compile-cache": "1.1.2", + "vizion": "0.2.13", + "yamljs": "0.3.0" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "requires": { + "micromatch": "3.1.8", + "normalize-path": "2.1.1" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "async": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", + "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", + "requires": { + "lodash": "4.17.4" + } + }, + "braces": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.1.tgz", + "integrity": "sha512-SO5lYHA3vO6gz66erVvedSCkp7AKWdv6VcQ2N4ysXfPxdAlxAMMAdwegGGcv1Bqwm7naF1hNdk5d6AAIEHV2nQ==", + "requires": { + "arr-flatten": "1.1.0", + "array-unique": "0.3.2", + "define-property": "1.0.0", + "extend-shallow": "2.0.1", + "fill-range": "4.0.0", + "isobject": "3.0.1", + "kind-of": "6.0.2", + "repeat-element": "1.1.2", + "snapdragon": "0.8.1", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "1.0.1" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "chokidar": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.2.tgz", + "integrity": "sha512-l32Hw3wqB0L2kGVmSbK/a+xXLDrUEsc84pSgMkmwygHvD7ubRsP/vxxHa5BtB6oix1XLLVCHyYMsckRXxThmZw==", + "requires": { + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.1", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.0", + "normalize-path": "2.1.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0", + "upath": "1.0.2" + } + }, + "commander": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==" + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "1.0.2", + "isobject": "3.0.1" + }, + "dependencies": { + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "0.1.6" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "0.1.1" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "0.3.2", + "define-property": "1.0.0", + "expand-brackets": "2.1.4", + "extend-shallow": "2.0.1", + "fragment-cache": "0.2.1", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "1.0.1" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "3.1.0", + "path-dirname": "1.0.2" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "2.1.1" + } + } + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "6.0.2" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "6.0.2" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "requires": { + "is-extglob": "2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "3.2.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "1.1.6" + } + } + } + }, + "is-odd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-odd/-/is-odd-2.0.0.tgz", + "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", + "requires": { + "is-number": "4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==" + } + } + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" + }, + "micromatch": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.8.tgz", + "integrity": "sha512-/XeuOQqYg+B5kwjDWekXseSwGS7CzE0w9Gjo4Cjkf/uFitNh47NrZHAY2vp/oS2YQVfebPIdbEIvgdy+kIcAog==", + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.1", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "extglob": "2.0.4", + "fragment-cache": "0.2.1", + "kind-of": "6.0.2", + "nanomatch": "1.2.9", + "object.pick": "1.3.0", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + } + }, + "nanomatch": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.9.tgz", + "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "fragment-cache": "0.2.1", + "is-odd": "2.0.0", + "is-windows": "1.0.2", + "kind-of": "6.0.2", + "object.pick": "1.3.0", + "regex-not": "1.0.0", + "snapdragon": "0.8.1", + "to-regex": "3.0.1" + } + }, + "sprintf-js": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz", + "integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=" + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + } + } + }, + "pm2-axon": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pm2-axon/-/pm2-axon-3.1.0.tgz", + "integrity": "sha512-5sBM+vHw0Cp2K9CJ9ZOYhKtNCCcgQ0eKOyFrSo5Jusbq9FfvuelsMG4WDaxkqosaQbf8N5YfyHhD7eOUcnm5rQ==", + "requires": { + "amp": "0.3.1", + "amp-message": "0.1.2", + "debug": "3.1.0", + "escape-regexp": "0.0.1" + } + }, + "pm2-axon-rpc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/pm2-axon-rpc/-/pm2-axon-rpc-0.5.0.tgz", + "integrity": "sha512-jKiAlnIitx+TtJ1++jThmN49gM0Dte4gm27Kqu2xAUQn33Rh9+5lOOqShS5Xbp0RPZL42hKNEgaVVOSqm3sJCg==", + "requires": { + "debug": "3.1.0", + "fclone": "1.0.11" + } + }, + "pm2-deploy": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/pm2-deploy/-/pm2-deploy-0.3.9.tgz", + "integrity": "sha512-IYF45fPwfLE27BivrtodK7zzN56BNDErK7brcldIHjVIHLlk+cdhijq3kwTkPPP3Tpc3H2C942QGRgjg0hHajA==", + "requires": { + "async": "1.5.2", + "tv4": "1.3.0" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + } + } + }, + "pm2-multimeter": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/pm2-multimeter/-/pm2-multimeter-0.1.2.tgz", + "integrity": "sha1-Gh5VFT1BoFU0zqI8/oYKuqDrSs4=", + "requires": { + "charm": "0.1.2" + } + }, + "pmx": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/pmx/-/pmx-1.6.4.tgz", + "integrity": "sha512-uk6REZHe8j3RhGlFUfyVwFrE6JFhZigTpMFs/4iYO4MzCqVTpNp1cED032oCc4R+m32wUITV/2RDmPX21T1LLg==", + "requires": { + "debug": "3.1.0", + "deep-metrics": "0.0.1", + "json-stringify-safe": "5.0.1", + "semver": "5.4.1", + "vxx": "1.2.2" + } + }, "portfinder": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", @@ -5367,6 +6287,14 @@ "dev": true, "requires": { "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } } } @@ -5374,8 +6302,7 @@ "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, "postcss": { "version": "6.0.14", @@ -5454,6 +6381,14 @@ "asap": "2.0.6" } }, + "promptly": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/promptly/-/promptly-2.2.0.tgz", + "integrity": "sha1-KhP6BjaIoqWYOxYf/wEIoH0m/HQ=", + "requires": { + "read": "1.0.7" + } + }, "proxy-addr": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", @@ -5707,6 +6642,14 @@ } } }, + "read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", + "requires": { + "mute-stream": "0.0.7" + } + }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -5746,7 +6689,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", - "dev": true, "requires": { "graceful-fs": "4.1.11", "minimatch": "3.0.4", @@ -5758,7 +6700,6 @@ "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, "requires": { "resolve": "1.5.0" } @@ -5773,6 +6714,11 @@ "strip-indent": "1.0.1" } }, + "referrer-policy": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.1.0.tgz", + "integrity": "sha1-NXdOtzW/UPtsB46DM0tHI1AgfXk=" + }, "regex-cache": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", @@ -5786,7 +6732,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.0.tgz", "integrity": "sha1-Qvg+OXcWIt+CawKvF2Ul1qXxV/k=", - "dev": true, "requires": { "extend-shallow": "2.0.1" } @@ -5818,14 +6763,12 @@ "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" }, "repeat-element": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", - "dev": true + "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=" }, "repeat-string": { "version": "1.6.1", @@ -5954,8 +6897,7 @@ "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" }, "restore-cursor": { "version": "2.0.0", @@ -5983,6 +6925,11 @@ "glob": "7.1.2" } }, + "rndm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/rndm/-/rndm-1.2.0.tgz", + "integrity": "sha1-8z/pz7Urv9UgqhgyO8ZdsRCht2w=" + }, "run-async": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", @@ -6093,6 +7040,11 @@ } } }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, "scss-tokenizer": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", @@ -6144,6 +7096,11 @@ "requires": { "ms": "2.0.0" } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, @@ -6163,6 +7120,13 @@ "ms": "2.0.0", "parseurl": "1.3.2", "safe-buffer": "5.1.1" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "serve-static": { @@ -6185,7 +7149,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", - "dev": true, "requires": { "to-object-path": "0.3.0" } @@ -6193,14 +7156,12 @@ "set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" }, "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", - "dev": true, "requires": { "extend-shallow": "2.0.1", "is-extendable": "0.1.1", @@ -6228,6 +7189,21 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "shelljs": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", + "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", + "requires": { + "glob": "7.1.2", + "interpret": "1.1.0", + "rechoir": "0.6.2" + } + }, + "shimmer": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.0.tgz", + "integrity": "sha512-xTCx2vohXC2EWWDqY/zb4+5Mu28D+HYNSOuFzsyRDRvI/e1ICb69afwaUwfjr+25ZXldbOLyp+iDUZHq8UnTag==" + }, "sigmund": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", @@ -6257,7 +7233,6 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.1.tgz", "integrity": "sha1-4StUh/re0+PeoKyR6UAL91tAE3A=", - "dev": true, "requires": { "base": "0.11.2", "debug": "2.6.9", @@ -6273,16 +7248,21 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } }, "define-property": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -6291,7 +7271,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -6301,14 +7280,12 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" } } }, @@ -6316,7 +7293,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, "requires": { "define-property": "1.0.0", "isobject": "3.0.1", @@ -6326,8 +7302,7 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, @@ -6335,7 +7310,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, "requires": { "kind-of": "3.2.2" } @@ -6361,7 +7335,6 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz", "integrity": "sha512-0KW2wvzfxm8NCTb30z0LMNyPqWCdDGE2viwzUaucqJdkTRXtZiSY3I+2A6nVAjmdOy0I4gU8DwnVVGsk9jvP2A==", - "dev": true, "requires": { "atob": "2.0.3", "decode-uri-component": "0.2.0", @@ -6370,11 +7343,25 @@ "urix": "0.1.0" } }, + "source-map-support": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.3.tgz", + "integrity": "sha512-eKkTgWYeBOQqFGXRfKabMFdnWepo51vWqEdoeikaEPFiJC7MCU5j2h4+6Q8npkZTeLGbSyecZvRxiSoWl3rh+w==", + "requires": { + "source-map": "0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" }, "sparkles": { "version": "1.0.0", @@ -6416,7 +7403,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, "requires": { "extend-shallow": "3.0.2" }, @@ -6425,7 +7411,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, "requires": { "assign-symbols": "1.0.0", "is-extendable": "1.0.1" @@ -6435,7 +7420,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, "requires": { "is-plain-object": "2.0.4" } @@ -6445,8 +7429,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.13.1", @@ -6474,7 +7457,6 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, "requires": { "define-property": "0.2.5", "object-copy": "0.1.0" @@ -6484,7 +7466,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -6493,7 +7474,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -6503,8 +7483,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" } } }, @@ -6610,8 +7589,7 @@ "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" }, "tabbable": { "version": "1.1.2", @@ -6663,6 +7641,13 @@ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } } } } @@ -6732,7 +7717,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, "requires": { "kind-of": "3.2.2" } @@ -6741,7 +7725,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.1.tgz", "integrity": "sha1-FTWL7kosg712N3uh3ASdDxiDeq4=", - "dev": true, "requires": { "define-property": "0.2.5", "extend-shallow": "2.0.1", @@ -6752,7 +7735,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -6761,7 +7743,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -6771,8 +7752,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" } } }, @@ -6780,7 +7760,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, "requires": { "is-number": "3.0.0", "repeat-string": "1.6.1" @@ -6790,7 +7769,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, "requires": { "kind-of": "3.2.2" } @@ -6857,12 +7835,22 @@ } } }, + "tsscmp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.5.tgz", + "integrity": "sha1-fcSjOvcVgatDN9qR2FylQn69mpc=" + }, "tunnel-agent": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", "dev": true }, + "tv4": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tv4/-/tv4-1.3.0.tgz", + "integrity": "sha1-0CDIRvrdUMhVq7JeuuzGj8EPeWM=" + }, "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", @@ -6967,7 +7955,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", - "dev": true, "requires": { "arr-union": "3.1.0", "get-value": "2.0.6", @@ -6979,7 +7966,6 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, "requires": { "extend-shallow": "2.0.1", "is-extendable": "0.1.1", @@ -7013,7 +7999,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, "requires": { "has-value": "0.3.1", "isobject": "3.0.1" @@ -7023,7 +8008,6 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, "requires": { "get-value": "2.0.6", "has-values": "0.1.4", @@ -7034,7 +8018,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, "requires": { "isarray": "1.0.0" } @@ -7044,14 +8027,12 @@ "has-values": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, @@ -7061,6 +8042,17 @@ "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", "dev": true }, + "upath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.0.2.tgz", + "integrity": "sha512-fCmij7T5LnwUme3dbnVSejvOHHlARjB3ikJFwgZfz386pHmf/gueuTLRFU94FZEaeCLlbQrweiUU700gG41tUw==", + "requires": { + "lodash.endswith": "4.2.1", + "lodash.isfunction": "3.0.9", + "lodash.isstring": "4.0.1", + "lodash.startswith": "4.2.1" + } + }, "update-notifier": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.3.0.tgz", @@ -7081,8 +8073,7 @@ "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" }, "url-join": { "version": "2.0.5", @@ -7103,7 +8094,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/use/-/use-2.0.2.tgz", "integrity": "sha1-riig1y+TvyJCKhii43mZMRLeyOg=", - "dev": true, "requires": { "define-property": "0.2.5", "isobject": "3.0.1", @@ -7114,7 +8104,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "0.1.6" } @@ -7123,7 +8112,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, "requires": { "is-accessor-descriptor": "0.1.6", "is-data-descriptor": "0.1.4", @@ -7133,20 +8121,17 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" }, "lazy-cache": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", - "dev": true, "requires": { "set-getter": "0.1.0" } @@ -7174,6 +8159,11 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" }, + "v8-compile-cache": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-1.1.2.tgz", + "integrity": "sha512-ejdrifsIydN1XDH7EuR2hn8ZrkRKUYF7tUcBjBy/lhrCvs2K+zRlbW9UHc0IQ9RsYFZJFqJrieoIHfkCa0DBRA==" + }, "v8flags": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-2.1.1.tgz", @@ -7320,11 +8310,61 @@ } } }, + "vizion": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/vizion/-/vizion-0.2.13.tgz", + "integrity": "sha1-ExTN7is0EW+fWxJIU2+V2/zW718=", + "requires": { + "async": "1.5.2" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + } + } + }, "void-elements": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=" }, + "vxx": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/vxx/-/vxx-1.2.2.tgz", + "integrity": "sha1-dB+1HG8R0zg9pvm5IBil17qAdhE=", + "requires": { + "continuation-local-storage": "3.2.1", + "debug": "2.6.9", + "extend": "3.0.1", + "is": "3.2.1", + "lodash.findindex": "4.6.0", + "lodash.isequal": "4.5.0", + "lodash.merge": "4.6.1", + "methods": "1.1.2", + "semver": "5.4.1", + "shimmer": "1.2.0", + "uuid": "3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + } + } + }, "which": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", @@ -7478,6 +8518,11 @@ "signal-exit": "3.0.2" } }, + "x-xss-protection": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.0.0.tgz", + "integrity": "sha1-iYr7k4abJGYc+cUvnujbjtB2Tdk=" + }, "xdg-basedir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", @@ -7501,6 +8546,15 @@ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", "dev": true }, + "yamljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", + "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==", + "requires": { + "argparse": "1.0.9", + "glob": "7.1.2" + } + }, "yargs": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", diff --git a/package.json b/package.json index 9361c20..61a0285 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,11 @@ "private": true, "main": "index.js", "scripts": { - "start": "node index", + "start": "pm2 start index.js -i max", "dev": "nodemon -r dotenv/config index.js", "sass": "gulp watch:sass", - "lint": "eslint" + "lint": "eslint", + "stop": "pm2 stop all" }, "devDependencies": { "eslint": "^4.13.1", @@ -21,22 +22,27 @@ "dependencies": { "apicache": "^1.2.0", "bcrypt": "^1.0.3", + "connect-flash": "^0.1.1", "connect-mongo": "^2.0.1", "cors": "^2.8.4", + "csurf": "^1.9.0", "dotenv": "^4.0.0", "express": "^4.16.2", "express-rate-limit": "^2.11.0", "express-session": "^1.15.6", + "helmet": "^3.11.0", "jwt-simple": "^0.5.1", "material-components-web": "^0.28.0", "moment": "^2.20.1", "mongoose": "^5.0.1", "morgan": "^1.9.0", + "ms": "^2.1.1", "normalize.css": "^7.0.0", "passport": "^0.4.0", "passport-github": "^1.1.0", "passport-jwt": "^3.0.1", "passport-local": "^1.0.0", + "pm2": "^2.10.1", "pug": "^2.0.0-rc.4", "serve-favicon": "^2.4.5" } diff --git a/server.js b/server.js new file mode 100644 index 0000000..9f74490 --- /dev/null +++ b/server.js @@ -0,0 +1,73 @@ +const express = require('express'); +const logger = require('morgan'); +const favicon = require('serve-favicon'); +const session = require('express-session'); +const flash = require('connect-flash'); +const MongoStore = require('connect-mongo')(session); +const helmet = require('helmet'); + +const data = require('./shared/data'); +const config = require('./shared/config'); +const services = require('./shared/services'); +const middleware = require('./shared/middleware'); + +const main = require('./main'); +const admin = require('./admin'); +const api = require('./api'); + +const server = express(); + +server.set('view engine', 'pug'); +server.set('views', config.paths.views); +server.set('config', config); +server.set('port', config.port); + +server.locals.basedir = config.paths.views; +server.locals.VERSION = config.version; +server.locals.LANGUAGES = data.languages; + +server.use(helmet({ + frameguard: { action: 'deny' }, + hsts: false +})); + +//server.use(middleware.https()); + +server.use(express.static(config.paths.public)); +server.use('/lib', express.static(config.paths.lib)); +server.use(favicon(config.paths.favicon)); +server.use(express.urlencoded({ extended: false })); +server.use(express.json()); +server.use(logger('dev')); +server.use(session({ + name: 'sessionId', + secret: config.sessionSecret, + resave: false, + saveUninitialized: false, + cookie: { + httpOnly: true, + //secure: true, + signed: true, + maxAge: 1000 * 60 * 60 * 24 * 3 // 3 days + }, + store: new MongoStore({ + mongooseConnection: services.db.connection, + ttl: 60 * 60 * 24 * 3, // 3 days + touchAfter: 60 * 60 * 24 // 1 day + }) +})); + +server.use(services.passport.initialize()); +server.use(services.passport.session()); +server.use(flash()); +server.use(middleware.flash); +server.use(middleware.cart); + +server.use('/', main); +server.use('/admin', admin); +server.use('/api', api); + +server.use(middleware.error.notFound); +server.use(server.get('env') === 'development' ? middleware.error.development : middleware.error.production); + +module.exports = server; \ No newline at end of file diff --git a/shared/config/index.js b/shared/config/index.js index 3b9f286..f791be7 100644 --- a/shared/config/index.js +++ b/shared/config/index.js @@ -1,17 +1,15 @@ const path = require('path'); +const env = process.env.NODE_ENV; const ROOT_PATH = path.resolve(__dirname, '..', '..'); module.exports = { version: process.env.APP_VERSION, - env: process.env.NODE_ENV, - port: process.env.PORT || 3000, + env, + port: process.env.PORT || 3001, sessionSecret: 'HacJmB3ma6crKKtK', jwtSecret: '8UG^LmgL!!N#42vq', - mongodbUri: { - local: 'mongodb://localhost:27017/codedojo', - mlab: process.env.MONGODB_MLAB_URL - }, + mongodbUri: env === 'development' ? 'mongodb://localhost:27017/codedojo' : process.env.MONGODB_URL, paths: { views: path.join(ROOT_PATH, 'shared', 'views'), public: path.join(ROOT_PATH, 'shared', 'public'), diff --git a/shared/middleware/csrf.js b/shared/middleware/csrf.js new file mode 100644 index 0000000..c9c7055 --- /dev/null +++ b/shared/middleware/csrf.js @@ -0,0 +1,7 @@ +module.exports = function csrf(req, res, next) { + let csrfToken = req.csrfToken(); + + res.locals.csrfToken = csrfToken; + + next(); +}; \ No newline at end of file diff --git a/shared/middleware/error.js b/shared/middleware/error.js index 69611c4..a972555 100644 --- a/shared/middleware/error.js +++ b/shared/middleware/error.js @@ -5,6 +5,14 @@ module.exports = { next(new NotFoundError()); }, + csrf(err, req, res, next) { + if (err.code !== 'EBADCSRFTOKEN') return next(err); + + // handle CSRF token errors here + res.status(403); + res.send('form tampered with'); + }, + development(error, req, res, next) { console.error(error); diff --git a/shared/middleware/https.js b/shared/middleware/https.js new file mode 100644 index 0000000..26bf23c --- /dev/null +++ b/shared/middleware/https.js @@ -0,0 +1,11 @@ +module.exports = function ensureHttps() { + return (req, res, next) => { + if (req.secure) next(); + else { + if (req.method === 'GET') + res.redirect(301, `https://${req.headers.host}${req.originalUrl}`); + else + res.status(403).send('Для доступа к сайту необходимо использовать защищенное соединение через HTTPS.'); + } + }; +}; \ No newline at end of file diff --git a/shared/middleware/index.js b/shared/middleware/index.js index 8949fbe..31b4510 100644 --- a/shared/middleware/index.js +++ b/shared/middleware/index.js @@ -1,5 +1,7 @@ module.exports = { + csrf: require('./csrf'), cart: require('./cart'), error: require('./error'), - flash: require('./flash') + flash: require('./flash'), + https: require('./https') }; \ No newline at end of file diff --git a/shared/middleware/www.js b/shared/middleware/www.js new file mode 100644 index 0000000..ef90e82 --- /dev/null +++ b/shared/middleware/www.js @@ -0,0 +1,11 @@ +module.exports = function ensureNoWww() { + return (req, res, next) => { + let host = req.hostname; + + if (host.match(/^www\./) !== null) { + res.redirect(301, req.protocol + '://' + host.replace(/^www\./, '') + req.originalUrl); + } else { + next(); + } + }; +}; \ No newline at end of file