diff --git a/config/passport.config.js b/config/passport.config.js new file mode 100644 index 0000000..d50607f --- /dev/null +++ b/config/passport.config.js @@ -0,0 +1,29 @@ +const LocalStrategy = require('passport-local').Strategy; +const bcrypt = require('bcryptjs'); + +function initialize(passport, getUserByEmail, getUserById) { + const authenticateUser = async (email, password, done) => { + const user = getUserByEmail(email); + if (user == null) { + return done(null, false, { message: 'Пользователь с такой почтой не найден' }); + } + + try { + if (await bcrypt.compare(password, user.password)) { + return done(null, user); + } else { + return done(null, false, { message: 'Неверный пароль' }); + } + } catch (error) { + return done(error); + } + } + + passport.use(new LocalStrategy({ usernameField: 'email' }, authenticateUser)); + passport.serializeUser((user, done) => done(null, user.id)); + passport.deserializeUser((id, done) => { + return done(null, getUserById(id)) + }) +} + +module.exports = initialize; \ No newline at end of file diff --git a/config/routes/admin.js b/config/routes/admin.js new file mode 100644 index 0000000..ea53e15 --- /dev/null +++ b/config/routes/admin.js @@ -0,0 +1,59 @@ +const bcrypt = require('bcryptjs'); +const passport = require('passport'); + +const initializePassport = require('../passport.config'); + +function checkNotAuthenticated(req, res, next) { + if (req.isAuthenticated()) { + return res.redirect('/control-panel'); + } + next(); +} + +function loginRoute(req, res) { + res.render('admin') + +} + +// function registerRoute(req, res) { +// res.render('register') +// } + +module.exports = function (server, db) { + server.get('/admin', checkNotAuthenticated, (req, res) => { + db.collection('administrators').find().toArray((err, result) => { + initializePassport( + passport, + email => result.find(user => user.email === email), + id => result.find(user => user.id === id) + ); + }); + loginRoute(req, res); + }); + // Отдача страницы с регистрацией + //server.get('/register', checkNotAuthenticated, (req, res) => registerRoute(req, res)); + server.post('/admin', checkNotAuthenticated, passport.authenticate('local', { + successRedirect: '/control-panel', + failureRedirect: '/admin', + failureFlash: true + })) + // Регистрация админа через форму POST + // server.post('/register', checkNotAuthenticated, async (req, res) => { + // try { + // const hashedPassword = await bcrypt.hash(req.body.password, 10); + // db.collection('administrators').insertOne({ + // id: Date.now().toString(), + // name: req.body.name, + // email: req.body.email, + // password: hashedPassword + // }); + // res.redirect('/admin'); + // } catch { + // res.redirect('/register'); + // } + // }); + server.delete('/logout', (req, res) => { + req.logOut(); + res.redirect('/admin'); + }) +} \ No newline at end of file diff --git a/config/routes/routes.js b/config/routes/routes.js index 783c995..a3bd065 100644 --- a/config/routes/routes.js +++ b/config/routes/routes.js @@ -1,5 +1,13 @@ const ObjectID = require('mongodb').ObjectID; +function checkAuthenticated(req, res, next) { + if (req.isAuthenticated()) { + return next(); + } + + res.redirect('/admin'); +} + function indexRoute(req, res, dbo) { dbo.collection('categories').find().toArray((err, result) => { const categories = result.map(category => category.title[0].toUpperCase() + category.title.slice(1)); @@ -99,8 +107,8 @@ function onePageItemRoute(req, res, dbo) { }) }); }) - } catch (error) { - res.send('Error 404'); + } catch { + res.render('404'); } } @@ -128,6 +136,10 @@ function myOrdersRoute(req, res) { }); } +function controlPanelRoute(req, res) { + res.render('control-panel', {name: req.user.name}); +} + module.exports = function (server, db) { server.get('/', (req, res) => indexRoute(req, res, db)); server.get('/api', (req, res) => getItemsRoute(req, res, db)); @@ -136,4 +148,5 @@ module.exports = function (server, db) { server.get('/comments', (req, res) => commentsRoute(req, res, db)); server.get('/my-orders', (req, res) => myOrdersRoute(req, res, db)); server.get('/product/:id', (req, res) => onePageItemRoute(req, res, db)); + server.get('/control-panel', checkAuthenticated, (req, res) => controlPanelRoute(req, res)); } diff --git a/config/server.js b/config/server.js index 1d5e36e..ba50526 100644 --- a/config/server.js +++ b/config/server.js @@ -1,8 +1,16 @@ +if (process.env.NODE_ENV !== 'production') { + require('dotenv').config(); +} + const MongoClient = require('mongodb').MongoClient; const db_config = require('./database'); const express = require('express'); const server = express(); const path = require('path'); +const passport = require('passport'); +const flash = require('express-flash'); +const session = require('express-session'); +const methodOverride = require('method-override'); MongoClient.connect(db_config.url, {useNewUrlParser: true, useUnifiedTopology: true}, (err, client) => { if (err) return console.log(err) @@ -10,14 +18,26 @@ MongoClient.connect(db_config.url, {useNewUrlParser: true, useUnifiedTopology: t // Config server.use(express.static(path.join(__dirname, '../dist/public'))); + server.use(express.urlencoded({ extended: false })); + server.use(flash()); + server.use(session({ + secret: process.env.SESSION_SECRET, + resave: false, + saveUninitialized: false + })); + server.use(passport.initialize()); + server.use(passport.session()); + server.use(methodOverride('_method')); + server.set('view engine', 'pug'); server.set('views', path.join(__dirname, '../dist/views')); // Routes require('./routes/routes')(server, db); + require('./routes/admin')(server, db); // 404 Redirect - server.use((req, res) => res.status(404).send('Error 404')); + server.use((req, res) => res.status(404).render('404')); server.listen(3000, () => console.log('Listen port 3000...')); }) diff --git a/dist/views/404.pug b/dist/views/404.pug new file mode 100644 index 0000000..c2c2c94 --- /dev/null +++ b/dist/views/404.pug @@ -0,0 +1,14 @@ +//- Head HTML +include ./assets/head.pug +//- Header Template +include ./assets/templates/header.pug + +main.main-section + include ./assets/templates/modals/cart.pug + .container + .row + .col.s12 + h1(style="margin-top: 5rem;font-size:70px;") Ошибка 404. + +//- Footer Template +include ./assets/templates/footer.pug \ No newline at end of file diff --git a/dist/views/admin.pug b/dist/views/admin.pug new file mode 100644 index 0000000..be15ff0 --- /dev/null +++ b/dist/views/admin.pug @@ -0,0 +1,12 @@ +if messages.error + h3 #{messages.error} +h1 Login Page +form(action="/admin" method="POST") + div + label(for="email") Email + input(type="email" id="email" name="email" required) + div + label(for="password") Password + input(type="password" id="password" name="password" required) + button(type="submit") Login +a(href="/register") Register \ No newline at end of file diff --git a/dist/views/control-panel.pug b/dist/views/control-panel.pug new file mode 100644 index 0000000..d0ee6ef --- /dev/null +++ b/dist/views/control-panel.pug @@ -0,0 +1,4 @@ +h1 Control-Panel +h3 Привет #{name} +form(action="/logout?_method=DELETE" method="POST") + button(type="submit") Выйти \ No newline at end of file diff --git a/dist/views/register.pug b/dist/views/register.pug new file mode 100644 index 0000000..5467ade --- /dev/null +++ b/dist/views/register.pug @@ -0,0 +1,13 @@ +h1 Register Form +form(action="/register" method="POST") + div + label(for="name") Name + input(type="text" id="name" name="name" required) + div + label(for="email") Email + input(type="email" id="email" name="email" required) + div + label(for="password") Password + input(type="password" id="password" name="password" required) + button(type="submit") Register +a(href="/admin") Login \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e5fc7a3..7cbbc80 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1388,6 +1388,11 @@ "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", "dev": true }, + "bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -2048,6 +2053,11 @@ } } }, + "connect-flash": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/connect-flash/-/connect-flash-0.1.1.tgz", + "integrity": "sha1-2GMPJtlaf4UfmVax6MxnMvO2qjA=" + }, "console-browserify": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", @@ -2359,6 +2369,12 @@ "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", "dev": true }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==", + "dev": true + }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -2636,6 +2652,54 @@ } } }, + "express-flash": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/express-flash/-/express-flash-0.0.2.tgz", + "integrity": "sha1-I9GovPP5DXB5KOSJ+Whp7K0KzaI=", + "requires": { + "connect-flash": "0.1.x" + } + }, + "express-session": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.0.tgz", + "integrity": "sha512-t4oX2z7uoSqATbMfsxWMbNjAL0T5zpvcJCk3Z9wnPPN7ibddhnmDZXHfEcoBMG2ojKXZoCyPMc5FbtK+G7SoDg==", + "requires": { + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.0", + "uid-safe": "~2.1.5" + }, + "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" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + } + } + }, "extend-shallow": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", @@ -4310,6 +4374,32 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, + "method-override": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", + "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", + "requires": { + "debug": "3.1.0", + "methods": "~1.1.2", + "parseurl": "~1.3.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -4688,6 +4778,11 @@ "ee-first": "1.1.1" } }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4814,6 +4909,28 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "passport": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.4.0.tgz", + "integrity": "sha1-xQlWkTR71a07XhgCOMORTRbwWBE=", + "requires": { + "passport-strategy": "1.x.x", + "pause": "0.0.1" + } + }, + "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.x.x" + } + }, + "passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" + }, "path-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", @@ -4854,6 +4971,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + }, "pbkdf2": { "version": "3.0.17", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", @@ -5110,6 +5232,11 @@ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", "dev": true }, + "random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -6164,6 +6291,14 @@ "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", "optional": true }, + "uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "requires": { + "random-bytes": "~1.0.0" + } + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", diff --git a/package.json b/package.json index 2cd070f..c0b595a 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,14 @@ "dependencies": { "@babel/polyfill": "^7.7.0", "axios": "^0.19.0", + "bcryptjs": "^2.4.3", "express": "^4.17.1", + "express-flash": "0.0.2", + "express-session": "^1.17.0", + "method-override": "^3.0.0", "mongodb": "^3.3.4", + "passport": "^0.4.0", + "passport-local": "^1.0.0", "pug": "^2.0.4" }, "devDependencies": { @@ -31,6 +37,7 @@ "@babel/preset-env": "^7.7.1", "babel-loader": "^8.0.6", "concurrently": "^5.0.0", + "dotenv": "^8.2.0", "webpack": "^4.41.2", "webpack-cli": "^3.3.10" }