mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-08-09 07:25:21 +02:00
Authorization added
This commit is contained in:
parent
9966131100
commit
4055f1148e
13 changed files with 228 additions and 30 deletions
|
@ -17,8 +17,10 @@
|
||||||
"codex.editor.header": "^2.0.5",
|
"codex.editor.header": "^2.0.5",
|
||||||
"cookie-parser": "~1.4.3",
|
"cookie-parser": "~1.4.3",
|
||||||
"debug": "~4.1.0",
|
"debug": "~4.1.0",
|
||||||
|
"dotenv": "^6.2.0",
|
||||||
"express": "~4.16.0",
|
"express": "~4.16.0",
|
||||||
"http-errors": "~1.7.1",
|
"http-errors": "~1.7.1",
|
||||||
|
"jsonwebtoken": "^8.4.0",
|
||||||
"module-dispatcher": "^1.0.2",
|
"module-dispatcher": "^1.0.2",
|
||||||
"morgan": "~1.9.0",
|
"morgan": "~1.9.0",
|
||||||
"multer": "^1.3.1",
|
"multer": "^1.3.1",
|
||||||
|
|
29
src/routes/auth.js
Normal file
29
src/routes/auth.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const { password: db } = require('../utils/database/index');
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
|
/* GET authorization page. */
|
||||||
|
router.get('/auth', function (req, res, next) {
|
||||||
|
res.render('auth', { title: 'Login page ', header: 'Enter password' });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/auth', async (req, res) => {
|
||||||
|
const passwordDoc = await db.findOne({password: req.body.password});
|
||||||
|
|
||||||
|
if (passwordDoc !== null) {
|
||||||
|
const token = jwt.sign({
|
||||||
|
'iss': 'Codex Team',
|
||||||
|
'sub': 'auth',
|
||||||
|
'iat': Date.now()
|
||||||
|
}, passwordDoc.password);
|
||||||
|
|
||||||
|
res.cookie('authToken', token);
|
||||||
|
|
||||||
|
res.redirect('/');
|
||||||
|
} else {
|
||||||
|
res.render('auth', { title: 'Login page', header: 'Wrong password!<br \\/>Try once more' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
|
@ -1,9 +1,21 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
|
const verifyToken = require('./middlewares/token');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
/* GET home page. */
|
/* GET home page. */
|
||||||
router.get('/', function (req, res, next) {
|
router.get('/', async function (req, res, next) {
|
||||||
res.render('index', { title: 'Express' });
|
let isAuthorized = false;
|
||||||
|
|
||||||
|
await verifyToken(req.cookies.authToken).then(
|
||||||
|
async () => {
|
||||||
|
console.log('Authorized user entered page');
|
||||||
|
isAuthorized = true;
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
console.log('Not authorized');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
res.render('index', { title: 'Express', isAuthorized: isAuthorized });
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
|
@ -3,12 +3,14 @@ const router = express.Router();
|
||||||
|
|
||||||
const home = require('./home');
|
const home = require('./home');
|
||||||
const pages = require('./pages');
|
const pages = require('./pages');
|
||||||
|
const auth = require('./auth');
|
||||||
const api = require('./api');
|
const api = require('./api');
|
||||||
|
|
||||||
const pagesMiddleware = require('./middlewares/pages');
|
const pagesMiddleware = require('./middlewares/pages');
|
||||||
|
|
||||||
router.use('/', pagesMiddleware, home);
|
router.use('/', pagesMiddleware, home);
|
||||||
router.use('/', pagesMiddleware, pages);
|
router.use('/', pagesMiddleware, pages);
|
||||||
|
router.use('/', pagesMiddleware, auth);
|
||||||
router.use('/api', api);
|
router.use('/api', api);
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|
14
src/routes/middlewares/token.js
Normal file
14
src/routes/middlewares/token.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
require('dotenv').config();
|
||||||
|
|
||||||
|
const jwt = require('jsonwebtoken');
|
||||||
|
|
||||||
|
module.exports = function verifyToken(token) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
jwt.verify(token, process.env.PASSWORD, (err, decodedToken) => {
|
||||||
|
if (err || !decodedToken) {
|
||||||
|
return reject(err);
|
||||||
|
}
|
||||||
|
resolve(decodedToken);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
|
@ -1,37 +1,53 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const Pages = require('../controllers/pages');
|
const Pages = require('../controllers/pages');
|
||||||
|
const verifyToken = require('./middlewares/token');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new page form
|
* Create new page form
|
||||||
*/
|
*/
|
||||||
router.get('/page/new', async (req, res) => {
|
router.get('/page/new', async (req, res) => {
|
||||||
let pagesAvailable = await Pages.getAll();
|
verifyToken(req.cookies.authToken).then(
|
||||||
|
async () => {
|
||||||
|
let pagesAvailable = await Pages.getAll();
|
||||||
|
|
||||||
res.render('pages/form', {
|
res.render('pages/form', {
|
||||||
pagesAvailable,
|
pagesAvailable,
|
||||||
page: null
|
page: null
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
res.render('auth', { title: 'Login page', header: 'Enter password to do this!' });
|
||||||
|
}
|
||||||
|
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edit page form
|
* Edit page form
|
||||||
*/
|
*/
|
||||||
router.get('/page/edit/:id', async (req, res, next) => {
|
router.get('/page/edit/:id', async (req, res, next) => {
|
||||||
const pageId = req.params.id;
|
verifyToken(req.cookies.authToken).then(
|
||||||
|
async () => {
|
||||||
|
const pageId = req.params.id;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let page = await Pages.get(pageId);
|
let page = await Pages.get(pageId);
|
||||||
let pagesAvailable = await Pages.getAll();
|
let pagesAvailable = await Pages.getAll();
|
||||||
|
|
||||||
res.render('pages/form', {
|
res.render('pages/form', {
|
||||||
pagesAvailable,
|
pagesAvailable,
|
||||||
page
|
page
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(404);
|
res.status(404);
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
res.render('auth', { title: 'Login page', header: 'Enter password to do this!' });
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,6 +55,17 @@ router.get('/page/edit/:id', async (req, res, next) => {
|
||||||
*/
|
*/
|
||||||
router.get('/page/:id', async (req, res, next) => {
|
router.get('/page/:id', async (req, res, next) => {
|
||||||
const pageId = req.params.id;
|
const pageId = req.params.id;
|
||||||
|
let isAuthorized = false;
|
||||||
|
|
||||||
|
await verifyToken(req.cookies.authToken).then(
|
||||||
|
async () => {
|
||||||
|
console.log('Authorized user entered page');
|
||||||
|
isAuthorized = true;
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
console.log('Not authorized');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let page = await Pages.get(pageId);
|
let page = await Pages.get(pageId);
|
||||||
|
@ -46,7 +73,7 @@ router.get('/page/:id', async (req, res, next) => {
|
||||||
let pageParent = await page.parent;
|
let pageParent = await page.parent;
|
||||||
|
|
||||||
res.render('pages/page', {
|
res.render('pages/page', {
|
||||||
page, pageParent
|
page, pageParent, isAuthorized
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(404);
|
res.status(404);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const pages = require('./pages');
|
const pages = require('./pages');
|
||||||
|
const password = require('./password');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Database
|
* @class Database
|
||||||
|
@ -142,5 +143,6 @@ class Database {
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
class: Database,
|
class: Database,
|
||||||
pages: new Database(pages)
|
pages: new Database(pages),
|
||||||
|
password: new Database(password)
|
||||||
};
|
};
|
||||||
|
|
6
src/utils/database/password.js
Normal file
6
src/utils/database/password.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
const Datastore = require('nedb');
|
||||||
|
const config = require('../../../config');
|
||||||
|
|
||||||
|
const db = new Datastore({filename: `./${config.database}/password.db`, autoload: true});
|
||||||
|
|
||||||
|
module.exports = db;
|
10
src/views/auth.twig
Normal file
10
src/views/auth.twig
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{% extends 'layout.twig' %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<h1>{{header}}</h1>
|
||||||
|
<form method="post" action="/auth">
|
||||||
|
<input type="password" name="password" placeholder="Password">
|
||||||
|
<input type="submit" value="login">
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
|
@ -4,10 +4,14 @@
|
||||||
</a>
|
</a>
|
||||||
<ul class="docs-header__menu">
|
<ul class="docs-header__menu">
|
||||||
<li>
|
<li>
|
||||||
<a class="docs-header__button" href="/page/new">
|
{% if isAuthorized == true %}
|
||||||
{{ svg('plus') }}
|
<a class="docs-header__button" href="/page/new">
|
||||||
Add Page
|
{{ svg('plus') }}
|
||||||
</a>
|
Add Page
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<a class="docs-header__button" href="/auth">Authorize</a>
|
||||||
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
{% for option in config.menu %}
|
{% for option in config.menu %}
|
||||||
<li>
|
<li>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<link rel="stylesheet" href="/dist/main.css" />
|
<link rel="stylesheet" href="/dist/main.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% include "components/header.twig" %}
|
{% include "components/header.twig" with isAuthorized %}
|
||||||
<div class="docs">
|
<div class="docs">
|
||||||
<aside class="docs__aside">
|
<aside class="docs__aside">
|
||||||
{% include "components/aside.twig" %}
|
{% include "components/aside.twig" %}
|
||||||
|
|
|
@ -13,9 +13,15 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<time class="page__header-time">
|
<time class="page__header-time">
|
||||||
Last edit {{ (page.body.time / 1000) | date("M d Y") }}
|
Last edit {{ (page.body.time / 1000) | date("M d Y") }}
|
||||||
<a href="/page/edit/{{ page._id }}" class="page__header-button">
|
{% if isAuthorized == true %}
|
||||||
Edit
|
<a href="/page/edit/{{ page._id }}" class="page__header-button">
|
||||||
</a>
|
Edit
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="/auth" class="page__header-button">
|
||||||
|
Authorize to edit
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
</time>
|
</time>
|
||||||
</header>
|
</header>
|
||||||
<h1 class="page__title">
|
<h1 class="page__title">
|
||||||
|
|
84
yarn.lock
84
yarn.lock
|
@ -1303,6 +1303,11 @@ browserslist@^4.0.0, browserslist@^4.0.1, browserslist@^4.3.4, browserslist@^4.3
|
||||||
electron-to-chromium "^1.3.86"
|
electron-to-chromium "^1.3.86"
|
||||||
node-releases "^1.0.5"
|
node-releases "^1.0.5"
|
||||||
|
|
||||||
|
buffer-equal-constant-time@1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
|
||||||
|
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
|
||||||
|
|
||||||
buffer-from@^1.0.0:
|
buffer-from@^1.0.0:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||||
|
@ -2324,6 +2329,11 @@ dot-prop@^4.1.0, dot-prop@^4.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-obj "^1.0.0"
|
is-obj "^1.0.0"
|
||||||
|
|
||||||
|
dotenv@^6.2.0:
|
||||||
|
version "6.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064"
|
||||||
|
integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==
|
||||||
|
|
||||||
duplexer3@^0.1.4:
|
duplexer3@^0.1.4:
|
||||||
version "0.1.4"
|
version "0.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
|
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
|
||||||
|
@ -2339,6 +2349,13 @@ duplexify@^3.4.2, duplexify@^3.6.0:
|
||||||
readable-stream "^2.0.0"
|
readable-stream "^2.0.0"
|
||||||
stream-shift "^1.0.0"
|
stream-shift "^1.0.0"
|
||||||
|
|
||||||
|
ecdsa-sig-formatter@1.0.10:
|
||||||
|
version "1.0.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz#1c595000f04a8897dfb85000892a0f4c33af86c3"
|
||||||
|
integrity sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=
|
||||||
|
dependencies:
|
||||||
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
ee-first@1.1.1:
|
ee-first@1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||||
|
@ -3880,11 +3897,43 @@ json5@^2.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
|
|
||||||
|
jsonwebtoken@^8.4.0:
|
||||||
|
version "8.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.4.0.tgz#8757f7b4cb7440d86d5e2f3becefa70536c8e46a"
|
||||||
|
integrity sha512-coyXjRTCy0pw5WYBpMvWOMN+Kjaik2MwTUIq9cna/W7NpO9E+iYbumZONAz3hcr+tXFJECoQVrtmIoC3Oz0gvg==
|
||||||
|
dependencies:
|
||||||
|
jws "^3.1.5"
|
||||||
|
lodash.includes "^4.3.0"
|
||||||
|
lodash.isboolean "^3.0.3"
|
||||||
|
lodash.isinteger "^4.0.4"
|
||||||
|
lodash.isnumber "^3.0.3"
|
||||||
|
lodash.isplainobject "^4.0.6"
|
||||||
|
lodash.isstring "^4.0.1"
|
||||||
|
lodash.once "^4.0.0"
|
||||||
|
ms "^2.1.1"
|
||||||
|
|
||||||
just-extend@^3.0.0:
|
just-extend@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-3.0.0.tgz#cee004031eaabf6406da03a7b84e4fe9d78ef288"
|
resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-3.0.0.tgz#cee004031eaabf6406da03a7b84e4fe9d78ef288"
|
||||||
integrity sha512-Fu3T6pKBuxjWT/p4DkqGHFRsysc8OauWr4ZRTY9dIx07Y9O0RkoR5jcv28aeD1vuAwhm3nLkDurwLXoALp4DpQ==
|
integrity sha512-Fu3T6pKBuxjWT/p4DkqGHFRsysc8OauWr4ZRTY9dIx07Y9O0RkoR5jcv28aeD1vuAwhm3nLkDurwLXoALp4DpQ==
|
||||||
|
|
||||||
|
jwa@^1.1.5:
|
||||||
|
version "1.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.1.6.tgz#87240e76c9808dbde18783cf2264ef4929ee50e6"
|
||||||
|
integrity sha512-tBO/cf++BUsJkYql/kBbJroKOgHWEigTKBAjjBEmrMGYd1QMBC74Hr4Wo2zCZw6ZrVhlJPvoMrkcOnlWR/DJfw==
|
||||||
|
dependencies:
|
||||||
|
buffer-equal-constant-time "1.0.1"
|
||||||
|
ecdsa-sig-formatter "1.0.10"
|
||||||
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
|
jws@^3.1.5:
|
||||||
|
version "3.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/jws/-/jws-3.1.5.tgz#80d12d05b293d1e841e7cb8b4e69e561adcf834f"
|
||||||
|
integrity sha512-GsCSexFADNQUr8T5HPJvayTjvPIfoyJPtLQBwn5a4WZQchcrPMPMAWcC1AzJVRDKyD6ZPROPAxgv6rfHViO4uQ==
|
||||||
|
dependencies:
|
||||||
|
jwa "^1.1.5"
|
||||||
|
safe-buffer "^5.0.1"
|
||||||
|
|
||||||
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
|
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
|
||||||
version "3.2.2"
|
version "3.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
|
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
|
||||||
|
@ -4022,11 +4071,46 @@ lodash.get@^4.4.2:
|
||||||
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
|
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
|
||||||
integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
|
integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=
|
||||||
|
|
||||||
|
lodash.includes@^4.3.0:
|
||||||
|
version "4.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
|
||||||
|
integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=
|
||||||
|
|
||||||
|
lodash.isboolean@^3.0.3:
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
|
||||||
|
integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=
|
||||||
|
|
||||||
|
lodash.isinteger@^4.0.4:
|
||||||
|
version "4.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
|
||||||
|
integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=
|
||||||
|
|
||||||
|
lodash.isnumber@^3.0.3:
|
||||||
|
version "3.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
|
||||||
|
integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=
|
||||||
|
|
||||||
|
lodash.isplainobject@^4.0.6:
|
||||||
|
version "4.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
|
||||||
|
integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
|
||||||
|
|
||||||
|
lodash.isstring@^4.0.1:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
|
||||||
|
integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=
|
||||||
|
|
||||||
lodash.memoize@^4.1.2:
|
lodash.memoize@^4.1.2:
|
||||||
version "4.1.2"
|
version "4.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||||
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
|
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
|
||||||
|
|
||||||
|
lodash.once@^4.0.0:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
|
||||||
|
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
|
||||||
|
|
||||||
lodash.uniq@^4.5.0:
|
lodash.uniq@^4.5.0:
|
||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue