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/README.md b/README.md index 8249961..3203cf2 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` - вход +* Профиль пользователя + * `/user` - страница пользователя + * `/user/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/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..172a2eb 100644 --- a/admin/controllers/home.js +++ b/admin/controllers/home.js @@ -1,5 +1,7 @@ module.exports = { showIndexPage(req, res) { - + res.render('index', { + id: 'admin' + }); } }; \ 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/config/index.js b/config/index.js deleted file mode 100644 index 14e9050..0000000 --- a/config/index.js +++ /dev/null @@ -1,17 +0,0 @@ -const path = require('path'); - -module.exports = { - version: process.env.APP_VERSION, - env: process.env.NODE_ENV, - port: process.env.PORT || 3000, - sessionSecret: 'HacJmB3ma6crKKtK', - mongodbUri: { - development: 'mongodb://localhost:27017/codedojo' - }, - paths: { - views: path.resolve(__dirname, '..', 'views'), - public: path.resolve(__dirname, '..', 'public'), - favicon: path.resolve(__dirname, '..', 'public', 'favicon.ico'), - lib: path.resolve(__dirname, '..', 'node_modules') - } -}; \ No newline at end of file diff --git a/controllers/auth.js b/controllers/auth.js deleted file mode 100644 index 91208ef..0000000 --- a/controllers/auth.js +++ /dev/null @@ -1,65 +0,0 @@ -const { user: User } = require('../models'); - -module.exports = { - // GET /auth/register - showRegisterPage(req, res) { - res.render('auth/register', { - id: 'register', - className: 'auth-page', - title: 'Регистрация' - }); - }, - - // GET /auth/login - showLoginPage(req, res) { - res.render('auth/login', { - id: 'login', - className: 'auth-page', - title: 'Вход' - }); - }, - - // 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); - }, - - // 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); - }, - - // GET /auth/logout - logout(req, res, next) { - if (req.session) { - req.session.destroy(error => { - if (error) return next(error); - - res.redirect('/'); - }); - } - } -}; \ No newline at end of file diff --git a/controllers/search.js b/controllers/search.js deleted file mode 100644 index db32917..0000000 --- a/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/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 55a0f35..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 db = require('./services/db'); -const config = require('./config'); -const { error, auth } = require('./middleware'); -const routers = require('./routers'); -const admin = require('./admin'); - -const app = express(); - -app.set('view engine', 'pug'); -app.set('views', config.paths.views); -app.set('config', config); - -app.locals.version = config.version; -app.locals.basedir = config.paths.views; - -app.use(express.static(config.paths.public)); -app.use('/lib', express.static(config.paths.lib)); -app.use(express.urlencoded({ extended: false })); - -app.use(favicon(config.paths.favicon)); -app.use(logger('dev')); -app.use(express.urlencoded({ extended: false })); -app.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: db.connection, - ttl: 60 * 60 * 24 * 3, // 3 days - touchAfter: 60 * 60 * 24 // 1 day - }) -})); - -app.use(auth.findUser); - -app.use('/', routers.main); -app.use('/auth', routers.auth); - -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); - -app.use(error.notFound); -app.use(app.get('env') === 'development' ? error.development : error.production); - -app.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/controllers/auth.js b/main/controllers/auth.js new file mode 100644 index 0000000..c864deb --- /dev/null +++ b/main/controllers/auth.js @@ -0,0 +1,44 @@ +const { passport } = require('../../shared/services'); + +module.exports = { + // GET /auth/register + showRegisterPage(req, res) { + res.render('auth/register', { + id: 'register', + className: 'auth-page', + title: 'Регистрация' + }); + }, + + // GET /auth/login + showLoginPage(req, res) { + res.render('auth/login', { + id: 'login', + className: 'auth-page', + title: 'Вход' + }); + }, + + // POST /auth/register + register: passport.authenticate('local-register', { + failureRedirect: '/auth/register', + successRedirect: '/user' + }), + + // POST /auth/login + login: passport.authenticate('local-login', { + failureRedirect: '/auth/login', + successRedirect: '/user' + }), + + // GET /auth/logout + logout(req, res, next) { + if (req.session) { + req.session.destroy(error => { + if (error) return next(error); + + res.redirect('/'); + }); + } + } +}; \ No newline at end of file diff --git a/controllers/book.js b/main/controllers/book.js similarity index 71% rename from controllers/book.js rename to main/controllers/book.js index bd3f632..f929bd2 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) { @@ -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/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/controllers/index.js b/main/controllers/index.js new file mode 100644 index 0000000..deeeb7c --- /dev/null +++ b/main/controllers/index.js @@ -0,0 +1,8 @@ +module.exports = { + auth: require('./auth'), + book: require('./book'), + cart: require('./cart'), + main: require('./main'), + oauth: require('./oauth'), + 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/main/controllers/oauth.js b/main/controllers/oauth.js new file mode 100644 index 0000000..6cdd9ff --- /dev/null +++ b/main/controllers/oauth.js @@ -0,0 +1,11 @@ +const { passport } = require('../../shared/services'); + +module.exports = { + github: { + authenticate: passport.authenticate('github'), + callback: passport.authenticate('github', { + failureRedirect: '/auth/login', + successRedirect: '/user' + }) + } +}; \ No newline at end of file diff --git a/controllers/topic.js b/main/controllers/topic.js similarity index 52% rename from controllers/topic.js rename to main/controllers/topic.js index 074bd3a..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) { @@ -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/main/index.js b/main/index.js new file mode 100644 index 0000000..2f2b71f --- /dev/null +++ b/main/index.js @@ -0,0 +1,34 @@ +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'); + +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(csurf(), csrf); + +main.use((req, res, next) => { + res.locals.user = req.user; + + next(); +}); + +main.use('/', routers.main); +main.use('/auth', routers.auth); +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); + +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..20fe157 --- /dev/null +++ b/main/middleware/auth.js @@ -0,0 +1,15 @@ +module.exports = { + allowAuthenticated(req, res, next) { + if (req.isAuthenticated()) return next(); + + req.flash('error', 'Для продолжения необходимо войти или зарегистрироваться'); + + 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/main/routers/auth.js b/main/routers/auth.js new file mode 100644 index 0000000..8a354b1 --- /dev/null +++ b/main/routers/auth.js @@ -0,0 +1,24 @@ +const router = require('express').Router(); + +const { auth: authMiddleware } = require('../middleware'); +const { + auth: authController, + oauth: oauthController +} = require('../controllers'); + +router.route('/register') + .all(authMiddleware.allowUnauthenticated) + .get(authController.showRegisterPage) + .post(authController.register); + +router.route('/login') + .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', 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 89% rename from routers/book.js rename to main/routers/book.js index 3b10d19..d83f88d 100644 --- a/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/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 new file mode 100644 index 0000000..cdaa7e9 --- /dev/null +++ b/main/routers/index.js @@ -0,0 +1,8 @@ +module.exports = { + auth: require('./auth'), + book: require('./book'), + cart: require('./cart'), + main: require('./main'), + 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/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 76% rename from routers/profile.js rename to main/routers/user.js index 3ed96c1..43ff37f 100644 --- a/routers/profile.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 new file mode 100644 index 0000000..5d71bc5 --- /dev/null +++ b/main/views/_includes/search-form.pug @@ -0,0 +1,3 @@ +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/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..bb0fb31 --- /dev/null +++ b/main/views/_layout.pug @@ -0,0 +1,6 @@ +extends /_layout + +include /_mixins/mdc/index + +block header + include _includes/toolbar \ No newline at end of file diff --git a/views/_mixins/book-card.pug b/main/views/_mixins/book-card.pug similarity index 74% rename from views/_mixins/book-card.pug rename to main/views/_mixins/book-card.pug index 9bcb394..8b58a98 100644 --- a/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/auth/login.pug b/main/views/auth/login.pug new file mode 100644 index 0000000..566d0bf --- /dev/null +++ b/main/views/auth/login.pug @@ -0,0 +1,22 @@ +extends ../_layout + +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 + + 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 diff --git a/main/views/auth/register.pug b/main/views/auth/register.pug new file mode 100644 index 0000000..e830983 --- /dev/null +++ b/main/views/auth/register.pug @@ -0,0 +1,23 @@ +extends ../_layout + +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 + + 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 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/views/books/book.pug b/main/views/books/book.pug similarity index 97% rename from views/books/book.pug rename to main/views/books/book.pug index 4584c57..928e472 100644 --- a/views/books/book.pug +++ b/main/views/books/book.pug @@ -1,10 +1,10 @@ -extends ../_layout +extends _layout 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/views/books/index.pug b/main/views/books/index.pug similarity index 94% rename from views/books/index.pug rename to main/views/books/index.pug index 3c4b956..ee02a81 100644 --- a/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/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/main/views/index.pug b/main/views/index.pug new file mode 100644 index 0000000..1998a08 --- /dev/null +++ b/main/views/index.pug @@ -0,0 +1,13 @@ +extends _layout + +block header + +block content + 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/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/middleware/auth.js b/middleware/auth.js deleted file mode 100644 index 61bc81c..0000000 --- a/middleware/auth.js +++ /dev/null @@ -1,30 +0,0 @@ -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(); - - 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/error.js b/middleware/error.js deleted file mode 100644 index 922db5d..0000000 --- a/middleware/error.js +++ /dev/null @@ -1,26 +0,0 @@ -module.exports = { - notFound(req, res, next) { - let error = new Error('Не найдено'); - error.status = 404; - - next(error); - }, - - development(error, req, res, next) { - console.error(error); - - res.render('error', { - id: 'error', - title: 'Ошибка', - error - }); - }, - - production(error, req, res, next) { - res.render('error', { - id: 'error', - title: 'Ошибка', - message: error.message - }); - } -}; \ 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 ba65e42..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", @@ -164,6 +176,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", @@ -188,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" } @@ -205,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", @@ -289,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", @@ -303,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", @@ -312,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", @@ -320,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", @@ -392,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", @@ -406,11 +425,15 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" } } }, + "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", @@ -453,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", @@ -492,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=" + } } } } @@ -555,6 +589,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", @@ -575,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", @@ -591,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=" } } }, @@ -635,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", @@ -707,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", @@ -733,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", @@ -746,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" } @@ -755,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", @@ -765,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" } @@ -809,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", @@ -835,8 +906,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", @@ -858,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" @@ -885,6 +954,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", @@ -904,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", @@ -937,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", @@ -1006,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", @@ -1024,14 +1117,28 @@ "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", "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", @@ -1046,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", @@ -1072,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", @@ -1096,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", @@ -1106,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": { @@ -1119,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", @@ -1133,11 +1306,18 @@ "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", "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", - "dev": true, "requires": { "clone": "1.0.3" } @@ -1146,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" } @@ -1198,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", @@ -1212,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", @@ -1282,6 +1471,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", @@ -1302,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", @@ -1346,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", @@ -1474,6 +1704,17 @@ "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", + "integrity": "sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=", + "dev": true + }, "execa": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", @@ -1516,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", @@ -1559,10 +1805,25 @@ "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=" + } } } } }, + "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", @@ -1585,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=" + } } } } @@ -1598,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" } @@ -1655,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", @@ -1713,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=" + } } } } @@ -1783,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": { @@ -1976,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", @@ -2013,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", @@ -2154,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", @@ -2172,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", @@ -2661,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" } @@ -2690,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", @@ -2700,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=" } } }, @@ -2709,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" @@ -2719,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" }, @@ -2728,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" } @@ -2739,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" } @@ -2758,11 +3045,52 @@ "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 + }, + "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", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" }, "homedir-polyfill": { "version": "1.0.1", @@ -2779,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", @@ -2797,6 +3135,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", @@ -2813,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", @@ -2896,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", @@ -2910,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", @@ -2924,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" } @@ -2939,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" } @@ -2962,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" } @@ -2971,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", @@ -2981,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==" } } }, @@ -3020,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", @@ -3144,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" }, @@ -3152,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=" } } }, @@ -3252,6 +3617,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 +3642,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 +3713,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.1.1", + "xtend": "4.0.1" + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -3359,11 +3752,37 @@ "promise": "7.3.1" } }, - "kareem": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.0.1.tgz", - "integrity": "sha512-SsR+TZe595qXYzbWS5KWHBt4mM5h1MA7HFXp3oZnPkunxjaymx0fKhB8cxl6/R7Qm8aFXnI6J7DnyxV/QUSKLA==" - }, + "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", + "integrity": "sha512-SsR+TZe595qXYzbWS5KWHBt4mM5h1MA7HFXp3oZnPkunxjaymx0fKhB8cxl6/R7Qm8aFXnI6J7DnyxV/QUSKLA==" + }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", @@ -3381,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", @@ -3516,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", @@ -3525,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", @@ -3542,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", @@ -3553,18 +4002,38 @@ "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", "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.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", @@ -3652,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", @@ -3671,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" } @@ -4107,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" @@ -4117,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" } @@ -4132,6 +4597,19 @@ "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==" + }, + "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", @@ -4165,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": { @@ -4190,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=" + } } } } @@ -4216,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": { @@ -4226,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", @@ -4242,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", @@ -4302,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", @@ -4607,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 + } } } } @@ -4636,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" } @@ -4672,6 +5209,22 @@ "set-blocking": "2.0.0" } }, + "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", @@ -4683,6 +5236,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", @@ -4697,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", @@ -4708,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" } @@ -4717,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", @@ -4727,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==" } } } @@ -4738,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" }, @@ -4746,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=" } } }, @@ -4815,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" }, @@ -4823,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=" } } }, @@ -4858,6 +5408,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", @@ -4981,8 +5555,62 @@ "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", + "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-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", + "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-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", @@ -5024,74 +5652,657 @@ "path-root-regex": "0.1.2" } }, - "path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "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", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "dev": true, + "requires": { + "through": "2.3.8" + } + }, + "performance-now": { + "version": "2.1.0", + "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", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "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": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "amp": "0.3.1", + "amp-message": "0.1.2", + "debug": "3.1.0", + "escape-regexp": "0.0.1" } }, - "pause-stream": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", - "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", - "dev": true, + "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": { - "through": "2.3.8" + "debug": "3.1.0", + "fclone": "1.0.11" } }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + "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=" + } + } }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "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" + } }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true + "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" + } }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "portfinder": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.13.tgz", + "integrity": "sha1-uzLs2HwnEErm7kS1o8y/Drsa7ek=", "dev": true, "requires": { - "pinkie": "2.0.4" + "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" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + } } }, - "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 - }, "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", @@ -5170,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", @@ -5423,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", @@ -5462,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", @@ -5474,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" } @@ -5489,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", @@ -5502,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" } @@ -5534,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", @@ -5637,6 +6864,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", @@ -5664,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", @@ -5693,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", @@ -5803,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", @@ -5854,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=" } } }, @@ -5873,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": { @@ -5895,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" } @@ -5903,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", @@ -5938,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", @@ -5967,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", @@ -5983,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" } @@ -6001,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", @@ -6011,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=" } } }, @@ -6026,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", @@ -6036,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=" } } }, @@ -6045,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" } @@ -6071,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", @@ -6080,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", @@ -6126,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" }, @@ -6135,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" @@ -6145,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" } @@ -6155,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", @@ -6184,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" @@ -6194,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" } @@ -6203,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", @@ -6213,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==" } } }, @@ -6320,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", @@ -6373,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=" + } } } } @@ -6442,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" } @@ -6451,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", @@ -6462,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" } @@ -6471,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", @@ -6481,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==" } } }, @@ -6490,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" @@ -6500,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" } @@ -6512,6 +7780,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", @@ -6559,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", @@ -6631,6 +7917,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", @@ -6643,11 +7934,27 @@ "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", "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", - "dev": true, "requires": { "arr-union": "3.1.0", "get-value": "2.0.6", @@ -6659,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", @@ -6693,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" @@ -6703,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", @@ -6714,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" } @@ -6724,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=" } } }, @@ -6741,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", @@ -6761,7 +8073,12 @@ "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, + "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": { @@ -6777,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", @@ -6788,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" } @@ -6797,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", @@ -6807,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" } @@ -6848,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", @@ -6994,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", @@ -7152,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", @@ -7161,8 +8532,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", @@ -7176,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 e43846a..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", @@ -15,18 +16,33 @@ "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-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/public/css/app.css b/public/css/app.css deleted file mode 100644 index fea5f74..0000000 --- a/public/css/app.css +++ /dev/null @@ -1,430 +0,0 @@ -/*! 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); } - -#content { - display: -webkit-box; - display: -ms-flexbox; - display: flex; } - -.book-card { - background-color: white; } - .book-card .mdc-card__action { - min-width: 36px; } - .book-card .mdc-card__action .mdc-button__icon:only-child { - margin-right: 0; } - .book-card .mdc-card__action:last-child { - margin-left: auto; } - -.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; } - .auth-page .auth-form { - width: 100%; - max-width: 400px; } - .auth-page .auth-form .mdc-text-field { - display: 100%; - max-width: 200px; - display: block; } diff --git a/routers/auth.js b/routers/auth.js deleted file mode 100644 index 24d4ccf..0000000 --- a/routers/auth.js +++ /dev/null @@ -1,18 +0,0 @@ -const router = require('express').Router(); - -const { auth: { authenticated, unauthenticated } } = require('../middleware'); -const { auth: controller } = require('../controllers'); - -router.route('/register') - .all(unauthenticated) - .get(controller.showRegisterPage) - .post(controller.register); - -router.route('/login') - .all(unauthenticated) - .get(controller.showLoginPage) - .post(controller.login); - -router.get('/logout', authenticated, controller.logout); - -module.exports = router; \ No newline at end of file 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/routers/search.js b/routers/search.js deleted file mode 100644 index dd6ca31..0000000 --- a/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/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 new file mode 100644 index 0000000..f791be7 --- /dev/null +++ b/shared/config/index.js @@ -0,0 +1,26 @@ +const path = require('path'); + +const env = process.env.NODE_ENV; +const ROOT_PATH = path.resolve(__dirname, '..', '..'); + +module.exports = { + version: process.env.APP_VERSION, + env, + port: process.env.PORT || 3001, + sessionSecret: 'HacJmB3ma6crKKtK', + jwtSecret: '8UG^LmgL!!N#42vq', + 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'), + favicon: path.join(ROOT_PATH, 'shared', 'public', 'favicon.ico'), + lib: path.join(ROOT_PATH, '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/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/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/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 new file mode 100644 index 0000000..a972555 --- /dev/null +++ b/shared/middleware/error.js @@ -0,0 +1,33 @@ +const { NotFoundError } = require('../utils/error'); + +module.exports = { + notFound(req, res, next) { + 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); + + res.status(error.status).render('error', { + id: 'error', + title: 'Ошибка', + error + }); + }, + + production(error, req, res, next) { + res.status(error.status).render('error', { + id: 'error', + title: 'Ошибка', + message: error.message + }); + } +}; \ No newline at end of file diff --git a/shared/middleware/flash.js b/shared/middleware/flash.js new file mode 100644 index 0000000..38438ef --- /dev/null +++ b/shared/middleware/flash.js @@ -0,0 +1,5 @@ +module.exports = function flash(req, res, next) { + res.locals.errors = req.flash('error'); + + next(); +}; \ No newline at end of file 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 new file mode 100644 index 0000000..31b4510 --- /dev/null +++ b/shared/middleware/index.js @@ -0,0 +1,7 @@ +module.exports = { + csrf: require('./csrf'), + cart: require('./cart'), + error: require('./error'), + 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 diff --git a/models/book.js b/shared/models/book.js similarity index 64% rename from models/book.js rename to shared/models/book.js index 1c72da2..4830f8d 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,25 +10,37 @@ 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, imageUrl: String, + price: { type: Number, default: 1000 }, codeUrl: String, 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/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 new file mode 100644 index 0000000..ed77e1c --- /dev/null +++ b/shared/models/index.js @@ -0,0 +1,7 @@ +module.exports = { + Book: require('./book'), + Cart: require('./cart'), + 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 63% rename from models/user.js rename to shared/models/user.js index 65263d3..2cb0cf5 100644 --- a/models/user.js +++ b/shared/models/user.js @@ -11,11 +11,24 @@ 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: { type: String, unique: true, sparse: true }, + 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'; +}); + User.pre('save', function(next) { if (!this.isModified('password')) return next(); @@ -35,26 +48,8 @@ User.post('save', function(error, user, next) { } }); -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; - }); - }); +User.methods.isCorrectPassword = function(password) { + return bcrypt.compare(password, this.password); }; module.exports = mongoose.model('User', User); \ No newline at end of file diff --git a/shared/public/css/app.css b/shared/public/css/app.css new file mode 100644 index 0000000..bcf15f4 --- /dev/null +++ b/shared/public/css/app.css @@ -0,0 +1,93 @@ +body { + background-color: rgba(0, 0, 0, 0.05); } + +.mdc-card { + background-color: white; } + +#content { + display: -webkit-box; + display: -ms-flexbox; + display: flex; } + +.book-card { + background-color: white; } + .book-card .mdc-card__action { + min-width: 36px; } + .book-card .mdc-card__action .mdc-button__icon:only-child { + margin-right: 0; } + .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 { + 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; } + .auth-page .auth-form .mdc-text-field { + 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; } + +#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/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/shared/public/img/bg.jpg b/shared/public/img/bg.jpg new file mode 100644 index 0000000..799b45f Binary files /dev/null and b/shared/public/img/bg.jpg differ 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 93% rename from services/db.js rename to shared/services/db.js index 7879a15..4b1cf6a 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.development); +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/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/shared/services/passport/github.js b/shared/services/passport/github.js new file mode 100644 index 0000000..7dcf3f5 --- /dev/null +++ b/shared/services/passport/github.js @@ -0,0 +1,25 @@ +const passport = require('passport'); +const { Strategy: GitHubStrategy } = require('passport-github'); +const { User } = require('../../models'); + +const config = require('../../config'); + +passport.use(new GitHubStrategy(config.oauth.github, (accessToken, refreshToken, profile, done) => { + 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); +})); \ No newline at end of file diff --git a/shared/services/passport/index.js b/shared/services/passport/index.js new file mode 100644 index 0000000..a4a8bf3 --- /dev/null +++ b/shared/services/passport/index.js @@ -0,0 +1,16 @@ +const passport = require('passport'); + +const { User } = require('../../models'); +require('./local'); +require('./github'); +require('./jwt'); + +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/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/shared/services/passport/local.js b/shared/services/passport/local.js new file mode 100644 index 0000000..9008d02 --- /dev/null +++ b/shared/services/passport/local.js @@ -0,0 +1,34 @@ +const passport = require('passport'); +const { Strategy: LocalStrategy } = require('passport-local'); + +const { 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); + console.log(user) + done(null, user); + }) + .catch(done); + }) + .catch(done); +})); \ No newline at end of file diff --git a/styles/base.scss b/shared/styles/base.scss similarity index 53% rename from styles/base.scss rename to shared/styles/base.scss index 5f9c479..17bfcb3 100644 --- a/styles/base.scss +++ b/shared/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/shared/styles/components/_index.scss b/shared/styles/components/_index.scss new file mode 100644 index 0000000..176c642 --- /dev/null +++ b/shared/styles/components/_index.scss @@ -0,0 +1,3 @@ +@import 'book-card'; +@import 'flash'; +@import 'search-form'; \ No newline at end of file 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/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/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/shared/styles/pages/_index.scss b/shared/styles/pages/_index.scss new file mode 100644 index 0000000..22dfe57 --- /dev/null +++ b/shared/styles/pages/_index.scss @@ -0,0 +1,4 @@ +@import 'auth/index'; +@import 'error'; +@import 'main'; +@import 'user'; \ No newline at end of file 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 50% rename from styles/pages/auth/common.scss rename to shared/styles/pages/auth/common.scss index fb41315..e2929af 100644 --- a/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/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/shared/styles/pages/error.scss b/shared/styles/pages/error.scss new file mode 100644 index 0000000..561e426 --- /dev/null +++ b/shared/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/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/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 82% rename from views/_layout.pug rename to shared/views/_layout.pug index 2bdb7e9..c94cdc3 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}`) @@ -16,14 +17,12 @@ html(lang='ru') block head body(id=`${id}-page` class=className).mdc-typography - block header - include _includes/toolbar + if errors + for error in errors + div.flash.flash--error= error + block header + 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/shared/views/_mixins/mdc/index.pug b/shared/views/_mixins/mdc/index.pug new file mode 100644 index 0000000..e6f8654 --- /dev/null +++ b/shared/views/_mixins/mdc/index.pug @@ -0,0 +1,2 @@ +include button +include text-field \ No newline at end of file diff --git a/views/_mixins/mdc/textfield.pug b/shared/views/_mixins/mdc/text-field.pug similarity index 79% rename from views/_mixins/mdc/textfield.pug rename to shared/views/_mixins/mdc/text-field.pug index 3685298..077f99e 100644 --- a/views/_mixins/mdc/textfield.pug +++ b/shared/views/_mixins/mdc/text-field.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 97% rename from views/error.pug rename to shared/views/error.pug index 984cedb..d7fa8fa 100644 --- a/views/error.pug +++ b/shared/views/error.pug @@ -1,7 +1,5 @@ extends _layout -block header - block content main#main i.error-icon.material-icons error diff --git a/styles/components/_index.scss b/styles/components/_index.scss deleted file mode 100644 index 1c81bd4..0000000 --- a/styles/components/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'book-card'; \ No newline at end of file diff --git a/styles/pages/_index.scss b/styles/pages/_index.scss deleted file mode 100644 index 5764c53..0000000 --- a/styles/pages/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'auth/index'; \ No newline at end of file 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/_mixins/mdc/index.pug b/views/_mixins/mdc/index.pug deleted file mode 100644 index f718893..0000000 --- a/views/_mixins/mdc/index.pug +++ /dev/null @@ -1,2 +0,0 @@ -include button -include textfield \ No newline at end of file diff --git a/views/auth/login.pug b/views/auth/login.pug deleted file mode 100644 index 939669e..0000000 --- a/views/auth/login.pug +++ /dev/null @@ -1,21 +0,0 @@ -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 - - 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/register') Зарегистрироваться - - section.mdc-card__actions - a.mdc-button.mdc-card__action.mdc-button--dense(href='/auth/reset-password') Восстановить пароль \ No newline at end of file diff --git a/views/auth/register.pug b/views/auth/register.pug deleted file mode 100644 index 163a97d..0000000 --- a/views/auth/register.pug +++ /dev/null @@ -1,20 +0,0 @@ -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 - - section.mdc-card__supporting-text - div.mdc-text-field - input.mdc-text-field__input(type='email' name='email' placeholder='Электронная почта') - div.mdc-text-field - input.mdc-text-field__input(type='password' name='password' placeholder='Пароль') - div.mdc-text-field - input.mdc-text-field__input(type='password' name='confirmPassword' placeholder='Подтвердите пароль') - - 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/views/dev.pug b/views/dev.pug deleted file mode 100644 index e69de29..0000000 diff --git a/views/index.pug b/views/index.pug deleted file mode 100644 index 4196241..0000000 --- a/views/index.pug +++ /dev/null @@ -1,4 +0,0 @@ -extends _layout - -block content - h1 Главная страница \ No newline at end of file diff --git a/views/profile/edit.pug b/views/profile/edit.pug deleted file mode 100644 index e69de29..0000000 diff --git a/views/profile/index.pug b/views/profile/index.pug deleted file mode 100644 index 70245e8..0000000 --- a/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