1
0
Fork 0
mirror of https://github.com/codex-team/codex.docs.git synced 2025-08-07 14:35:26 +02:00

Merge branch 'main' into feature/add-sidebar-toggler

This commit is contained in:
YeoKyung Yoon 2022-08-11 00:46:06 +09:00 committed by GitHub
commit 5a8aff9d0c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 590 additions and 25 deletions

264
package-lock.json generated
View file

@ -7,13 +7,15 @@
"name": "codex.docs",
"license": "Apache-2.0",
"dependencies": {
"@hawk.so/javascript": "^3.0.1",
"@hawk.so/nodejs": "^3.1.2",
"config": "^3.3.6",
"cookie-parser": "^1.4.5",
"csurf": "^1.11.0",
"debug": "^4.3.2",
"dotenv": "^16.0.0",
"express": "^4.17.1",
"file-type": "^16.5.2",
"file-type": "^16.5.4",
"http-errors": "^2.0.0",
"jsonwebtoken": "^8.5.1",
"mime": "^3.0.0",
@ -1991,6 +1993,33 @@
"node": ">= 4"
}
},
"node_modules/@hawk.so/javascript": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@hawk.so/javascript/-/javascript-3.0.1.tgz",
"integrity": "sha512-c8YzhxDzginwzavhW9uwYT8FkYl8beUbr5bpuzcTTX+KKR1ryntogBIaYvTSbdr/ZMP9aS6yJQ9vKglJON3f/Q==",
"dependencies": {
"@hawk.so/types": "^0.1.13",
"error-stack-parser": "^2.0.6"
}
},
"node_modules/@hawk.so/nodejs": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@hawk.so/nodejs/-/nodejs-3.1.2.tgz",
"integrity": "sha512-FqZtJDEc3G/VdirsEEfA4BodA3OGXCSy2188aPSeaLkLWswaKAnkaJNTGHQL59dtOeSbvipMJVgtoqihHkpGBQ==",
"dependencies": {
"@hawk.so/types": "^0.1.15",
"axios": "^0.21.1",
"stack-trace": "^0.0.10"
}
},
"node_modules/@hawk.so/types": {
"version": "0.1.18",
"resolved": "https://registry.npmjs.org/@hawk.so/types/-/types-0.1.18.tgz",
"integrity": "sha512-SvECLGmLb5t90OSpk3n8DCjJsUoyjrq/Z6Ioil80tVkbMXRdGjaHZpn/0w1gBqtgNWBfW2cSbsQPqmyDj1NsqQ==",
"dependencies": {
"@types/mongodb": "^3.5.34"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz",
@ -2253,6 +2282,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/bson": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.2.0.tgz",
"integrity": "sha512-ELCPqAdroMdcuxqwMgUpifQyRoTpyYCNr1V9xKyF40VsBobsj+BbWNRvwGchMgBPGqkw655ypkjj2MEF5ywVwg==",
"deprecated": "This is a stub types definition. bson provides its own type definitions, so you do not need this installed.",
"dependencies": {
"bson": "*"
}
},
"node_modules/@types/cacheable-request": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz",
@ -2592,6 +2630,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/mongodb": {
"version": "3.6.20",
"resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz",
"integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==",
"dependencies": {
"@types/bson": "*",
"@types/node": "*"
}
},
"node_modules/@types/morgan": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.3.tgz",
@ -2647,7 +2694,6 @@
"version": "16.11.26",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz",
"integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/node-fetch": {
@ -3525,6 +3571,14 @@
"postcss": "^8.1.0"
}
},
"node_modules/axios": {
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"dependencies": {
"follow-redirects": "^1.14.0"
}
},
"node_modules/babel": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel/-/babel-6.23.0.tgz",
@ -3707,6 +3761,25 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"license": "MIT"
},
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
]
},
"node_modules/basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
@ -3873,6 +3946,40 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/bson": {
"version": "4.6.5",
"resolved": "https://registry.npmjs.org/bson/-/bson-4.6.5.tgz",
"integrity": "sha512-uqrgcjyOaZsHfz7ea8zLRCLe1u+QGUSzMZmvXqO24CDW7DWoW1qiN9folSwa7hSneTSgM2ykDIzF5kcQQ8cwNw==",
"dependencies": {
"buffer": "^5.6.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"node_modules/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",
@ -5288,6 +5395,14 @@
"is-arrayish": "^0.2.1"
}
},
"node_modules/error-stack-parser": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz",
"integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==",
"dependencies": {
"stackframe": "^1.3.4"
}
},
"node_modules/es-abstract": {
"version": "1.19.2",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.2.tgz",
@ -6386,10 +6501,9 @@
}
},
"node_modules/file-type": {
"version": "16.5.3",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.3.tgz",
"integrity": "sha512-uVsl7iFhHSOY4bEONLlTK47iAHtNsFHWP5YE4xJfZ4rnX7S1Q3wce09XgqSC7E/xh8Ncv/be1lNoyprlUH/x6A==",
"license": "MIT",
"version": "16.5.4",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz",
"integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
"dependencies": {
"readable-web-to-node-stream": "^3.0.0",
"strtok3": "^6.2.4",
@ -6476,6 +6590,25 @@
"dev": true,
"license": "MIT"
},
"node_modules/follow-redirects": {
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/foreachasync": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz",
@ -12576,6 +12709,19 @@
"dev": true,
"license": "MIT"
},
"node_modules/stack-trace": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
"integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
"engines": {
"node": "*"
}
},
"node_modules/stackframe": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
"integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="
},
"node_modules/statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
@ -15044,6 +15190,33 @@
}
}
},
"@hawk.so/javascript": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@hawk.so/javascript/-/javascript-3.0.1.tgz",
"integrity": "sha512-c8YzhxDzginwzavhW9uwYT8FkYl8beUbr5bpuzcTTX+KKR1ryntogBIaYvTSbdr/ZMP9aS6yJQ9vKglJON3f/Q==",
"requires": {
"@hawk.so/types": "^0.1.13",
"error-stack-parser": "^2.0.6"
}
},
"@hawk.so/nodejs": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@hawk.so/nodejs/-/nodejs-3.1.2.tgz",
"integrity": "sha512-FqZtJDEc3G/VdirsEEfA4BodA3OGXCSy2188aPSeaLkLWswaKAnkaJNTGHQL59dtOeSbvipMJVgtoqihHkpGBQ==",
"requires": {
"@hawk.so/types": "^0.1.15",
"axios": "^0.21.1",
"stack-trace": "^0.0.10"
}
},
"@hawk.so/types": {
"version": "0.1.18",
"resolved": "https://registry.npmjs.org/@hawk.so/types/-/types-0.1.18.tgz",
"integrity": "sha512-SvECLGmLb5t90OSpk3n8DCjJsUoyjrq/Z6Ioil80tVkbMXRdGjaHZpn/0w1gBqtgNWBfW2cSbsQPqmyDj1NsqQ==",
"requires": {
"@types/mongodb": "^3.5.34"
}
},
"@humanwhocodes/config-array": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz",
@ -15254,6 +15427,14 @@
}
}
},
"@types/bson": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.2.0.tgz",
"integrity": "sha512-ELCPqAdroMdcuxqwMgUpifQyRoTpyYCNr1V9xKyF40VsBobsj+BbWNRvwGchMgBPGqkw655ypkjj2MEF5ywVwg==",
"requires": {
"bson": "*"
}
},
"@types/cacheable-request": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz",
@ -15546,6 +15727,15 @@
"integrity": "sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==",
"dev": true
},
"@types/mongodb": {
"version": "3.6.20",
"resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.6.20.tgz",
"integrity": "sha512-WcdpPJCakFzcWWD9juKoZbRtQxKIMYF/JIAM4JrNHrMcnJL6/a2NWjXxW7fo9hxboxxkg+icff8d7+WIEvKgYQ==",
"requires": {
"@types/bson": "*",
"@types/node": "*"
}
},
"@types/morgan": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/@types/morgan/-/morgan-1.9.3.tgz",
@ -15598,8 +15788,7 @@
"@types/node": {
"version": "16.11.26",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.26.tgz",
"integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ==",
"dev": true
"integrity": "sha512-GZ7bu5A6+4DtG7q9GsoHXy3ALcgeIHP4NnL0Vv2wu0uUB/yQex26v0tf6/na1mm0+bS9Uw+0DFex7aaKr2qawQ=="
},
"@types/node-fetch": {
"version": "2.6.1",
@ -16234,6 +16423,14 @@
"postcss-value-parser": "^4.2.0"
}
},
"axios": {
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"requires": {
"follow-redirects": "^1.14.0"
}
},
"babel": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel/-/babel-6.23.0.tgz",
@ -16361,6 +16558,11 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
@ -16481,6 +16683,23 @@
"picocolors": "^1.0.0"
}
},
"bson": {
"version": "4.6.5",
"resolved": "https://registry.npmjs.org/bson/-/bson-4.6.5.tgz",
"integrity": "sha512-uqrgcjyOaZsHfz7ea8zLRCLe1u+QGUSzMZmvXqO24CDW7DWoW1qiN9folSwa7hSneTSgM2ykDIzF5kcQQ8cwNw==",
"requires": {
"buffer": "^5.6.0"
}
},
"buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"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",
@ -17479,6 +17698,14 @@
"is-arrayish": "^0.2.1"
}
},
"error-stack-parser": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz",
"integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==",
"requires": {
"stackframe": "^1.3.4"
}
},
"es-abstract": {
"version": "1.19.2",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.19.2.tgz",
@ -18261,9 +18488,9 @@
}
},
"file-type": {
"version": "16.5.3",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.3.tgz",
"integrity": "sha512-uVsl7iFhHSOY4bEONLlTK47iAHtNsFHWP5YE4xJfZ4rnX7S1Q3wce09XgqSC7E/xh8Ncv/be1lNoyprlUH/x6A==",
"version": "16.5.4",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz",
"integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==",
"requires": {
"readable-web-to-node-stream": "^3.0.0",
"strtok3": "^6.2.4",
@ -18330,6 +18557,11 @@
"integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==",
"dev": true
},
"follow-redirects": {
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA=="
},
"foreachasync": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz",
@ -22572,6 +22804,16 @@
"integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
"dev": true
},
"stack-trace": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
"integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg=="
},
"stackframe": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
"integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw=="
},
"statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",

View file

@ -26,7 +26,7 @@
"debug": "^4.3.2",
"dotenv": "^16.0.0",
"express": "^4.17.1",
"file-type": "^16.5.2",
"file-type": "^16.5.4",
"http-errors": "^2.0.0",
"jsonwebtoken": "^8.5.1",
"mime": "^3.0.0",
@ -34,6 +34,7 @@
"morgan": "^1.10.0",
"multer": "^1.4.2",
"nedb": "^1.8.0",
"node-cache": "^5.1.2",
"node-fetch": "^2.6.1",
"open-graph-scraper": "^4.9.0",
"twig": "^1.15.4",

View file

@ -2,7 +2,8 @@ import Page, { PageData } from '../models/page';
import Alias from '../models/alias';
import PagesOrder from './pagesOrder';
import PageOrder from '../models/pageOrder';
import HttpException from "../exceptions/httpException";
import HttpException from '../exceptions/httpException';
import PagesFlatArray from '../models/pagesFlatArray';
type PageDataFields = keyof PageData;
@ -195,6 +196,7 @@ class Pages {
pagesAvailable[index] = null;
pagesAvailable = Pages.removeChildren(pagesAvailable, item._id);
});
PagesFlatArray.regenerate();
return pagesAvailable;
}
@ -221,6 +223,7 @@ class Pages {
alias.save();
}
await PagesFlatArray.regenerate();
return insertedPage;
} catch (e) {
@ -264,6 +267,7 @@ class Pages {
Alias.markAsDeprecated(previousUri);
}
}
await PagesFlatArray.regenerate();
return updatedPage;
}
@ -286,8 +290,10 @@ class Pages {
await alias.destroy();
}
const removedPage = page.destroy();
await PagesFlatArray.regenerate();
return page.destroy();
return removedPage;
}
/**

View file

@ -1,5 +1,6 @@
import PageOrder from '../models/pageOrder';
import Page from '../models/page';
import PagesFlatArray from '../models/pagesFlatArray';
/**
* @class PagesOrder
@ -62,6 +63,7 @@ class PagesOrder {
order.push(childId);
await order.save();
await PagesFlatArray.regenerate();
}
/**
@ -76,11 +78,13 @@ class PagesOrder {
oldParentOrder.remove(targetPageId);
await oldParentOrder.save();
await PagesFlatArray.regenerate();
const newParentOrder = await PageOrder.get(newParentId);
newParentOrder.push(targetPageId);
await newParentOrder.save();
await PagesFlatArray.regenerate();
}
/**
@ -125,6 +129,7 @@ class PagesOrder {
pageOrder.order = Array.from(new Set([...pageOrder.order, ...unordered]));
pageOrder.putAbove(currentPageId, putAbovePageId);
await pageOrder.save();
await PagesFlatArray.regenerate();
}
/**
@ -138,7 +143,8 @@ class PagesOrder {
throw new Error('Page with given id does not contain order');
}
return order.destroy();
await order.destroy();
await PagesFlatArray.regenerate();
}
}

View file

@ -182,7 +182,7 @@ class PageOrder {
*
* @param {string} pageId - identity of page
*/
public getPageBefore(pageId: string): string | null {
public getSubPageBefore(pageId: string): string | null {
if (this.order === undefined) {
return null;
}
@ -204,7 +204,7 @@ class PageOrder {
*
* @param pageId - identity of page
*/
public getPageAfter(pageId: string): string | null {
public getSubPageAfter(pageId: string): string | null {
if (this.order === undefined) {
return null;
}

View file

@ -0,0 +1,182 @@
import Page from './page';
import PageOrder from './pageOrder';
import NodeCache from 'node-cache';
// Create cache for flat array
const cache = new NodeCache({ stdTTL: 120 });
const cacheKey = 'pagesFlatArray';
/**
* Element for pagesFlatArray
*/
export interface PagesFlatArrayData {
/**
* Page id
*/
id: string;
/**
* Page parent id
*/
parentId?: string;
/**
* id of parent with parent id '0'
*/
rootId: string;
/**
* Page level in sidebar view
*/
level: number;
/**
* Page title
*/
title: string;
/**
* Page uri
*/
uri?: string;
}
/**
* @class PagesFlatArray model - flat array of pages, which are ordered like in sidebar
*/
class PagesFlatArray {
/**
* Returns pages flat array
*
* @param nestingLimit - number of flat array nesting, set null to dismiss the restriction, default nesting 2
* @returns {Promise<Array<PagesFlatArrayData>>}
*/
public static async get(nestingLimit: number | null = 2): Promise<Array<PagesFlatArrayData>> {
// Get flat array from cache
let arr = cache.get(cacheKey) as Array<PagesFlatArrayData>;
// Check is flat array consists in cache
if (!arr) {
arr = await this.regenerate();
}
if (!nestingLimit) {
return arr;
}
return arr.filter( (item) => item.level < nestingLimit );
}
/**
* Generates new flat array, saves it to cache, returns it
* Calls, when there is no pages flat array data in cache or when page or pageOrder data updates
*
* @returns {Promise<Array<PagesFlatArrayData>>}
*/
public static async regenerate(): Promise<Array<PagesFlatArrayData>> {
const pages = await Page.getAll();
const pagesOrders = await PageOrder.getAll();
let arr = new Array<PagesFlatArrayData>();
// Get root order
const rootOrder = pagesOrders.find( order => order.page == '0' );
// Check is root order is not empty
if (!rootOrder) {
return [];
}
for (const pageId of rootOrder.order) {
arr = arr.concat(this.getChildrenFlatArray(pageId, 0, pages,
pagesOrders));
}
// Save generated flat array to cache
cache.set(cacheKey, arr);
return arr;
}
/**
* Returns previous page
*
* @param pageId - page id
* @returns {Promise<PagesFlatArrayData | undefined>}
*/
public static async getPageBefore(pageId: string): Promise<PagesFlatArrayData | undefined> {
const arr = await this.get();
const pageIndex = arr.findIndex( (item) => item.id == pageId);
// Check if index is not the first
if (pageIndex && pageIndex > 0) {
// Return previous element from array
return arr[pageIndex - 1];
} else {
return;
}
}
/**
* Returns next page
*
* @param pageId - page id
* @returns {Promise<PagesFlatArrayData | undefined>}
*/
public static async getPageAfter(pageId: string): Promise<PagesFlatArrayData | undefined> {
const arr = await this.get();
const pageIndex = arr.findIndex( (item) => item.id == pageId );
// Check if index is not the last
if (pageIndex < arr.length -1) {
// Return next element from array
return arr[pageIndex + 1];
} else {
return;
}
}
/**
* Returns child pages array
*
* @param pageId - parent page id
* @param level - page level in sidebar
* @param pages - all pages
* @param orders - all page orders
* @returns {Promise<Array<PagesFlatArrayData>>}
*/
private static getChildrenFlatArray(pageId: string, level: number,
pages: Array<Page>, orders: Array<PageOrder>): Array<PagesFlatArrayData> {
let arr: Array<PagesFlatArrayData> = new Array<PagesFlatArrayData>();
const page = pages.find( item => item._id == pageId );
// Add element to child array
if (page) {
arr.push( {
id: page._id!,
level: level,
parentId: page._parent,
rootId: '0',
title: page.title!,
uri: page.uri,
} );
}
const order = orders.find(item => item.page == pageId);
if (order) {
for (const childPageId of order.order) {
arr = arr.concat(this.getChildrenFlatArray(childPageId, level + 1,
pages, orders));
}
}
return arr;
}
}
export default PagesFlatArray;

View file

@ -3,6 +3,7 @@ import Aliases from '../controllers/aliases';
import Pages from '../controllers/pages';
import Alias from '../models/alias';
import verifyToken from './middlewares/token';
import PagesFlatArray from '../models/pagesFlatArray';
const router = express.Router();
@ -32,9 +33,14 @@ router.get('*', verifyToken, async (req: Request, res: Response) => {
const pageParent = await page.getParent();
const previousPage = await PagesFlatArray.getPageBefore(alias.id);
const nextPage = await PagesFlatArray.getPageAfter(alias.id);
res.render('pages/page', {
page,
pageParent,
previousPage,
nextPage,
config: req.app.locals.config,
});
}

View file

@ -158,8 +158,8 @@ router.delete('/page/:id', async (req: Request, res: Response) => {
}
const parentPageOrder = await PagesOrder.get(page._parent);
const pageBeforeId = parentPageOrder.getPageBefore(page._id);
const pageAfterId = parentPageOrder.getPageAfter(page._id);
const pageBeforeId = parentPageOrder.getSubPageBefore(page._id);
const pageAfterId = parentPageOrder.getSubPageAfter(page._id);
let pageToRedirect;

View file

@ -3,6 +3,7 @@ import Pages from '../controllers/pages';
import PagesOrder from '../controllers/pagesOrder';
import verifyToken from './middlewares/token';
import allowEdit from './middlewares/locals';
import PagesFlatArray from '../models/pagesFlatArray';
const router = express.Router();
@ -62,10 +63,15 @@ router.get('/page/:id', verifyToken, async (req: Request, res: Response, next: N
const pageParent = await page.parent;
const previousPage = await PagesFlatArray.getPageBefore(pageId);
const nextPage = await PagesFlatArray.getPageAfter(pageId);
res.render('pages/page', {
page,
pageParent,
config: req.app.locals.config,
previousPage,
nextPage,
});
} catch (error) {
res.status(404);

View file

@ -0,0 +1,50 @@
{#
Reusable navigaor component.
Available props:
- previousPage
- nextPage
- class: additional class for the navigator
Usage example:
{% include 'components/navigator.twig' with {previousPage: previousPage, nextPage: nextPage} %}
#}
{% set mainClass = 'navigator__item' %}
{% set tag = 'div' %}
{% set tag = 'a' %}
<div class="navigator">
{% if previousPage %}
<{{ tag }}
{{ name is not empty ? 'name="' ~ name ~ '"': '' }}
class="{{ mainClass }} {{ mainClass }}--previous {{ class ?? '' }}"
href="/{{ previousPage.uri }}"
>
<div class="{{ mainClass }}-direction">
previous
</div>
<div class="{{ mainClass }}-label">
{{ previousPage.title }}
</div>
</{{ tag }}>
{% endif %}
{% if nextPage %}
<{{ tag }}
{{ name is not empty ? 'name="' ~ name ~ '"': '' }}
class="{{ mainClass }} {{ mainClass }}--next {{ class ?? '' }}"
href="/{{ nextPage.uri }}"
>
<div class="{{ mainClass }}-direction">
next
</div>
<div class="{{ mainClass }}-label">
{{ nextPage.title }}
</div>
</{{ tag }}>
{% endif %}
</div>

View file

@ -39,8 +39,7 @@
{% endif %}
{% endfor %}
</section>
<footer>
</footer>
{% include 'components/navigator.twig' with {previousPage: previousPage, nextPage: nextPage} %}
</article>
{% endblock %}

View file

@ -0,0 +1,42 @@
.navigator {
display: flex;
}
.navigator__item {
margin-top: 35px;
cursor: pointer;
-webkit-user-select: none;
display: flex;
flex-direction: column;
justify-content: space-between;
background-color: var(--color-link-hover);
border-radius: 10px;
padding: 12px 16px 12px 16px;
color: black;
width: max-content;
font-weight: 500;
font-size: 14px;
&--previous {
align-items: flex-start;
margin-left: 0;
}
&--next {
align-items: flex-end;
margin-left: auto;
}
&-direction {
text-transform: capitalize;
color: var(--color-direction-navigation);
font-size: 12px;
font-weight: 400;
}
&-label {
width: 100%;
}
}

View file

@ -496,3 +496,4 @@
}
}
}

View file

@ -10,6 +10,7 @@
@import './components/auth.pcss';
@import './components/button.pcss';
@import './components/sidebar.pcss';
@import './components/navigator.pcss';
@import './components/table-of-content.pcss';
body {

View file

@ -1,6 +1,7 @@
:root {
--color-text-main: #313649;
--color-text-second: #5d6068;
--color-direction-navigation: #717682;
--color-line-gray: #E8E8EB;
--color-link-active: #2071cc;
--color-link-hover: #F3F6F8;
@ -124,7 +125,7 @@
--squircle {
border-radius: 8px;
@supports(-webkit-mask-box-image: url('')){
border-radius: 0;
-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 10.3872C0 1.83334 1.83334 0 10.3872 0H13.6128C22.1667 0 24 1.83334 24 10.3872V13.6128C24 22.1667 22.1667 24 13.6128 24H10.3872C1.83334 24 0 22.1667 0 13.6128V10.3872Z' fill='black'/%3E%3C/svg%3E%0A") 48% 41% 37.9% 53.3%;;

View file

@ -3806,6 +3806,18 @@ has-bigints@^1.0.1, has-bigints@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
handlebars@^4.1.0:
version "4.7.7"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1"
integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==
dependencies:
minimist "^1.2.5"
neo-async "^2.6.0"
source-map "^0.6.1"
wordwrap "^1.0.0"
optionalDependencies:
uglify-js "^3.1.4"
has-flag@^3.0.0:
version "3.0.0"
@ -4579,7 +4591,7 @@ make-dir@^1.3.0:
dependencies:
pify "^3.0.0"
make-dir@^2.0.0, make-dir@^2.1.0:
make-dir@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==
@ -4847,7 +4859,7 @@ negotiator@0.6.3:
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
neo-async@^2.6.2:
neo-async@^2.6.0, neo-async@^2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
@ -6538,7 +6550,7 @@ supports-color@^5.3.0, supports-color@^5.4.0:
dependencies:
has-flag "^3.0.0"
supports-color@^6.1.0:
supports-color@^6.0.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
@ -6804,6 +6816,11 @@ typescript@^4.3.5:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235"
integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==
uglify-js@^3.1.4:
version "3.16.3"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.16.3.tgz#94c7a63337ee31227a18d03b8a3041c210fd1f1d"
integrity sha512-uVbFqx9vvLhQg0iBaau9Z75AxWJ8tqM9AV890dIZCLApF4rTcyHwmAvLeEdYRs+BzYWu8Iw81F79ah0EfTXbaw==
uid-safe@2.1.5:
version "2.1.5"
resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a"
@ -7056,6 +7073,11 @@ word-wrap@^1.2.3:
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
wordwrap@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==
wrap-ansi@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"