mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-08-07 06:25:21 +02:00
backend rewritten in TS
This commit is contained in:
parent
ec7d17dfdc
commit
3ed21fad59
87 changed files with 8969 additions and 3964 deletions
|
@ -8,7 +8,7 @@
|
|||
{"title": "Support Project", "uri": "/support"}
|
||||
],
|
||||
"landingFrameSrc": "https://codex.so/editor?frame=1",
|
||||
"startPage": "codex",
|
||||
"startPage": "",
|
||||
"misprintsChatId": "12344564",
|
||||
"yandexMetrikaId": "",
|
||||
"carbon": {
|
||||
|
|
80
bin/server.js
Normal file
80
bin/server.js
Normal file
|
@ -0,0 +1,80 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
const app_1 = __importDefault(require("../src/app"));
|
||||
const http_1 = __importDefault(require("http"));
|
||||
const config_1 = __importDefault(require("config"));
|
||||
const debug_1 = __importDefault(require("debug"));
|
||||
const debug = debug_1.default.debug("codex.editor.docs:server");
|
||||
/**
|
||||
* Get port from environment and store in Express.
|
||||
*/
|
||||
const port = normalizePort(config_1.default.get("port") || '3000');
|
||||
app_1.default.set('port', port);
|
||||
/**
|
||||
* Create HTTP server.
|
||||
*/
|
||||
const server = http_1.default.createServer(app_1.default);
|
||||
/**
|
||||
* Listen on provided port, on all network interfaces.
|
||||
*/
|
||||
server.listen(port);
|
||||
server.on('error', onError);
|
||||
server.on('listening', onListening);
|
||||
/**
|
||||
* Normalize a port into a number, string, or false.
|
||||
*/
|
||||
function normalizePort(val) {
|
||||
const value = parseInt(val, 10);
|
||||
if (isNaN(value)) {
|
||||
// named pipe
|
||||
return val;
|
||||
}
|
||||
if (value >= 0) {
|
||||
// port number
|
||||
return value;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Event listener for HTTP server "error" event.
|
||||
*/
|
||||
function onError(error) {
|
||||
if (error.syscall !== 'listen') {
|
||||
throw error;
|
||||
}
|
||||
const bind = typeof port === 'string'
|
||||
? 'Pipe ' + port
|
||||
: 'Port ' + port;
|
||||
// handle specific listen errors with friendly messages
|
||||
switch (error.code) {
|
||||
case 'EACCES':
|
||||
console.error(bind + ' requires elevated privileges');
|
||||
process.exit(1);
|
||||
case 'EADDRINUSE':
|
||||
console.error(bind + ' is already in use');
|
||||
process.exit(1);
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Event listener for HTTP server "listening" event.
|
||||
*/
|
||||
function onListening() {
|
||||
const addr = server.address();
|
||||
if (addr === null) {
|
||||
debug('Address not found');
|
||||
process.exit(1);
|
||||
}
|
||||
const bind = typeof addr === 'string'
|
||||
? 'pipe ' + addr
|
||||
: 'port ' + addr.port;
|
||||
debug('Listening on ' + bind);
|
||||
}
|
||||
exports.default = { server, app: app_1.default };
|
92
bin/server.ts
Normal file
92
bin/server.ts
Normal file
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
import app from "../src/app";
|
||||
import http from "http";
|
||||
import config from "config";
|
||||
import Debug from "debug";
|
||||
|
||||
const debug = Debug.debug("codex.editor.docs:server");
|
||||
|
||||
/**
|
||||
* Get port from environment and store in Express.
|
||||
*/
|
||||
const port = normalizePort(config.get("port") || '3000');
|
||||
|
||||
app.set('port', port);
|
||||
|
||||
/**
|
||||
* Create HTTP server.
|
||||
*/
|
||||
const server = http.createServer(app);
|
||||
|
||||
/**
|
||||
* Listen on provided port, on all network interfaces.
|
||||
*/
|
||||
server.listen(port);
|
||||
server.on('error', onError);
|
||||
server.on('listening', onListening);
|
||||
|
||||
/**
|
||||
* Normalize a port into a number, string, or false.
|
||||
*/
|
||||
function normalizePort(val: string): number | string | false {
|
||||
const value = parseInt(val, 10);
|
||||
|
||||
if (isNaN(value)) {
|
||||
// named pipe
|
||||
return val;
|
||||
}
|
||||
|
||||
if (value >= 0) {
|
||||
// port number
|
||||
return value;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener for HTTP server "error" event.
|
||||
*/
|
||||
function onError(error: NodeJS.ErrnoException) {
|
||||
if (error.syscall !== 'listen') {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const bind = typeof port === 'string'
|
||||
? 'Pipe ' + port
|
||||
: 'Port ' + port;
|
||||
|
||||
// handle specific listen errors with friendly messages
|
||||
switch (error.code) {
|
||||
case 'EACCES':
|
||||
console.error(bind + ' requires elevated privileges');
|
||||
process.exit(1);
|
||||
case 'EADDRINUSE':
|
||||
console.error(bind + ' is already in use');
|
||||
process.exit(1);
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener for HTTP server "listening" event.
|
||||
*/
|
||||
function onListening() {
|
||||
const addr = server.address();
|
||||
|
||||
if (addr === null) {
|
||||
debug('Address not found');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const bind = typeof addr === 'string'
|
||||
? 'pipe ' + addr
|
||||
: 'port ' + addr.port;
|
||||
|
||||
debug('Listening on ' + bind);
|
||||
}
|
||||
|
||||
export default {server, app};
|
86
bin/www
86
bin/www
|
@ -1,86 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
const app = require('../src/app');
|
||||
const debug = require('debug')('codex.editor.docs:server');
|
||||
const http = require('http');
|
||||
const config = require('../config');
|
||||
|
||||
/**
|
||||
* Get port from environment and store in Express.
|
||||
*/
|
||||
const port = normalizePort(config.port || '3000');
|
||||
|
||||
app.set('port', port);
|
||||
|
||||
/**
|
||||
* Create HTTP server.
|
||||
*/
|
||||
const server = http.createServer(app);
|
||||
|
||||
/**
|
||||
* Listen on provided port, on all network interfaces.
|
||||
*/
|
||||
server.listen(port);
|
||||
server.on('error', onError);
|
||||
server.on('listening', onListening);
|
||||
|
||||
/**
|
||||
* Normalize a port into a number, string, or false.
|
||||
*/
|
||||
function normalizePort(val) {
|
||||
const value = parseInt(val, 10);
|
||||
|
||||
if (isNaN(value)) {
|
||||
// named pipe
|
||||
return val;
|
||||
}
|
||||
|
||||
if (value >= 0) {
|
||||
// port number
|
||||
return value;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener for HTTP server "error" event.
|
||||
*/
|
||||
function onError(error) {
|
||||
if (error.syscall !== 'listen') {
|
||||
throw error;
|
||||
}
|
||||
|
||||
const bind = typeof port === 'string'
|
||||
? 'Pipe ' + port
|
||||
: 'Port ' + port;
|
||||
|
||||
// handle specific listen errors with friendly messages
|
||||
switch (error.code) {
|
||||
case 'EACCES':
|
||||
console.error(bind + ' requires elevated privileges');
|
||||
process.exit(1);
|
||||
case 'EADDRINUSE':
|
||||
console.error(bind + ' is already in use');
|
||||
process.exit(1);
|
||||
default:
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event listener for HTTP server "listening" event.
|
||||
*/
|
||||
function onListening() {
|
||||
const addr = server.address();
|
||||
const bind = typeof addr === 'string'
|
||||
? 'pipe ' + addr
|
||||
: 'port ' + addr.port;
|
||||
|
||||
debug('Listening on ' + bind);
|
||||
}
|
||||
|
||||
module.exports = {server, app};
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"port": 3000,
|
||||
"database": ".db",
|
||||
"rcFile": "./.codexdocsrc",
|
||||
"uploads": "public/uploads",
|
||||
"secret": "iamasecretstring"
|
||||
}
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
/**
|
||||
* This module reads configuration file depending on NODE_ENV
|
||||
*
|
||||
* @type {module}
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const NODE_ENV = process.env.NODE_ENV || 'development';
|
||||
const configPath = `./${NODE_ENV}.json`;
|
||||
let config;
|
||||
|
||||
if (fs.existsSync(path.resolve(__dirname, configPath))) {
|
||||
config = require(configPath);
|
||||
} else {
|
||||
config = {
|
||||
database: '.db',
|
||||
port: 3000,
|
||||
uploads: 'public/uploads',
|
||||
secret: 'secret'
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = config;
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"port": 3000,
|
||||
"database": ".db",
|
||||
"rcFile": "./.codexdocsrc",
|
||||
"uploads": "public/uploads",
|
||||
"secret": "iamasecretstring"
|
||||
}
|
||||
|
|
|
@ -1,45 +1,54 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
let { password: db } = require('./src/utils/database');
|
||||
const program = require('commander');
|
||||
|
||||
const bcrypt = require('bcrypt');
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const database_1 = __importDefault(require("./src/utils/database"));
|
||||
let db = database_1.default['password'];
|
||||
const commander_1 = __importDefault(require("commander"));
|
||||
const program = commander_1.default.program;
|
||||
const bcrypt_1 = __importDefault(require("bcrypt"));
|
||||
const saltRounds = 12;
|
||||
|
||||
/**
|
||||
* Script for generating password, that will be used to create and edit pages in CodeX.Docs.
|
||||
* Hashes password with bcrypt and inserts it to the database.
|
||||
* @see {https://github.com/tj/commander.js | CommanderJS}
|
||||
*/
|
||||
program
|
||||
.description('Application for generating password, that will be used to create and edit pages in CodeX.Docs.')
|
||||
.usage('[password]')
|
||||
.arguments('<password>')
|
||||
.action(async function (password) {
|
||||
bcrypt.hash(password, saltRounds, async (error, hash) => {
|
||||
if (error) {
|
||||
return 'Hash generating error';
|
||||
}
|
||||
|
||||
const userDoc = { passHash: hash };
|
||||
|
||||
await db.remove({}, {multi: true});
|
||||
await db.insert(userDoc);
|
||||
|
||||
console.log('Password was successfully generated');
|
||||
.description('Application for generating password, that will be used to create and edit pages in CodeX.Docs.')
|
||||
.usage('[password]')
|
||||
.arguments('<password>')
|
||||
.action(function (password) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
bcrypt_1.default.hash(password, saltRounds, (error, hash) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (error) {
|
||||
return 'Hash generating error';
|
||||
}
|
||||
const userDoc = { passHash: hash };
|
||||
yield db.remove({}, { multi: true });
|
||||
yield db.insert(userDoc);
|
||||
console.log('Password was successfully generated');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
program.on('--help', () => {
|
||||
console.log('');
|
||||
console.log('Example:');
|
||||
console.log('yarn generatePassword qwerty');
|
||||
console.log('');
|
||||
});
|
||||
|
||||
program.on('--help', () => {
|
||||
console.log('');
|
||||
console.log('Example:');
|
||||
console.log('yarn generatePassword qwerty');
|
||||
console.log('');
|
||||
});
|
||||
program.parse(process.argv);
|
||||
|
||||
if (process.argv.length !== 3) {
|
||||
console.error('Invalid command: %s\nSee --help or -h for a list of available commands.', program.args.join(' '));
|
||||
process.exit(1);
|
||||
console.error('Invalid command: %s\nSee --help or -h for a list of available commands.', program.args.join(' '));
|
||||
process.exit(1);
|
||||
}
|
||||
|
|
48
generatePassword.ts
Normal file
48
generatePassword.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
import database from "./src/utils/database";
|
||||
let db = database['password'];
|
||||
|
||||
import commander from "commander";
|
||||
const program = commander.program;
|
||||
|
||||
import bcrypt from "bcrypt";
|
||||
const saltRounds = 12;
|
||||
|
||||
/**
|
||||
* Script for generating password, that will be used to create and edit pages in CodeX.Docs.
|
||||
* Hashes password with bcrypt and inserts it to the database.
|
||||
* @see {https://github.com/tj/commander.js | CommanderJS}
|
||||
*/
|
||||
program
|
||||
.description('Application for generating password, that will be used to create and edit pages in CodeX.Docs.')
|
||||
.usage('[password]')
|
||||
.arguments('<password>')
|
||||
.action(async function (password) {
|
||||
bcrypt.hash(password, saltRounds, async (error, hash) => {
|
||||
if (error) {
|
||||
return 'Hash generating error';
|
||||
}
|
||||
|
||||
const userDoc = { passHash: hash };
|
||||
|
||||
await db.remove({}, {multi: true});
|
||||
await db.insert(userDoc);
|
||||
|
||||
console.log('Password was successfully generated');
|
||||
});
|
||||
});
|
||||
|
||||
program.on('--help', () => {
|
||||
console.log('');
|
||||
console.log('Example:');
|
||||
console.log('yarn generatePassword qwerty');
|
||||
console.log('');
|
||||
});
|
||||
|
||||
program.parse(process.argv);
|
||||
|
||||
if (process.argv.length !== 3) {
|
||||
console.error('Invalid command: %s\nSee --help or -h for a list of available commands.', program.args.join(' '));
|
||||
process.exit(1);
|
||||
}
|
24
nodemon.json
24
nodemon.json
|
@ -1,11 +1,15 @@
|
|||
{
|
||||
"ignore": [
|
||||
"node_modules",
|
||||
"src/frontend",
|
||||
"public/dist"
|
||||
],
|
||||
"events": {
|
||||
"restart": "echo \"App restarted due to: '$FILENAME'\""
|
||||
},
|
||||
"ext": "js,twig"
|
||||
}
|
||||
"ignore": [
|
||||
"node_modules",
|
||||
"src/frontend",
|
||||
"public/dist"
|
||||
],
|
||||
"events": {
|
||||
"restart": "echo \"App restarted due to: '$FILENAME'\""
|
||||
},
|
||||
"watch": ["src/"],
|
||||
"execMap": {
|
||||
"ts": "node -r ts-node/register"
|
||||
},
|
||||
"ext": "js,json,ts,twig"
|
||||
}
|
70
package.json
70
package.json
|
@ -4,41 +4,46 @@
|
|||
"bin": {
|
||||
"generatePassword": "bin/generatePassword.js"
|
||||
},
|
||||
"browserslist": ["last 2 versions", "> 1%"],
|
||||
"browserslist": [
|
||||
"last 2 versions",
|
||||
"> 1%"
|
||||
],
|
||||
"scripts": {
|
||||
"start": "cross-env NODE_ENV=production nodemon ./bin/www",
|
||||
"start:dev": "cross-env NODE_ENV=development nodemon ./bin/www",
|
||||
"start": "cross-env NODE_ENV=production nodemon --config nodemon.json ./bin/server.js",
|
||||
"start:dev": "cross-env NODE_ENV=development nodemon --config nodemon.json ./bin/server.ts",
|
||||
"test": "cross-env NODE_ENV=testing mocha --recursive ./test",
|
||||
"lint": "eslint --fix --cache ./src/**/*.js",
|
||||
"build": "webpack ./src/frontend/js/app.js --o='./public/dist/[name].bundle.js' --output-library=Docs --output-public-path=/dist/ -p --mode=production",
|
||||
"build:dev": "webpack ./src/frontend/js/app.js --o='./public/dist/[name].bundle.js' --output-library=Docs --output-public-path=/dist/ -p --mode=development --watch",
|
||||
"precommit": "yarn lint && yarn test --exit",
|
||||
"generatePassword": "node ./generatePassword.js",
|
||||
"editor-upgrade": "yarn add -D @editorjs/{editorjs,header,code,delimiter,list,link,image,table,inline-code,marker,warning,checklist,raw}@latest"
|
||||
"editor-upgrade": "yarn add -D @editorjs/{editorjs,header,code,delimiter,list,link,image,table,inline-code,marker,warning,checklist,raw}@latest",
|
||||
"build:ts": "npx tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"bcrypt": "^5.0.0",
|
||||
"commander": "^2.19.0",
|
||||
"cookie-parser": "~1.4.3",
|
||||
"cross-env": "^5.2.0",
|
||||
"csurf": "^1.9.0",
|
||||
"debug": "~4.1.0",
|
||||
"dotenv": "^6.2.0",
|
||||
"express": "~4.16.0",
|
||||
"file-type": "^10.7.1",
|
||||
"http-errors": "~1.7.1",
|
||||
"jsonwebtoken": "^8.4.0",
|
||||
"mime": "^2.4.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"morgan": "~1.9.0",
|
||||
"multer": "^1.3.1",
|
||||
"bcrypt": "^5.0.1",
|
||||
"commander": "^8.1.0",
|
||||
"config": "^3.3.6",
|
||||
"cookie-parser": "^1.4.5",
|
||||
"cross-env": "^7.0.3",
|
||||
"csurf": "^1.11.0",
|
||||
"debug": "^4.3.2",
|
||||
"dotenv": "^10.0.0",
|
||||
"express": "^4.17.1",
|
||||
"file-type": "^16.5.2",
|
||||
"http-errors": "^1.8.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"mime": "^2.5.2",
|
||||
"mkdirp": "^1.0.4",
|
||||
"morgan": "^1.10.0",
|
||||
"multer": "^1.4.2",
|
||||
"nedb": "^1.8.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"nodemon": "^1.18.3",
|
||||
"open-graph-scraper": "^4.5.0",
|
||||
"twig": "~1.12.0",
|
||||
"nodemon": "^2.0.12",
|
||||
"open-graph-scraper": "^4.9.0",
|
||||
"twig": "^1.15.4",
|
||||
"typescript-eslint": "^0.0.1-alpha.0",
|
||||
"uuid4": "^1.0.0"
|
||||
"uuid4": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
|
@ -59,6 +64,23 @@
|
|||
"@editorjs/raw": "^2.3.0",
|
||||
"@editorjs/table": "^2.0.1",
|
||||
"@editorjs/warning": "^1.2.0",
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/commander": "^2.12.2",
|
||||
"@types/config": "^0.0.39",
|
||||
"@types/cookie-parser": "^1.4.2",
|
||||
"@types/csurf": "^1.11.2",
|
||||
"@types/debug": "^4.1.7",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/file-type": "^10.9.1",
|
||||
"@types/jsonwebtoken": "^8.5.4",
|
||||
"@types/mkdirp": "^1.0.2",
|
||||
"@types/morgan": "^1.9.3",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/nedb": "^1.8.12",
|
||||
"@types/node": "^16.4.1",
|
||||
"@types/node-fetch": "^2.5.12",
|
||||
"@types/open-graph-scraper": "^4.8.1",
|
||||
"@types/twig": "^1.12.6",
|
||||
"autoprefixer": "^9.1.3",
|
||||
"babel": "^6.23.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
|
@ -97,6 +119,8 @@
|
|||
"postcss-smart-import": "^0.7.6",
|
||||
"rimraf": "^2.6.3",
|
||||
"sinon": "^7.0.0",
|
||||
"ts-node": "^10.1.0",
|
||||
"typescript": "^4.3.5",
|
||||
"webpack": "^4.17.1",
|
||||
"webpack-cli": "^3.1.0"
|
||||
}
|
||||
|
|
62
src/app.js
62
src/app.js
|
@ -1,42 +1,34 @@
|
|||
const createError = require('http-errors');
|
||||
const express = require('express');
|
||||
const path = require('path');
|
||||
const cookieParser = require('cookie-parser');
|
||||
const logger = require('morgan');
|
||||
const rcParser = require('./utils/rcparser');
|
||||
const routes = require('./routes');
|
||||
|
||||
const app = express();
|
||||
const config = rcParser.getConfiguration();
|
||||
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = __importDefault(require("express"));
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const cookie_parser_1 = __importDefault(require("cookie-parser"));
|
||||
const morgan_1 = __importDefault(require("morgan"));
|
||||
const rcparser_1 = __importDefault(require("./utils/rcparser"));
|
||||
const routes_1 = __importDefault(require("./routes"));
|
||||
const app = express_1.default();
|
||||
const config = rcparser_1.default.getConfiguration();
|
||||
app.locals.config = config;
|
||||
|
||||
// view engine setup
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
app.set('views', path_1.default.join(__dirname, 'views'));
|
||||
app.set('view engine', 'twig');
|
||||
require('./utils/twig');
|
||||
|
||||
app.use(logger('dev'));
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
app.use(cookieParser());
|
||||
app.use(express.static(path.join(__dirname, '../public')));
|
||||
|
||||
app.use('/', routes);
|
||||
// catch 404 and forward to error handler
|
||||
app.use(function (req, res, next) {
|
||||
next(createError(404));
|
||||
});
|
||||
|
||||
app.use(morgan_1.default('dev'));
|
||||
app.use(express_1.default.json());
|
||||
app.use(express_1.default.urlencoded({ extended: true }));
|
||||
app.use(cookie_parser_1.default());
|
||||
app.use(express_1.default.static(path_1.default.join(__dirname, '../public')));
|
||||
app.use('/', routes_1.default);
|
||||
// error handler
|
||||
app.use(function (err, req, res, next) {
|
||||
// set locals, only providing error in development
|
||||
res.locals.message = err.message;
|
||||
res.locals.error = req.app.get('env') === 'development' ? err : {};
|
||||
|
||||
// render the error page
|
||||
res.status(err.status || 500);
|
||||
res.render('error');
|
||||
// set locals, only providing error in development
|
||||
res.locals.message = err.message;
|
||||
res.locals.error = req.app.get('env') == 'development' ? err : {};
|
||||
// render the error page
|
||||
res.status(err.status || 500);
|
||||
res.render('error');
|
||||
});
|
||||
|
||||
module.exports = app;
|
||||
exports.default = app;
|
||||
|
|
39
src/app.ts
Normal file
39
src/app.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import express, { Request, Response } from "express";
|
||||
import path from "path";
|
||||
import cookieParser from "cookie-parser";
|
||||
import morgan from "morgan";
|
||||
import rcParser from "./utils/rcparser";
|
||||
import routes from "./routes";
|
||||
import { NextFunction } from "connect";
|
||||
import HttpException from "./exceptions/httpException";
|
||||
|
||||
const app = express();
|
||||
const config = rcParser.getConfiguration();
|
||||
|
||||
app.locals.config = config;
|
||||
|
||||
// view engine setup
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
app.set('view engine', 'twig');
|
||||
require('./utils/twig');
|
||||
|
||||
app.use(morgan('dev'));
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
app.use(cookieParser());
|
||||
app.use(express.static(path.join(__dirname, '../public')));
|
||||
|
||||
app.use('/', routes);
|
||||
|
||||
// error handler
|
||||
app.use(function (err: HttpException, req: Request, res: Response, next: NextFunction) {
|
||||
// set locals, only providing error in development
|
||||
res.locals.message = err.message;
|
||||
res.locals.error = req.app.get('env') == 'development' ? err : {};
|
||||
|
||||
// render the error page
|
||||
res.status(err.status || 500);
|
||||
res.render('error');
|
||||
});
|
||||
|
||||
export default app;
|
|
@ -1,25 +1,37 @@
|
|||
const Alias = require('../models/alias');
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const alias_1 = __importDefault(require("../models/alias"));
|
||||
/**
|
||||
* @class Aliases
|
||||
* @classdesc Aliases controller
|
||||
*/
|
||||
class Aliases {
|
||||
/**
|
||||
* Find and return entity with given alias
|
||||
*
|
||||
* @param {string} aliasName - alias name of entity
|
||||
* @returns {Promise<Alias>}
|
||||
*/
|
||||
static async get(aliasName) {
|
||||
const alias = await Alias.get(aliasName);
|
||||
|
||||
if (!alias.id) {
|
||||
throw new Error('Entity with given alias does not exist');
|
||||
/**
|
||||
* Find and return entity with given alias
|
||||
*
|
||||
* @param {string} aliasName - alias name of entity
|
||||
* @returns {Promise<Alias>}
|
||||
*/
|
||||
static get(aliasName) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const alias = yield alias_1.default.get(aliasName);
|
||||
if (!alias.id) {
|
||||
throw new Error('Entity with given alias does not exist');
|
||||
}
|
||||
return alias;
|
||||
});
|
||||
}
|
||||
|
||||
return alias;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Aliases;
|
||||
exports.default = Aliases;
|
||||
|
|
25
src/controllers/aliases.ts
Normal file
25
src/controllers/aliases.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import Alias from '../models/alias';
|
||||
|
||||
/**
|
||||
* @class Aliases
|
||||
* @classdesc Aliases controller
|
||||
*/
|
||||
class Aliases {
|
||||
/**
|
||||
* Find and return entity with given alias
|
||||
*
|
||||
* @param {string} aliasName - alias name of entity
|
||||
* @returns {Promise<Alias>}
|
||||
*/
|
||||
static async get(aliasName: string): Promise<Alias> {
|
||||
const alias = await Alias.get(aliasName);
|
||||
|
||||
if (!alias.id) {
|
||||
throw new Error('Entity with given alias does not exist');
|
||||
}
|
||||
|
||||
return alias;
|
||||
}
|
||||
}
|
||||
|
||||
export default Aliases;
|
|
@ -1,194 +1,191 @@
|
|||
const Model = require('../models/page');
|
||||
const Alias = require('../models/alias');
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const page_1 = __importDefault(require("../models/page"));
|
||||
const page_2 = __importDefault(require("../models/page"));
|
||||
const alias_1 = __importDefault(require("../models/alias"));
|
||||
/**
|
||||
* @class Pages
|
||||
* @classdesc Pages controller
|
||||
*/
|
||||
class Pages {
|
||||
/**
|
||||
* Fields required for page model creation
|
||||
*
|
||||
* @returns {['title', 'body']}
|
||||
*/
|
||||
static get REQUIRED_FIELDS() {
|
||||
return [ 'body' ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return page model with passed id
|
||||
*
|
||||
* @param {string} id - page id
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static async get(id) {
|
||||
const page = await Model.get(id);
|
||||
|
||||
if (!page._id) {
|
||||
throw new Error('Page with given id does not exist');
|
||||
/**
|
||||
* Fields required for page model creation
|
||||
*
|
||||
* @returns {['title', 'body']}
|
||||
*/
|
||||
static get REQUIRED_FIELDS() {
|
||||
return ['body'];
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all pages
|
||||
*
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
static async getAll() {
|
||||
return Model.getAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all pages without children of passed page
|
||||
*
|
||||
* @param {string} parent - id of current page
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
static async getAllExceptChildren(parent) {
|
||||
const pagesAvailable = this.removeChildren(await Pages.getAll(), parent);
|
||||
|
||||
return pagesAvailable.filter((item) => item !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all children elements to null
|
||||
*
|
||||
* @param {Page[]} [pagesAvailable] - Array of all pages
|
||||
* @param {string} parent - id of parent page
|
||||
* @returns {Array<?Page>}
|
||||
*/
|
||||
static removeChildren(pagesAvailable, parent) {
|
||||
pagesAvailable.forEach(async (item, index) => {
|
||||
if (item === null || item._parent !== parent) {
|
||||
return;
|
||||
}
|
||||
pagesAvailable[index] = null;
|
||||
pagesAvailable = Pages.removeChildren(pagesAvailable, item._id);
|
||||
});
|
||||
|
||||
return pagesAvailable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new page model and save it in the database
|
||||
*
|
||||
* @param {PageData} data
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static async insert(data) {
|
||||
try {
|
||||
Pages.validate(data);
|
||||
|
||||
const page = new Model(data);
|
||||
|
||||
const insertedPage = await page.save();
|
||||
|
||||
if (insertedPage.uri) {
|
||||
const alias = new Alias({
|
||||
id: insertedPage._id,
|
||||
type: Alias.types.PAGE,
|
||||
}, insertedPage.uri);
|
||||
|
||||
alias.save();
|
||||
}
|
||||
|
||||
return insertedPage;
|
||||
} catch (validationError) {
|
||||
throw new Error(validationError);
|
||||
/**
|
||||
* Find and return page model with passed id
|
||||
*
|
||||
* @param {string} id - page id
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static get(id) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const page = yield page_1.default.get(id);
|
||||
if (!page._id) {
|
||||
throw new Error('Page with given id does not exist');
|
||||
}
|
||||
return page;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check PageData object for required fields
|
||||
*
|
||||
* @param {PageData} data
|
||||
* @throws {Error} - validation error
|
||||
*/
|
||||
static validate(data) {
|
||||
const allRequiredFields = Pages.REQUIRED_FIELDS.every(field => typeof data[field] !== 'undefined');
|
||||
|
||||
if (!allRequiredFields) {
|
||||
throw new Error('Some of required fields is missed');
|
||||
/**
|
||||
* Return all pages
|
||||
*
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
static getAll() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return page_1.default.getAll();
|
||||
});
|
||||
}
|
||||
|
||||
const hasBlocks = data.body && data.body.blocks && Array.isArray(data.body.blocks) && data.body.blocks.length > 0;
|
||||
|
||||
if (!hasBlocks) {
|
||||
throw new Error('Page body is invalid');
|
||||
/**
|
||||
* Return all pages without children of passed page
|
||||
*
|
||||
* @param {string} parent - id of current page
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
static getAllExceptChildren(parent) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const pagesAvailable = this.removeChildren(yield Pages.getAll(), parent);
|
||||
const nullfilteredpages = [];
|
||||
pagesAvailable.forEach((item, index) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (item instanceof page_2.default) {
|
||||
nullfilteredpages.push(item);
|
||||
}
|
||||
}));
|
||||
return nullfilteredpages;
|
||||
});
|
||||
}
|
||||
|
||||
const hasHeaderAsFirstBlock = data.body.blocks[0].type === 'header';
|
||||
|
||||
if (!hasHeaderAsFirstBlock) {
|
||||
throw new Error('First page Block must be a Header');
|
||||
/**
|
||||
* Set all children elements to null
|
||||
*
|
||||
* @param {Array<Page|null>} [pagesAvailable] - Array of all pages
|
||||
* @param {string} parent - id of parent page
|
||||
* @returns {Array<?Page>}
|
||||
*/
|
||||
static removeChildren(pagesAvailable, parent) {
|
||||
pagesAvailable.forEach((item, index) => __awaiter(this, void 0, void 0, function* () {
|
||||
if (item === null || item._parent !== parent) {
|
||||
return;
|
||||
}
|
||||
pagesAvailable[index] = null;
|
||||
pagesAvailable = Pages.removeChildren(pagesAvailable, item._id);
|
||||
}));
|
||||
return pagesAvailable;
|
||||
}
|
||||
|
||||
const headerIsNotEmpty = data.body.blocks[0].data.text.replace('<br>', '').trim() !== '';
|
||||
|
||||
if (!headerIsNotEmpty) {
|
||||
throw new Error('Please, fill page Header');
|
||||
/**
|
||||
* Create new page model and save it in the database
|
||||
*
|
||||
* @param {PageData} data
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static insert(data) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
try {
|
||||
Pages.validate(data);
|
||||
const page = new page_1.default(data);
|
||||
const insertedPage = yield page.save();
|
||||
if (insertedPage.uri) {
|
||||
const alias = new alias_1.default({
|
||||
id: insertedPage._id,
|
||||
type: alias_1.default.types.PAGE,
|
||||
}, insertedPage.uri);
|
||||
alias.save();
|
||||
}
|
||||
return insertedPage;
|
||||
}
|
||||
catch (validationError) {
|
||||
throw new Error(validationError);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update page with given id in the database
|
||||
*
|
||||
* @param {string} id - page id
|
||||
* @param {PageData} data
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static async update(id, data) {
|
||||
const page = await Model.get(id);
|
||||
const previousUri = page.uri;
|
||||
|
||||
if (!page._id) {
|
||||
throw new Error('Page with given id does not exist');
|
||||
/**
|
||||
* Check PageData object for required fields
|
||||
*
|
||||
* @param {PageData} data
|
||||
* @throws {Error} - validation error
|
||||
*/
|
||||
static validate(data) {
|
||||
const allRequiredFields = Pages.REQUIRED_FIELDS.every(field => typeof data[field] !== 'undefined');
|
||||
if (!allRequiredFields) {
|
||||
throw new Error('Some of required fields is missed');
|
||||
}
|
||||
const hasBlocks = data.body && data.body.blocks && Array.isArray(data.body.blocks) && data.body.blocks.length > 0;
|
||||
if (!hasBlocks) {
|
||||
throw new Error('Page body is invalid');
|
||||
}
|
||||
const hasHeaderAsFirstBlock = data.body.blocks[0].type === 'header';
|
||||
if (!hasHeaderAsFirstBlock) {
|
||||
throw new Error('First page Block must be a Header');
|
||||
}
|
||||
const headerIsNotEmpty = data.body.blocks[0].data.text.replace('<br>', '').trim() !== '';
|
||||
if (!headerIsNotEmpty) {
|
||||
throw new Error('Please, fill page Header');
|
||||
}
|
||||
}
|
||||
|
||||
if (data.uri && !data.uri.match(/^[a-z0-9'-]+$/i)) {
|
||||
throw new Error('Uri has unexpected characters');
|
||||
/**
|
||||
* Update page with given id in the database
|
||||
*
|
||||
* @param {string} id - page id
|
||||
* @param {PageData} data
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static update(id, data) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const page = yield page_1.default.get(id);
|
||||
const previousUri = page.uri;
|
||||
if (!page._id) {
|
||||
throw new Error('Page with given id does not exist');
|
||||
}
|
||||
if (data.uri && !data.uri.match(/^[a-z0-9'-]+$/i)) {
|
||||
throw new Error('Uri has unexpected characters');
|
||||
}
|
||||
page.data = data;
|
||||
const updatedPage = yield page.save();
|
||||
if (updatedPage.uri !== previousUri) {
|
||||
if (updatedPage.uri) {
|
||||
const alias = new alias_1.default({
|
||||
id: updatedPage._id,
|
||||
type: alias_1.default.types.PAGE,
|
||||
}, updatedPage.uri);
|
||||
alias.save();
|
||||
}
|
||||
alias_1.default.markAsDeprecated(previousUri);
|
||||
}
|
||||
return updatedPage;
|
||||
});
|
||||
}
|
||||
|
||||
page.data = data;
|
||||
const updatedPage = await page.save();
|
||||
|
||||
if (updatedPage.uri !== previousUri) {
|
||||
if (updatedPage.uri) {
|
||||
const alias = new Alias({
|
||||
id: updatedPage._id,
|
||||
type: Alias.types.PAGE,
|
||||
}, updatedPage.uri);
|
||||
|
||||
alias.save();
|
||||
}
|
||||
|
||||
Alias.markAsDeprecated(previousUri);
|
||||
/**
|
||||
* Remove page with given id from the database
|
||||
*
|
||||
* @param {string} id - page id
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static remove(id) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const page = yield page_1.default.get(id);
|
||||
if (!page._id) {
|
||||
throw new Error('Page with given id does not exist');
|
||||
}
|
||||
const alias = yield alias_1.default.get(page.uri);
|
||||
yield alias.destroy();
|
||||
return page.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
return updatedPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove page with given id from the database
|
||||
*
|
||||
* @param {string} id - page id
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static async remove(id) {
|
||||
const page = await Model.get(id);
|
||||
|
||||
if (!page._id) {
|
||||
throw new Error('Page with given id does not exist');
|
||||
}
|
||||
|
||||
const alias = await Alias.get(page.uri);
|
||||
|
||||
await alias.destroy();
|
||||
|
||||
return page.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Pages;
|
||||
exports.default = Pages;
|
||||
|
|
202
src/controllers/pages.ts
Normal file
202
src/controllers/pages.ts
Normal file
|
@ -0,0 +1,202 @@
|
|||
import Model, { PageData } from '../models/page';
|
||||
import Page from '../models/page';
|
||||
import Alias from '../models/alias';
|
||||
import { deflate } from 'zlib';
|
||||
|
||||
/**
|
||||
* @class Pages
|
||||
* @classdesc Pages controller
|
||||
*/
|
||||
class Pages {
|
||||
/**
|
||||
* Fields required for page model creation
|
||||
*
|
||||
* @returns {['title', 'body']}
|
||||
*/
|
||||
static get REQUIRED_FIELDS() {
|
||||
return [ 'body' ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return page model with passed id
|
||||
*
|
||||
* @param {string} id - page id
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static async get(id: string): Promise<Page> {
|
||||
const page = await Model.get(id);
|
||||
|
||||
if (!page._id) {
|
||||
throw new Error('Page with given id does not exist');
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all pages
|
||||
*
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
static async getAll(): Promise<Page[]> {
|
||||
return Model.getAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all pages without children of passed page
|
||||
*
|
||||
* @param {string} parent - id of current page
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
static async getAllExceptChildren(parent: string): Promise<Page[]> {
|
||||
const pagesAvailable = this.removeChildren(await Pages.getAll(), parent);
|
||||
|
||||
const nullfilteredpages: Page[] = [];
|
||||
pagesAvailable.forEach( async (item, index) => {
|
||||
if (item instanceof Page) {
|
||||
nullfilteredpages.push(item);
|
||||
}
|
||||
})
|
||||
return nullfilteredpages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all children elements to null
|
||||
*
|
||||
* @param {Array<Page|null>} [pagesAvailable] - Array of all pages
|
||||
* @param {string} parent - id of parent page
|
||||
* @returns {Array<?Page>}
|
||||
*/
|
||||
static removeChildren(pagesAvailable: Array<Page|null>, parent: string | undefined): Array<Page | null> {
|
||||
pagesAvailable.forEach(async (item, index) => {
|
||||
if (item === null || item._parent !== parent) {
|
||||
return;
|
||||
}
|
||||
pagesAvailable[index] = null;
|
||||
pagesAvailable = Pages.removeChildren(pagesAvailable, item._id);
|
||||
});
|
||||
|
||||
return pagesAvailable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new page model and save it in the database
|
||||
*
|
||||
* @param {PageData} data
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static async insert(data: PageData): Promise<Page> {
|
||||
try {
|
||||
Pages.validate(data);
|
||||
|
||||
const page = new Model(data);
|
||||
|
||||
const insertedPage = await page.save();
|
||||
|
||||
if (insertedPage.uri) {
|
||||
const alias = new Alias({
|
||||
id: insertedPage._id,
|
||||
type: Alias.types.PAGE,
|
||||
}, insertedPage.uri);
|
||||
|
||||
alias.save();
|
||||
}
|
||||
|
||||
return insertedPage;
|
||||
} catch (validationError) {
|
||||
throw new Error(validationError);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check PageData object for required fields
|
||||
*
|
||||
* @param {PageData} data
|
||||
* @throws {Error} - validation error
|
||||
*/
|
||||
static validate(data: PageData) {
|
||||
const allRequiredFields = Pages.REQUIRED_FIELDS.every(field => typeof data[field] !== 'undefined');
|
||||
|
||||
if (!allRequiredFields) {
|
||||
throw new Error('Some of required fields is missed');
|
||||
}
|
||||
|
||||
const hasBlocks = data.body && data.body.blocks && Array.isArray(data.body.blocks) && data.body.blocks.length > 0;
|
||||
|
||||
if (!hasBlocks) {
|
||||
throw new Error('Page body is invalid');
|
||||
}
|
||||
|
||||
const hasHeaderAsFirstBlock = data.body.blocks[0].type === 'header';
|
||||
|
||||
if (!hasHeaderAsFirstBlock) {
|
||||
throw new Error('First page Block must be a Header');
|
||||
}
|
||||
|
||||
const headerIsNotEmpty = data.body.blocks[0].data.text.replace('<br>', '').trim() !== '';
|
||||
|
||||
if (!headerIsNotEmpty) {
|
||||
throw new Error('Please, fill page Header');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update page with given id in the database
|
||||
*
|
||||
* @param {string} id - page id
|
||||
* @param {PageData} data
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static async update(id: string, data: PageData): Promise<Page> {
|
||||
const page = await Model.get(id);
|
||||
const previousUri = page.uri;
|
||||
|
||||
if (!page._id) {
|
||||
throw new Error('Page with given id does not exist');
|
||||
}
|
||||
|
||||
if (data.uri && !data.uri.match(/^[a-z0-9'-]+$/i)) {
|
||||
throw new Error('Uri has unexpected characters');
|
||||
}
|
||||
|
||||
page.data = data;
|
||||
const updatedPage = await page.save();
|
||||
|
||||
if (updatedPage.uri !== previousUri) {
|
||||
if (updatedPage.uri) {
|
||||
const alias = new Alias({
|
||||
id: updatedPage._id,
|
||||
type: Alias.types.PAGE,
|
||||
}, updatedPage.uri);
|
||||
|
||||
alias.save();
|
||||
}
|
||||
|
||||
Alias.markAsDeprecated(previousUri);
|
||||
}
|
||||
|
||||
return updatedPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove page with given id from the database
|
||||
*
|
||||
* @param {string} id - page id
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static async remove(id: string): Promise<Page> {
|
||||
const page = await Model.get(id);
|
||||
|
||||
if (!page._id) {
|
||||
throw new Error('Page with given id does not exist');
|
||||
}
|
||||
|
||||
const alias = await Alias.get(page.uri);
|
||||
|
||||
await alias.destroy();
|
||||
|
||||
return page.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
export default Pages;
|
|
@ -1,5 +1,18 @@
|
|||
const Model = require('../models/pageOrder');
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const pageOrder_1 = __importDefault(require("../models/pageOrder"));
|
||||
/**
|
||||
* @class PagesOrder
|
||||
* @classdesc PagesOrder controller
|
||||
|
@ -7,120 +20,114 @@ const Model = require('../models/pageOrder');
|
|||
* Manipulates with Pages: changes the order, deletes, updates and so on...
|
||||
*/
|
||||
class PagesOrder {
|
||||
/**
|
||||
* Returns Page's order
|
||||
*
|
||||
* @param {string} parentId - of which page we want to get children order
|
||||
* @returns {Promise<PageOrder>}
|
||||
*/
|
||||
static async get(parentId) {
|
||||
const order = await Model.get(parentId);
|
||||
|
||||
if (!order._id) {
|
||||
throw new Error('Page with given id does not contain order');
|
||||
/**
|
||||
* Returns Page's order
|
||||
*
|
||||
* @param {string} parentId - of which page we want to get children order
|
||||
* @returns {Promise<PageOrder>}
|
||||
*/
|
||||
static get(parentId) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const order = yield pageOrder_1.default.get(parentId);
|
||||
if (!order._id) {
|
||||
throw new Error('Page with given id does not contain order');
|
||||
}
|
||||
return order;
|
||||
});
|
||||
}
|
||||
|
||||
return order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all records about page's order
|
||||
*
|
||||
* @returns {Promise<PagesOrder[]>}
|
||||
*/
|
||||
static async getAll() {
|
||||
return Model.getAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes the child page to the parent's order list
|
||||
*
|
||||
* @param {string} parentId - parent page's id
|
||||
* @param {string} childId - new page pushed to the order
|
||||
*/
|
||||
static async push(parentId, childId) {
|
||||
const order = await Model.get(parentId);
|
||||
|
||||
order.push(childId);
|
||||
await order.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Move one page to another Page's order
|
||||
*
|
||||
* @param {string} oldParentId - old parent page's id
|
||||
* @param {string} newParentId - new parent page's id
|
||||
* @param {string} targetPageId - page's id which is changing the parent page
|
||||
*/
|
||||
static async move(oldParentId, newParentId, targetPageId) {
|
||||
const oldParentOrder = await Model.get(oldParentId);
|
||||
|
||||
oldParentOrder.remove(targetPageId);
|
||||
await oldParentOrder.save();
|
||||
|
||||
const newParentOrder = await Model.get(newParentId);
|
||||
|
||||
newParentOrder.push(targetPageId);
|
||||
await newParentOrder.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns new array with ordered pages
|
||||
*
|
||||
* @param {Page[]} pages - list of all available pages
|
||||
* @param {string} currentPageId - page's id around which we are ordering
|
||||
* @param {string} parentPageId - parent page's id that contains page above
|
||||
* @param {boolean} ignoreSelf - should we ignore current page in list or not
|
||||
* @returns {Page[]}
|
||||
*/
|
||||
static async getOrderedChildren(pages, currentPageId, parentPageId, ignoreSelf = false) {
|
||||
const children = await Model.get(parentPageId);
|
||||
const unordered = pages.filter(page => page._parent === parentPageId).map(page => page._id);
|
||||
|
||||
// Create unique array with ordered and unordered pages id
|
||||
const ordered = [ ...new Set([...children.order, ...unordered]) ];
|
||||
|
||||
const result = [];
|
||||
|
||||
ordered.forEach(pageId => {
|
||||
pages.forEach(page => {
|
||||
if (page._id === pageId && (pageId !== currentPageId || !ignoreSelf)) {
|
||||
result.push(page);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} unordered
|
||||
* @param {string} currentPageId - page's id that changes the order
|
||||
* @param {string} parentPageId - parent page's id that contains both two pages
|
||||
* @param {string} putAbovePageId - page's id above which we put the target page
|
||||
*/
|
||||
static async update(unordered, currentPageId, parentPageId, putAbovePageId) {
|
||||
const pageOrder = await Model.get(parentPageId);
|
||||
|
||||
// Create unique array with ordered and unordered pages id
|
||||
pageOrder.order = [ ...new Set([...pageOrder.order, ...unordered]) ];
|
||||
pageOrder.putAbove(currentPageId, putAbovePageId);
|
||||
await pageOrder.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parentId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
static async remove(parentId) {
|
||||
const order = await Model.get(parentId);
|
||||
|
||||
if (!order._id) {
|
||||
throw new Error('Page with given id does not contain order');
|
||||
/**
|
||||
* Returns all records about page's order
|
||||
*
|
||||
* @returns {Promise<PageOrder[]>}
|
||||
*/
|
||||
static getAll() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return pageOrder_1.default.getAll();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Pushes the child page to the parent's order list
|
||||
*
|
||||
* @param {string} parentId - parent page's id
|
||||
* @param {string} childId - new page pushed to the order
|
||||
*/
|
||||
static push(parentId, childId) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const order = yield pageOrder_1.default.get(parentId);
|
||||
order.push(childId);
|
||||
yield order.save();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Move one page to another Page's order
|
||||
*
|
||||
* @param {string} oldParentId - old parent page's id
|
||||
* @param {string} newParentId - new parent page's id
|
||||
* @param {string} targetPageId - page's id which is changing the parent page
|
||||
*/
|
||||
static move(oldParentId, newParentId, targetPageId) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const oldParentOrder = yield pageOrder_1.default.get(oldParentId);
|
||||
oldParentOrder.remove(targetPageId);
|
||||
yield oldParentOrder.save();
|
||||
const newParentOrder = yield pageOrder_1.default.get(newParentId);
|
||||
newParentOrder.push(targetPageId);
|
||||
yield newParentOrder.save();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Returns new array with ordered pages
|
||||
*
|
||||
* @param {Page[]} pages - list of all available pages
|
||||
* @param {string} currentPageId - page's id around which we are ordering
|
||||
* @param {string} parentPageId - parent page's id that contains page above
|
||||
* @param {boolean} ignoreSelf - should we ignore current page in list or not
|
||||
* @returns {Page[]}
|
||||
*/
|
||||
static getOrderedChildren(pages, currentPageId, parentPageId, ignoreSelf = false) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const children = yield pageOrder_1.default.get(parentPageId);
|
||||
const unordered = pages.filter(page => page._parent === parentPageId).map(page => page._id);
|
||||
// Create unique array with ordered and unordered pages id
|
||||
const ordered = Array.from(new Set([...children.order, ...unordered]));
|
||||
const result = [];
|
||||
ordered.forEach(pageId => {
|
||||
pages.forEach(page => {
|
||||
if (page._id === pageId && (pageId !== currentPageId || !ignoreSelf)) {
|
||||
result.push(page);
|
||||
}
|
||||
});
|
||||
});
|
||||
return result;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @param {string[]} unordered
|
||||
* @param {string} currentPageId - page's id that changes the order
|
||||
* @param {string} parentPageId - parent page's id that contains both two pages
|
||||
* @param {string} putAbovePageId - page's id above which we put the target page
|
||||
*/
|
||||
static update(unordered, currentPageId, parentPageId, putAbovePageId) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const pageOrder = yield pageOrder_1.default.get(parentPageId);
|
||||
// Create unique array with ordered and unordered pages id
|
||||
pageOrder.order = Array.from(new Set([...pageOrder.order, ...unordered]));
|
||||
pageOrder.putAbove(currentPageId, putAbovePageId);
|
||||
yield pageOrder.save();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @param {string} parentId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
static remove(parentId) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const order = yield pageOrder_1.default.get(parentId);
|
||||
if (!order._id) {
|
||||
throw new Error('Page with given id does not contain order');
|
||||
}
|
||||
return order.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
return order.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PagesOrder;
|
||||
exports.default = PagesOrder;
|
||||
|
|
128
src/controllers/pagesOrder.ts
Normal file
128
src/controllers/pagesOrder.ts
Normal file
|
@ -0,0 +1,128 @@
|
|||
import Model from '../models/pageOrder';
|
||||
import PageOrder from '../models/pageOrder';
|
||||
import Page from '../models/page';
|
||||
|
||||
/**
|
||||
* @class PagesOrder
|
||||
* @classdesc PagesOrder controller
|
||||
*
|
||||
* Manipulates with Pages: changes the order, deletes, updates and so on...
|
||||
*/
|
||||
class PagesOrder {
|
||||
/**
|
||||
* Returns Page's order
|
||||
*
|
||||
* @param {string} parentId - of which page we want to get children order
|
||||
* @returns {Promise<PageOrder>}
|
||||
*/
|
||||
static async get(parentId: string): Promise<PageOrder> {
|
||||
const order = await Model.get(parentId);
|
||||
|
||||
if (!order._id) {
|
||||
throw new Error('Page with given id does not contain order');
|
||||
}
|
||||
|
||||
return order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all records about page's order
|
||||
*
|
||||
* @returns {Promise<PageOrder[]>}
|
||||
*/
|
||||
static async getAll(): Promise<PageOrder[]> {
|
||||
return Model.getAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes the child page to the parent's order list
|
||||
*
|
||||
* @param {string} parentId - parent page's id
|
||||
* @param {string} childId - new page pushed to the order
|
||||
*/
|
||||
static async push(parentId: string, childId: string) {
|
||||
const order = await Model.get(parentId);
|
||||
|
||||
order.push(childId);
|
||||
await order.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Move one page to another Page's order
|
||||
*
|
||||
* @param {string} oldParentId - old parent page's id
|
||||
* @param {string} newParentId - new parent page's id
|
||||
* @param {string} targetPageId - page's id which is changing the parent page
|
||||
*/
|
||||
static async move(oldParentId: string, newParentId: string, targetPageId: string) {
|
||||
const oldParentOrder = await Model.get(oldParentId);
|
||||
|
||||
oldParentOrder.remove(targetPageId);
|
||||
await oldParentOrder.save();
|
||||
|
||||
const newParentOrder = await Model.get(newParentId);
|
||||
|
||||
newParentOrder.push(targetPageId);
|
||||
await newParentOrder.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns new array with ordered pages
|
||||
*
|
||||
* @param {Page[]} pages - list of all available pages
|
||||
* @param {string} currentPageId - page's id around which we are ordering
|
||||
* @param {string} parentPageId - parent page's id that contains page above
|
||||
* @param {boolean} ignoreSelf - should we ignore current page in list or not
|
||||
* @returns {Page[]}
|
||||
*/
|
||||
static async getOrderedChildren(pages: Page[], currentPageId: string, parentPageId: string, ignoreSelf = false) {
|
||||
const children = await Model.get(parentPageId);
|
||||
const unordered = pages.filter(page => page._parent === parentPageId).map(page => page._id);
|
||||
|
||||
// Create unique array with ordered and unordered pages id
|
||||
const ordered = Array.from(new Set([...children.order, ...unordered]));
|
||||
|
||||
const result: Page[] = [];
|
||||
|
||||
ordered.forEach(pageId => {
|
||||
pages.forEach(page => {
|
||||
if (page._id === pageId && (pageId !== currentPageId || !ignoreSelf)) {
|
||||
result.push(page);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} unordered
|
||||
* @param {string} currentPageId - page's id that changes the order
|
||||
* @param {string} parentPageId - parent page's id that contains both two pages
|
||||
* @param {string} putAbovePageId - page's id above which we put the target page
|
||||
*/
|
||||
static async update(unordered: string[], currentPageId: string, parentPageId: string, putAbovePageId: string) {
|
||||
const pageOrder = await Model.get(parentPageId);
|
||||
|
||||
// Create unique array with ordered and unordered pages id
|
||||
pageOrder.order = Array.from(new Set([...pageOrder.order, ...unordered]));
|
||||
pageOrder.putAbove(currentPageId, putAbovePageId);
|
||||
await pageOrder.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} parentId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
static async remove(parentId: string) {
|
||||
const order = await Model.get(parentId);
|
||||
|
||||
if (!order._id) {
|
||||
throw new Error('Page with given id does not contain order');
|
||||
}
|
||||
|
||||
return order.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
export default PagesOrder;
|
|
@ -1,13 +1,26 @@
|
|||
const fileType = require('file-type');
|
||||
const fetch = require('node-fetch');
|
||||
const fs = require('fs');
|
||||
const nodePath = require('path');
|
||||
|
||||
const Model = require('../models/file');
|
||||
const { random16 } = require('../utils/crypto');
|
||||
const { deepMerge } = require('../utils/objects');
|
||||
const config = require('../../config');
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const file_type_1 = __importDefault(require("file-type"));
|
||||
const node_fetch_1 = __importDefault(require("node-fetch"));
|
||||
const fs_1 = __importDefault(require("fs"));
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const file_1 = __importDefault(require("../models/file"));
|
||||
const crypto_1 = __importDefault(require("../utils/crypto"));
|
||||
const objects_1 = __importDefault(require("../utils/objects"));
|
||||
const config_1 = __importDefault(require("config"));
|
||||
const random16 = crypto_1.default.random16;
|
||||
/**
|
||||
* @class Transport
|
||||
* @classdesc Transport controller
|
||||
|
@ -15,114 +28,98 @@ const config = require('../../config');
|
|||
* Allows to save files from client or fetch them by URL
|
||||
*/
|
||||
class Transport {
|
||||
/**
|
||||
* Saves file passed from client
|
||||
*
|
||||
* @param {object} multerData - file data from multer
|
||||
* @param {string} multerData.originalname - original name of the file
|
||||
* @param {string} multerData.filename - name of the uploaded file
|
||||
* @param {string} multerData.path - path to the uploaded file
|
||||
* @param {number} multerData.size - size of the uploaded file
|
||||
* @param {string} multerData.mimetype - MIME type of the uploaded file
|
||||
*
|
||||
* @param {object} map - object that represents how should fields of File object should be mapped to response
|
||||
* @returns {Promise<FileData>}
|
||||
*/
|
||||
static async save(multerData, map) {
|
||||
const { originalname: name, path, filename, size, mimetype } = multerData;
|
||||
|
||||
const file = new Model({
|
||||
name,
|
||||
filename,
|
||||
path,
|
||||
size,
|
||||
mimetype,
|
||||
});
|
||||
|
||||
await file.save();
|
||||
|
||||
let response = file.data;
|
||||
|
||||
if (map) {
|
||||
response = Transport.composeResponse(file, map);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches file by passed URL
|
||||
*
|
||||
* @param {string} url - URL of the file
|
||||
* @param {object} map - object that represents how should fields of File object should be mapped to response
|
||||
* @returns {Promise<FileData>}
|
||||
*/
|
||||
static async fetch(url, map) {
|
||||
const fetchedFile = await fetch(url);
|
||||
const buffer = await fetchedFile.buffer();
|
||||
const filename = await random16();
|
||||
|
||||
const type = fileType(buffer);
|
||||
const ext = type ? type.ext : nodePath.extname(url).slice(1);
|
||||
|
||||
fs.writeFileSync(`${config.uploads}/${filename}.${ext}`, buffer);
|
||||
|
||||
const file = new Model({
|
||||
name: url,
|
||||
filename: `${filename}.${ext}`,
|
||||
path: `${config.uploads}/${filename}.${ext}`,
|
||||
size: buffer.length,
|
||||
mimetype: type ? type.mime : fetchedFile.headers.get('content-type'),
|
||||
});
|
||||
|
||||
await file.save();
|
||||
|
||||
let response = file.data;
|
||||
|
||||
if (map) {
|
||||
response = Transport.composeResponse(file, map);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map fields of File object to response by provided map object
|
||||
*
|
||||
* @param {File} file
|
||||
* @param {object} map - object that represents how should fields of File object should be mapped to response
|
||||
*
|
||||
*/
|
||||
static composeResponse(file, map) {
|
||||
const response = {};
|
||||
const { data } = file;
|
||||
|
||||
Object.entries(map).forEach(([name, path]) => {
|
||||
const fields = path.split(':');
|
||||
|
||||
if (fields.length > 1) {
|
||||
let object = {};
|
||||
const result = object;
|
||||
|
||||
fields.forEach((field, i) => {
|
||||
if (i === fields.length - 1) {
|
||||
object[field] = data[name];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
object[field] = {};
|
||||
object = object[field];
|
||||
/**
|
||||
* Saves file passed from client
|
||||
*
|
||||
* @param {object} multerData - file data from multer
|
||||
* @param {string} multerData.originalname - original name of the file
|
||||
* @param {string} multerData.filename - name of the uploaded file
|
||||
* @param {string} multerData.path - path to the uploaded file
|
||||
* @param {number} multerData.size - size of the uploaded file
|
||||
* @param {string} multerData.mimetype - MIME type of the uploaded file
|
||||
*
|
||||
* @param {object} map - object that represents how should fields of File object should be mapped to response
|
||||
* @returns {Promise<FileData>}
|
||||
*/
|
||||
static save(multerData, map) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const { originalname: name, path, filename, size, mimetype } = multerData;
|
||||
const file = new file_1.default({
|
||||
name,
|
||||
filename,
|
||||
path,
|
||||
size,
|
||||
mimetype,
|
||||
});
|
||||
yield file.save();
|
||||
let response = file.data;
|
||||
if (map) {
|
||||
response = Transport.composeResponse(file, map);
|
||||
}
|
||||
return response;
|
||||
});
|
||||
|
||||
deepMerge(response, result);
|
||||
} else {
|
||||
response[fields[0]] = data[name];
|
||||
}
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Fetches file by passed URL
|
||||
*
|
||||
* @param {string} url - URL of the file
|
||||
* @param {object} map - object that represents how should fields of File object should be mapped to response
|
||||
* @returns {Promise<FileData>}
|
||||
*/
|
||||
static fetch(url, map) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const fetchedFile = yield node_fetch_1.default(url);
|
||||
const buffer = yield fetchedFile.buffer();
|
||||
const filename = yield random16();
|
||||
const type = yield file_type_1.default.fromBuffer(buffer);
|
||||
const ext = type ? type.ext : path_1.default.extname(url).slice(1);
|
||||
fs_1.default.writeFileSync(`${config_1.default.get('uploads')}/${filename}.${ext}`, buffer);
|
||||
const file = new file_1.default({
|
||||
name: url,
|
||||
filename: `${filename}.${ext}`,
|
||||
path: `${config_1.default.get('uploads')}/${filename}.${ext}`,
|
||||
size: buffer.length,
|
||||
mimetype: type ? type.mime : fetchedFile.headers.get('content-type'),
|
||||
});
|
||||
yield file.save();
|
||||
let response = file.data;
|
||||
if (map) {
|
||||
response = Transport.composeResponse(file, map);
|
||||
}
|
||||
return response;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Map fields of File object to response by provided map object
|
||||
*
|
||||
* @param {File} file
|
||||
* @param {object} map - object that represents how should fields of File object should be mapped to response
|
||||
*
|
||||
*/
|
||||
static composeResponse(file, map) {
|
||||
;
|
||||
const response = {};
|
||||
const { data } = file;
|
||||
Object.entries(map).forEach(([name, path]) => {
|
||||
const fields = path.split(':');
|
||||
if (fields.length > 1) {
|
||||
let object = {};
|
||||
const result = object;
|
||||
fields.forEach((field, i) => {
|
||||
if (i === fields.length - 1) {
|
||||
object[field] = data[name];
|
||||
return;
|
||||
}
|
||||
object[field] = {};
|
||||
object = object[field];
|
||||
});
|
||||
objects_1.default(response, result);
|
||||
}
|
||||
else {
|
||||
response[fields[0]] = data[name];
|
||||
}
|
||||
});
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Transport;
|
||||
exports.default = Transport;
|
||||
|
|
134
src/controllers/transport.ts
Normal file
134
src/controllers/transport.ts
Normal file
|
@ -0,0 +1,134 @@
|
|||
import fileType from 'file-type';
|
||||
import fetch from 'node-fetch';
|
||||
import fs from 'fs';
|
||||
import nodePath from 'path';
|
||||
|
||||
import Model from '../models/file';
|
||||
import crypto from '../utils/crypto';
|
||||
import deepMerge from '../utils/objects';
|
||||
import config from 'config';
|
||||
import { FileData } from '../models/file';
|
||||
|
||||
const random16 = crypto.random16;
|
||||
|
||||
/**
|
||||
* @class Transport
|
||||
* @classdesc Transport controller
|
||||
*
|
||||
* Allows to save files from client or fetch them by URL
|
||||
*/
|
||||
class Transport {
|
||||
/**
|
||||
* Saves file passed from client
|
||||
*
|
||||
* @param {object} multerData - file data from multer
|
||||
* @param {string} multerData.originalname - original name of the file
|
||||
* @param {string} multerData.filename - name of the uploaded file
|
||||
* @param {string} multerData.path - path to the uploaded file
|
||||
* @param {number} multerData.size - size of the uploaded file
|
||||
* @param {string} multerData.mimetype - MIME type of the uploaded file
|
||||
*
|
||||
* @param {object} map - object that represents how should fields of File object should be mapped to response
|
||||
* @returns {Promise<FileData>}
|
||||
*/
|
||||
static async save(multerData: any, map: object): Promise<FileData> {
|
||||
const { originalname: name, path, filename, size, mimetype } = multerData;
|
||||
|
||||
const file = new Model({
|
||||
name,
|
||||
filename,
|
||||
path,
|
||||
size,
|
||||
mimetype,
|
||||
});
|
||||
|
||||
await file.save();
|
||||
|
||||
let response = file.data;
|
||||
|
||||
if (map) {
|
||||
response = Transport.composeResponse(file, map);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches file by passed URL
|
||||
*
|
||||
* @param {string} url - URL of the file
|
||||
* @param {object} map - object that represents how should fields of File object should be mapped to response
|
||||
* @returns {Promise<FileData>}
|
||||
*/
|
||||
static async fetch(url: string, map: object): Promise<FileData> {
|
||||
const fetchedFile = await fetch(url);
|
||||
const buffer = await fetchedFile.buffer();
|
||||
const filename = await random16();
|
||||
|
||||
const type = await fileType.fromBuffer(buffer);
|
||||
const ext = type ? type.ext : nodePath.extname(url).slice(1);
|
||||
|
||||
fs.writeFileSync(`${config.get('uploads')}/${filename}.${ext}`, buffer);
|
||||
|
||||
const file = new Model({
|
||||
name: url,
|
||||
filename: `${filename}.${ext}`,
|
||||
path: `${config.get('uploads')}/${filename}.${ext}`,
|
||||
size: buffer.length,
|
||||
mimetype: type ? type.mime : fetchedFile.headers.get('content-type'),
|
||||
});
|
||||
|
||||
await file.save();
|
||||
|
||||
let response = file.data;
|
||||
|
||||
if (map) {
|
||||
response = Transport.composeResponse(file, map);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map fields of File object to response by provided map object
|
||||
*
|
||||
* @param {File} file
|
||||
* @param {object} map - object that represents how should fields of File object should be mapped to response
|
||||
*
|
||||
*/
|
||||
static composeResponse(file: Model, map: object) {
|
||||
interface Dict {
|
||||
[key: string]: any;
|
||||
};
|
||||
const response = {} as Dict;
|
||||
const { data } = file;
|
||||
|
||||
Object.entries(map).forEach(([name, path]) => {
|
||||
const fields: string[] = path.split(':');
|
||||
|
||||
if (fields.length > 1) {
|
||||
let object = {} as Dict;
|
||||
const result = object;
|
||||
|
||||
fields.forEach((field, i) => {
|
||||
if (i === fields.length - 1) {
|
||||
object[field] = data[name];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
object[field] = {};
|
||||
object = object[field];
|
||||
});
|
||||
|
||||
deepMerge(response, result);
|
||||
} else {
|
||||
response[fields[0]] = data[name];
|
||||
}
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
export default Transport;
|
|
@ -1,20 +1,33 @@
|
|||
const Model = require('../models/user');
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const user_1 = __importDefault(require("../models/user"));
|
||||
/**
|
||||
* @class Users
|
||||
* @classdesc Users controller
|
||||
*/
|
||||
class Users {
|
||||
/**
|
||||
* Find and return user model.
|
||||
*
|
||||
* @returns {Promise<User>}
|
||||
*/
|
||||
static async get() {
|
||||
const userDoc = await Model.get();
|
||||
|
||||
return userDoc;
|
||||
}
|
||||
/**
|
||||
* Find and return user model.
|
||||
*
|
||||
* @returns {Promise<User>}
|
||||
*/
|
||||
static get() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const userDoc = yield user_1.default.get();
|
||||
return userDoc;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Users;
|
||||
exports.default = Users;
|
||||
|
|
20
src/controllers/users.ts
Normal file
20
src/controllers/users.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import Model from '../models/user';
|
||||
|
||||
/**
|
||||
* @class Users
|
||||
* @classdesc Users controller
|
||||
*/
|
||||
class Users {
|
||||
/**
|
||||
* Find and return user model.
|
||||
*
|
||||
* @returns {Promise<User>}
|
||||
*/
|
||||
static async get() {
|
||||
const userDoc = await Model.get();
|
||||
|
||||
return userDoc;
|
||||
}
|
||||
}
|
||||
|
||||
export default Users;
|
10
src/exceptions/httpException.js
Normal file
10
src/exceptions/httpException.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
class HttpException extends Error {
|
||||
constructor(status, message) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
exports.default = HttpException;
|
11
src/exceptions/httpException.ts
Normal file
11
src/exceptions/httpException.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
class HttpException extends Error {
|
||||
status: number;
|
||||
message: string;
|
||||
constructor(status: number, message: string) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
export default HttpException;
|
|
@ -1,16 +1,21 @@
|
|||
const { aliases: aliasesDb } = require('../utils/database/index');
|
||||
const { binaryMD5 } = require('../utils/crypto');
|
||||
|
||||
/**
|
||||
* @typedef {object} AliasData
|
||||
* @property {string} _id - alias id
|
||||
* @property {string} hash - alias binary hash
|
||||
* @property {string} type - entity type
|
||||
* @property {boolean} deprecated - indicate if alias deprecated
|
||||
* @property {string} id - entity id
|
||||
*
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const crypto_1 = __importDefault(require("../utils/crypto"));
|
||||
const index_1 = __importDefault(require("../utils/database/index"));
|
||||
const binaryMD5 = crypto_1.default.binaryMD5;
|
||||
const aliasesDb = index_1.default['aliases'];
|
||||
/**
|
||||
* @class Alias
|
||||
* @classdesc Alias model
|
||||
|
@ -22,126 +27,122 @@ const { binaryMD5 } = require('../utils/crypto');
|
|||
* @property {string} id - entity title
|
||||
*/
|
||||
class Alias {
|
||||
/**
|
||||
* Return Alias types
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static get types() {
|
||||
return {
|
||||
PAGE: 'page',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Find and return alias with given alias
|
||||
*
|
||||
* @param {string} aliasName - alias of entity
|
||||
* @returns {Promise<Alias>}
|
||||
*/
|
||||
static async get(aliasName) {
|
||||
const hash = binaryMD5(aliasName);
|
||||
let data = await aliasesDb.findOne({
|
||||
hash: hash,
|
||||
deprecated: false,
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
data = await aliasesDb.findOne({ hash: hash });
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
* @param {AliasData} data
|
||||
* @param {string} aliasName - alias of entity
|
||||
*/
|
||||
constructor(data = {}, aliasName = '') {
|
||||
if (data === null) {
|
||||
data = {};
|
||||
}
|
||||
if (data._id) {
|
||||
this._id = data._id;
|
||||
}
|
||||
if (aliasName) {
|
||||
this.hash = binaryMD5(aliasName);
|
||||
}
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
return new Alias(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
* @param {AliasData} data
|
||||
* @param {string} aliasName - alias of entity
|
||||
*/
|
||||
constructor(data = {}, aliasName = '') {
|
||||
if (data === null) {
|
||||
data = {};
|
||||
/**
|
||||
* Return Alias types
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static get types() {
|
||||
return {
|
||||
PAGE: 'page',
|
||||
};
|
||||
}
|
||||
if (data._id) {
|
||||
this._id = data._id;
|
||||
;
|
||||
/**
|
||||
* Find and return alias with given alias
|
||||
*
|
||||
* @param {string} aliasName - alias of entity
|
||||
* @returns {Promise<Alias>}
|
||||
*/
|
||||
static get(aliasName) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const hash = binaryMD5(aliasName);
|
||||
let data = yield aliasesDb.findOne({
|
||||
hash: hash,
|
||||
deprecated: false,
|
||||
});
|
||||
if (!data) {
|
||||
data = yield aliasesDb.findOne({ hash: hash });
|
||||
}
|
||||
if (data instanceof Error) {
|
||||
return new Alias();
|
||||
}
|
||||
return new Alias(data);
|
||||
});
|
||||
}
|
||||
if (aliasName) {
|
||||
this.hash = binaryMD5(aliasName);
|
||||
/**
|
||||
* Save or update alias data in the database
|
||||
*
|
||||
* @returns {Promise<Alias>}
|
||||
*/
|
||||
save() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (!this._id) {
|
||||
const insertedRow = yield aliasesDb.insert(this.data);
|
||||
this._id = insertedRow._id;
|
||||
}
|
||||
else {
|
||||
yield aliasesDb.update({ _id: this._id }, this.data);
|
||||
}
|
||||
return this;
|
||||
});
|
||||
}
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save or update alias data in the database
|
||||
*
|
||||
* @returns {Promise<Alias>}
|
||||
*/
|
||||
async save() {
|
||||
if (!this._id) {
|
||||
const insertedRow = await aliasesDb.insert(this.data);
|
||||
|
||||
this._id = insertedRow._id;
|
||||
} else {
|
||||
await aliasesDb.update({ _id: this._id }, this.data);
|
||||
/**
|
||||
* Set AliasData object fields to internal model fields
|
||||
*
|
||||
* @param {AliasData} aliasData
|
||||
*/
|
||||
set data(aliasData) {
|
||||
const { id, type, hash, deprecated } = aliasData;
|
||||
this.id = id || this.id;
|
||||
this.type = type || this.type;
|
||||
this.hash = hash || this.hash;
|
||||
this.deprecated = deprecated || false;
|
||||
}
|
||||
/**
|
||||
* Return AliasData object
|
||||
*
|
||||
* @returns {AliasData}
|
||||
*/
|
||||
get data() {
|
||||
return {
|
||||
_id: this._id,
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
hash: this.hash,
|
||||
deprecated: this.deprecated,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Mark alias as deprecated
|
||||
*
|
||||
* @param {string} aliasName - alias of entity
|
||||
* @returns {Promise<Alias>}
|
||||
*/
|
||||
static markAsDeprecated(aliasName) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const alias = yield Alias.get(aliasName);
|
||||
alias.deprecated = true;
|
||||
return alias.save();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @returns {Promise<Alias>}
|
||||
*/
|
||||
destroy() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
yield aliasesDb.remove({ _id: this._id });
|
||||
delete this._id;
|
||||
return this;
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set AliasData object fields to internal model fields
|
||||
*
|
||||
* @param {AliasData} aliasData
|
||||
*/
|
||||
set data(aliasData) {
|
||||
const { id, type, hash, deprecated } = aliasData;
|
||||
|
||||
this.id = id || this.id;
|
||||
this.type = type || this.type;
|
||||
this.hash = hash || this.hash;
|
||||
this.deprecated = deprecated || false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return AliasData object
|
||||
*
|
||||
* @returns {AliasData}
|
||||
*/
|
||||
get data() {
|
||||
return {
|
||||
_id: this._id,
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
hash: this.hash,
|
||||
deprecated: this.deprecated,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark alias as deprecated
|
||||
*
|
||||
* @param {string} aliasName - alias of entity
|
||||
* @returns {Promise<Alias>}
|
||||
*/
|
||||
static async markAsDeprecated(aliasName) {
|
||||
const alias = await Alias.get(aliasName);
|
||||
|
||||
alias.deprecated = true;
|
||||
|
||||
return alias.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<Alias>}
|
||||
*/
|
||||
async destroy() {
|
||||
await aliasesDb.remove({ _id: this._id });
|
||||
|
||||
delete this._id;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Alias;
|
||||
exports.default = Alias;
|
||||
|
|
167
src/models/alias.ts
Normal file
167
src/models/alias.ts
Normal file
|
@ -0,0 +1,167 @@
|
|||
import crypto from '../utils/crypto';
|
||||
import database from '../utils/database/index';
|
||||
|
||||
const binaryMD5 = crypto.binaryMD5;
|
||||
const aliasesDb = database['aliases'];
|
||||
|
||||
|
||||
/**
|
||||
* @typedef {object} AliasData
|
||||
* @property {string} _id - alias id
|
||||
* @property {string} hash - alias binary hash
|
||||
* @property {string} type - entity type
|
||||
* @property {boolean} deprecated - indicate if alias deprecated
|
||||
* @property {string} id - entity id
|
||||
*
|
||||
*/
|
||||
interface AliasData {
|
||||
_id?: string;
|
||||
hash?: string;
|
||||
type?: string;
|
||||
deprecated?: boolean;
|
||||
id?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class Alias
|
||||
* @classdesc Alias model
|
||||
*
|
||||
* @property {string} _id - alias id
|
||||
* @property {string} hash - alias binary hash
|
||||
* @property {string} type - entity type
|
||||
* @property {boolean} deprecated - indicate if alias deprecated
|
||||
* @property {string} id - entity title
|
||||
*/
|
||||
class Alias {
|
||||
_id: string | undefined;
|
||||
hash: string | undefined;
|
||||
type: string | undefined;
|
||||
deprecated: boolean | undefined;
|
||||
id: string | undefined;
|
||||
/**
|
||||
* Return Alias types
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
static get types(): { PAGE: string } {
|
||||
return {
|
||||
PAGE: 'page',
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Find and return alias with given alias
|
||||
*
|
||||
* @param {string} aliasName - alias of entity
|
||||
* @returns {Promise<Alias>}
|
||||
*/
|
||||
static async get(aliasName: string): Promise<Alias> {
|
||||
const hash = binaryMD5(aliasName);
|
||||
let data = await aliasesDb.findOne({
|
||||
hash: hash,
|
||||
deprecated: false,
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
data = await aliasesDb.findOne({ hash: hash });
|
||||
}
|
||||
|
||||
if (data instanceof Error) {
|
||||
return new Alias();
|
||||
}
|
||||
|
||||
return new Alias(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
* @param {AliasData} data
|
||||
* @param {string} aliasName - alias of entity
|
||||
*/
|
||||
constructor(data: AliasData = {}, aliasName: string = '') {
|
||||
if (data === null) {
|
||||
data = {};
|
||||
}
|
||||
if (data._id) {
|
||||
this._id = data._id;
|
||||
}
|
||||
if (aliasName) {
|
||||
this.hash = binaryMD5(aliasName);
|
||||
}
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save or update alias data in the database
|
||||
*
|
||||
* @returns {Promise<Alias>}
|
||||
*/
|
||||
async save(): Promise<Alias> {
|
||||
if (!this._id) {
|
||||
const insertedRow = await aliasesDb.insert(this.data) as { _id: string };
|
||||
|
||||
this._id = insertedRow._id;
|
||||
} else {
|
||||
await aliasesDb.update({ _id: this._id }, this.data);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set AliasData object fields to internal model fields
|
||||
*
|
||||
* @param {AliasData} aliasData
|
||||
*/
|
||||
set data(aliasData: AliasData) {
|
||||
const { id, type, hash, deprecated } = aliasData;
|
||||
|
||||
this.id = id || this.id;
|
||||
this.type = type || this.type;
|
||||
this.hash = hash || this.hash;
|
||||
this.deprecated = deprecated || false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return AliasData object
|
||||
*
|
||||
* @returns {AliasData}
|
||||
*/
|
||||
get data(): AliasData {
|
||||
return {
|
||||
_id: this._id,
|
||||
id: this.id,
|
||||
type: this.type,
|
||||
hash: this.hash,
|
||||
deprecated: this.deprecated,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark alias as deprecated
|
||||
*
|
||||
* @param {string} aliasName - alias of entity
|
||||
* @returns {Promise<Alias>}
|
||||
*/
|
||||
static async markAsDeprecated(aliasName: string) {
|
||||
const alias = await Alias.get(aliasName);
|
||||
|
||||
alias.deprecated = true;
|
||||
|
||||
return alias.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<Alias>}
|
||||
*/
|
||||
async destroy() {
|
||||
await aliasesDb.remove({ _id: this._id });
|
||||
|
||||
delete this._id;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export default Alias;
|
|
@ -1,16 +1,19 @@
|
|||
const { files: filesDb } = require('../utils/database/index');
|
||||
|
||||
/**
|
||||
* @typedef {object} FileData
|
||||
*
|
||||
* @property {string} _id - file id
|
||||
* @property {string} name - original file name
|
||||
* @property {string} filename - name of uploaded file
|
||||
* @property {string} path - path to uploaded file
|
||||
* @property {string} mimetype - file MIME type
|
||||
* @property {number} size - size of the file in
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const index_1 = __importDefault(require("../utils/database/index"));
|
||||
const filesDb = index_1.default['files'];
|
||||
/**
|
||||
* @class File
|
||||
* @class File model
|
||||
|
@ -23,138 +26,132 @@ const { files: filesDb } = require('../utils/database/index');
|
|||
* @property {number} size - size of the file in
|
||||
*/
|
||||
class File {
|
||||
/**
|
||||
* Find and return model of file with given id
|
||||
*
|
||||
* @param {string} _id - file id
|
||||
* @returns {Promise<File>}
|
||||
*/
|
||||
static async get(_id) {
|
||||
const data = await filesDb.findOne({ _id });
|
||||
|
||||
return new File(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return model of file with given id
|
||||
*
|
||||
* @param {string} filename - uploaded filename
|
||||
* @returns {Promise<File>}
|
||||
*/
|
||||
static async getByFilename(filename) {
|
||||
const data = await filesDb.findOne({ filename });
|
||||
|
||||
return new File(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all files which match passed query object
|
||||
*
|
||||
* @param {object} query
|
||||
* @returns {Promise<File[]>}
|
||||
*/
|
||||
static async getAll(query = {}) {
|
||||
const docs = await filesDb.find(query);
|
||||
|
||||
return Promise.all(docs.map(doc => new File(doc)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
* @param {FileData} data
|
||||
*/
|
||||
constructor(data = {}) {
|
||||
if (data === null) {
|
||||
data = {};
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
* @param {FileData} data
|
||||
*/
|
||||
constructor(data = {}) {
|
||||
if (data === null) {
|
||||
data = {};
|
||||
}
|
||||
if (data._id) {
|
||||
this._id = data._id;
|
||||
}
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
if (data._id) {
|
||||
this._id = data._id;
|
||||
/**
|
||||
* Find and return model of file with given id
|
||||
*
|
||||
* @param {string} _id - file id
|
||||
* @returns {Promise<File>}
|
||||
*/
|
||||
static get(_id) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const data = yield filesDb.findOne({ _id });
|
||||
return new File(data);
|
||||
});
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set FileData object fields to internal model fields
|
||||
*
|
||||
* @param {FileData} fileData
|
||||
*/
|
||||
set data(fileData) {
|
||||
const { name, filename, path, mimetype, size } = fileData;
|
||||
|
||||
this.name = name || this.name;
|
||||
this.filename = filename || this.filename;
|
||||
this.path = path ? this.processPath(path) : this.path;
|
||||
this.mimetype = mimetype || this.mimetype;
|
||||
this.size = size || this.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return FileData object
|
||||
*
|
||||
* @returns {FileData}
|
||||
*/
|
||||
get data() {
|
||||
return {
|
||||
_id: this._id,
|
||||
name: this.name,
|
||||
filename: this.filename,
|
||||
path: this.path,
|
||||
mimetype: this.mimetype,
|
||||
size: this.size,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Save or update file data in the database
|
||||
*
|
||||
* @returns {Promise<File>}
|
||||
*/
|
||||
async save() {
|
||||
if (!this._id) {
|
||||
const insertedRow = await filesDb.insert(this.data);
|
||||
|
||||
this._id = insertedRow._id;
|
||||
} else {
|
||||
await filesDb.update({ _id: this._id }, this.data);
|
||||
/**
|
||||
* Find and return model of file with given id
|
||||
*
|
||||
* @param {string} filename - uploaded filename
|
||||
* @returns {Promise<File>}
|
||||
*/
|
||||
static getByFilename(filename) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const data = yield filesDb.findOne({ filename });
|
||||
return new File(data);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Find all files which match passed query object
|
||||
*
|
||||
* @param {object} query
|
||||
* @returns {Promise<File[]>}
|
||||
*/
|
||||
static getAll(query = {}) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const docs = yield filesDb.find(query);
|
||||
if (docs instanceof Error) {
|
||||
return [];
|
||||
}
|
||||
return Promise.all(docs.map(doc => new File(doc)));
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Set FileData object fields to internal model fields
|
||||
*
|
||||
* @param {FileData} fileData
|
||||
*/
|
||||
set data(fileData) {
|
||||
const { name, filename, path, mimetype, size } = fileData;
|
||||
this.name = name || this.name;
|
||||
this.filename = filename || this.filename;
|
||||
this.path = path ? this.processPath(path) : this.path;
|
||||
this.mimetype = mimetype || this.mimetype;
|
||||
this.size = size || this.size;
|
||||
}
|
||||
/**
|
||||
* Return FileData object
|
||||
*
|
||||
* @returns {FileData}
|
||||
*/
|
||||
get data() {
|
||||
return {
|
||||
_id: this._id,
|
||||
name: this.name,
|
||||
filename: this.filename,
|
||||
path: this.path,
|
||||
mimetype: this.mimetype,
|
||||
size: this.size,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Save or update file data in the database
|
||||
*
|
||||
* @returns {Promise<File>}
|
||||
*/
|
||||
save() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (!this._id) {
|
||||
const insertedRow = yield filesDb.insert(this.data);
|
||||
this._id = insertedRow._id;
|
||||
}
|
||||
else {
|
||||
yield filesDb.update({ _id: this._id }, this.data);
|
||||
}
|
||||
return this;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Remove file data from the database
|
||||
*
|
||||
* @returns {Promise<File>}
|
||||
*/
|
||||
destroy() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
yield filesDb.remove({ _id: this._id });
|
||||
delete this._id;
|
||||
return this;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Removes unnecessary public folder prefix
|
||||
*
|
||||
* @param {string} path
|
||||
* @returns {string}
|
||||
*/
|
||||
processPath(path) {
|
||||
return path.replace(/^public/, '');
|
||||
}
|
||||
/**
|
||||
* Return readable file data
|
||||
*
|
||||
* @returns {FileData}
|
||||
*/
|
||||
toJSON() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove file data from the database
|
||||
*
|
||||
* @returns {Promise<File>}
|
||||
*/
|
||||
async destroy() {
|
||||
await filesDb.remove({ _id: this._id });
|
||||
|
||||
delete this._id;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes unnecessary public folder prefix
|
||||
*
|
||||
* @param {string} path
|
||||
* @returns {string}
|
||||
*/
|
||||
processPath(path) {
|
||||
return path.replace(/^public/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return readable file data
|
||||
*
|
||||
* @returns {FileData}
|
||||
*/
|
||||
toJSON() {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = File;
|
||||
exports.default = File;
|
||||
|
|
181
src/models/file.ts
Normal file
181
src/models/file.ts
Normal file
|
@ -0,0 +1,181 @@
|
|||
import database from '../utils/database/index';
|
||||
|
||||
const filesDb = database['files'];
|
||||
|
||||
/**
|
||||
* @typedef {object} FileData
|
||||
*
|
||||
* @property {string} _id - file id
|
||||
* @property {string} name - original file name
|
||||
* @property {string} filename - name of uploaded file
|
||||
* @property {string} path - path to uploaded file
|
||||
* @property {string} mimetype - file MIME type
|
||||
* @property {number} size - size of the file in
|
||||
*/
|
||||
|
||||
export interface FileData {
|
||||
_id?: string;
|
||||
name?: string;
|
||||
filename?: string;
|
||||
path?: string;
|
||||
mimetype?: string | null;
|
||||
size?: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class File
|
||||
* @class File model
|
||||
*
|
||||
* @property {string} _id - file id
|
||||
* @property {string} name - original file name
|
||||
* @property {string} filename - name of uploaded file
|
||||
* @property {string} path - path to uploaded file
|
||||
* @property {string} mimetype - file MIME type
|
||||
* @property {number} size - size of the file in
|
||||
*/
|
||||
class File {
|
||||
_id: string | undefined;
|
||||
name: string | undefined;
|
||||
filename: string | undefined;
|
||||
path: string | undefined;
|
||||
mimetype: string | undefined;
|
||||
size: number | undefined;
|
||||
/**
|
||||
* Find and return model of file with given id
|
||||
*
|
||||
* @param {string} _id - file id
|
||||
* @returns {Promise<File>}
|
||||
*/
|
||||
static async get(_id: string): Promise<File> {
|
||||
const data = await filesDb.findOne({ _id });
|
||||
|
||||
return new File(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return model of file with given id
|
||||
*
|
||||
* @param {string} filename - uploaded filename
|
||||
* @returns {Promise<File>}
|
||||
*/
|
||||
static async getByFilename(filename: string): Promise<File> {
|
||||
const data = await filesDb.findOne({ filename });
|
||||
|
||||
return new File(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all files which match passed query object
|
||||
*
|
||||
* @param {object} query
|
||||
* @returns {Promise<File[]>}
|
||||
*/
|
||||
static async getAll(query: object = {}): Promise<File[]> {
|
||||
const docs = await filesDb.find(query);
|
||||
|
||||
if (docs instanceof Error) {
|
||||
return [];
|
||||
}
|
||||
return Promise.all(docs.map(doc => new File(doc)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
* @param {FileData} data
|
||||
*/
|
||||
constructor(data: FileData = {}) {
|
||||
if (data === null) {
|
||||
data = {};
|
||||
}
|
||||
|
||||
if (data._id) {
|
||||
this._id = data._id;
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set FileData object fields to internal model fields
|
||||
*
|
||||
* @param {FileData} fileData
|
||||
*/
|
||||
set data(fileData: FileData) {
|
||||
const { name, filename, path, mimetype, size } = fileData;
|
||||
|
||||
this.name = name || this.name;
|
||||
this.filename = filename || this.filename;
|
||||
this.path = path ? this.processPath(path) : this.path;
|
||||
this.mimetype = mimetype || this.mimetype;
|
||||
this.size = size || this.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return FileData object
|
||||
*
|
||||
* @returns {FileData}
|
||||
*/
|
||||
get data(): FileData {
|
||||
return {
|
||||
_id: this._id,
|
||||
name: this.name,
|
||||
filename: this.filename,
|
||||
path: this.path,
|
||||
mimetype: this.mimetype,
|
||||
size: this.size,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Save or update file data in the database
|
||||
*
|
||||
* @returns {Promise<File>}
|
||||
*/
|
||||
async save(): Promise<File> {
|
||||
if (!this._id) {
|
||||
const insertedRow = await filesDb.insert(this.data) as { _id: string };
|
||||
|
||||
this._id = insertedRow._id;
|
||||
} else {
|
||||
await filesDb.update({ _id: this._id }, this.data);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove file data from the database
|
||||
*
|
||||
* @returns {Promise<File>}
|
||||
*/
|
||||
async destroy() {
|
||||
await filesDb.remove({ _id: this._id });
|
||||
|
||||
delete this._id;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes unnecessary public folder prefix
|
||||
*
|
||||
* @param {string} path
|
||||
* @returns {string}
|
||||
*/
|
||||
processPath(path: string) {
|
||||
return path.replace(/^public/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return readable file data
|
||||
*
|
||||
* @returns {FileData}
|
||||
*/
|
||||
toJSON() {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
|
||||
export default File;
|
|
@ -1,15 +1,20 @@
|
|||
const urlify = require('../utils/urlify');
|
||||
const { pages: pagesDb } = require('../utils/database/index');
|
||||
|
||||
/**
|
||||
* @typedef {object} PageData
|
||||
* @property {string} _id - page id
|
||||
* @property {string} title - page title
|
||||
* @property {string} uri - page uri
|
||||
* @property {*} body - page body
|
||||
* @property {string} parent - id of parent page
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const urlify_1 = __importDefault(require("../utils/urlify"));
|
||||
const index_1 = __importDefault(require("../utils/database/index"));
|
||||
const pagesDb = index_1.default['pages'];
|
||||
/**
|
||||
* @class Page
|
||||
* @class Page model
|
||||
|
@ -21,202 +26,201 @@ const { pages: pagesDb } = require('../utils/database/index');
|
|||
* @property {string} _parent - id of parent page
|
||||
*/
|
||||
class Page {
|
||||
/**
|
||||
* Find and return model of page with given id
|
||||
*
|
||||
* @param {string} _id - page id
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static async get(_id) {
|
||||
const data = await pagesDb.findOne({ _id });
|
||||
|
||||
return new Page(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return model of page with given uri
|
||||
*
|
||||
* @param {string} uri - page uri
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static async getByUri(uri) {
|
||||
const data = await pagesDb.findOne({ uri });
|
||||
|
||||
return new Page(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all pages which match passed query object
|
||||
*
|
||||
* @param {object} query
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
static async getAll(query = {}) {
|
||||
const docs = await pagesDb.find(query);
|
||||
|
||||
return Promise.all(docs.map(doc => new Page(doc)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
* @param {PageData} data
|
||||
*/
|
||||
constructor(data = {}) {
|
||||
if (data === null) {
|
||||
data = {};
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
* @param {PageData} data
|
||||
*/
|
||||
constructor(data = {}) {
|
||||
if (data === null) {
|
||||
data = {};
|
||||
}
|
||||
if (data._id) {
|
||||
this._id = data._id;
|
||||
}
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
if (data._id) {
|
||||
this._id = data._id;
|
||||
/**
|
||||
* Find and return model of page with given id
|
||||
*
|
||||
* @param {string} _id - page id
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static get(_id) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const data = yield pagesDb.findOne({ _id });
|
||||
if (data instanceof Error) {
|
||||
return new Page();
|
||||
}
|
||||
return new Page(data);
|
||||
});
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set PageData object fields to internal model fields
|
||||
*
|
||||
* @param {PageData} pageData
|
||||
*/
|
||||
set data(pageData) {
|
||||
const { body, parent, uri } = pageData;
|
||||
|
||||
this.body = body || this.body;
|
||||
this.title = this.extractTitleFromBody();
|
||||
this.uri = uri || '';
|
||||
this._parent = parent || this._parent || '0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return PageData object
|
||||
*
|
||||
* @returns {PageData}
|
||||
*/
|
||||
get data() {
|
||||
return {
|
||||
_id: this._id,
|
||||
title: this.title,
|
||||
uri: this.uri,
|
||||
body: this.body,
|
||||
parent: this._parent,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract first header from editor data
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
extractTitleFromBody() {
|
||||
const headerBlock = this.body ? this.body.blocks.find(block => block.type === 'header') : '';
|
||||
|
||||
return headerBlock ? headerBlock.data.text : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform title for uri
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
transformTitleToUri() {
|
||||
return urlify(this.title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Link given page as parent
|
||||
*
|
||||
* @param {Page} parentPage
|
||||
*/
|
||||
set parent(parentPage) {
|
||||
this._parent = parentPage._id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return parent page model
|
||||
*
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
get parent() {
|
||||
return pagesDb.findOne({ _id: this._parent })
|
||||
.then(data => new Page(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return child pages models
|
||||
*
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
get children() {
|
||||
return pagesDb.find({ parent: this._id })
|
||||
.then(data => data.map(page => new Page(page)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Save or update page data in the database
|
||||
*
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
async save() {
|
||||
this.uri = await this.composeUri(this.uri);
|
||||
|
||||
if (!this._id) {
|
||||
const insertedRow = await pagesDb.insert(this.data);
|
||||
|
||||
this._id = insertedRow._id;
|
||||
} else {
|
||||
await pagesDb.update({ _id: this._id }, this.data);
|
||||
/**
|
||||
* Find and return model of page with given uri
|
||||
*
|
||||
* @param {string} uri - page uri
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static getByUri(uri) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const data = yield pagesDb.findOne({ uri });
|
||||
if (data instanceof Error) {
|
||||
return new Page();
|
||||
}
|
||||
return new Page(data);
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove page data from the database
|
||||
*
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
async destroy() {
|
||||
await pagesDb.remove({ _id: this._id });
|
||||
|
||||
delete this._id;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return available uri
|
||||
*
|
||||
* @returns {Promise<string>}
|
||||
* @param uri
|
||||
*/
|
||||
async composeUri(uri) {
|
||||
let pageWithSameUriCount = 0;
|
||||
|
||||
if (!this._id) {
|
||||
uri = this.transformTitleToUri();
|
||||
/**
|
||||
* Find all pages which match passed query object
|
||||
*
|
||||
* @param {object} query
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
static getAll(query = {}) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const docs = yield pagesDb.find(query);
|
||||
if (docs instanceof Error) {
|
||||
return [];
|
||||
}
|
||||
return Promise.all(docs.map(doc => new Page(doc)));
|
||||
});
|
||||
}
|
||||
|
||||
if (uri) {
|
||||
let pageWithSameUri = await Page.getByUri(uri);
|
||||
|
||||
while (pageWithSameUri._id && pageWithSameUri._id !== this._id) {
|
||||
pageWithSameUriCount++;
|
||||
pageWithSameUri = await Page.getByUri(uri + `-${pageWithSameUriCount}`);
|
||||
}
|
||||
/**
|
||||
* Set PageData object fields to internal model fields
|
||||
*
|
||||
* @param {PageData} pageData
|
||||
*/
|
||||
set data(pageData) {
|
||||
const { body, parent, uri } = pageData;
|
||||
this.body = body || this.body;
|
||||
this.title = this.extractTitleFromBody();
|
||||
this.uri = uri || '';
|
||||
this._parent = parent || this._parent || '0';
|
||||
}
|
||||
/**
|
||||
* Return PageData object
|
||||
*
|
||||
* @returns {PageData}
|
||||
*/
|
||||
get data() {
|
||||
return {
|
||||
_id: this._id,
|
||||
title: this.title,
|
||||
uri: this.uri,
|
||||
body: this.body,
|
||||
parent: this._parent,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Extract first header from editor data
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
extractTitleFromBody() {
|
||||
const headerBlock = this.body ? this.body.blocks.find((block) => block.type === 'header') : '';
|
||||
return headerBlock ? headerBlock.data.text : '';
|
||||
}
|
||||
/**
|
||||
* Transform title for uri
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
transformTitleToUri() {
|
||||
return urlify_1.default(this.title);
|
||||
}
|
||||
/**
|
||||
* Link given page as parent
|
||||
*
|
||||
* @param {Page} parentPage
|
||||
*/
|
||||
set parent(parentPage) {
|
||||
this._parent = parentPage._id;
|
||||
}
|
||||
/**
|
||||
* Return parent page model
|
||||
*
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
get parent() {
|
||||
const data = pagesDb.findOne({ _id: this._parent });
|
||||
if (data instanceof Error) {
|
||||
return new Page();
|
||||
}
|
||||
return new Page(data);
|
||||
}
|
||||
/**
|
||||
* Return child pages models
|
||||
*
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
get children() {
|
||||
return pagesDb.find({ parent: this._id })
|
||||
.then(data => {
|
||||
if (data instanceof Error) {
|
||||
return [];
|
||||
}
|
||||
return data.map(page => new Page(page));
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Save or update page data in the database
|
||||
*
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
save() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
this.uri = yield this.composeUri(this.uri);
|
||||
if (!this._id) {
|
||||
const insertedRow = yield pagesDb.insert(this.data);
|
||||
this._id = insertedRow._id;
|
||||
}
|
||||
else {
|
||||
yield pagesDb.update({ _id: this._id }, this.data);
|
||||
}
|
||||
return this;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Remove page data from the database
|
||||
*
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
destroy() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
yield pagesDb.remove({ _id: this._id });
|
||||
delete this._id;
|
||||
return this;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Find and return available uri
|
||||
*
|
||||
* @returns {Promise<string>}
|
||||
* @param uri
|
||||
*/
|
||||
composeUri(uri) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let pageWithSameUriCount = 0;
|
||||
if (!this._id) {
|
||||
uri = this.transformTitleToUri();
|
||||
}
|
||||
if (uri) {
|
||||
let pageWithSameUri = yield Page.getByUri(uri);
|
||||
while (pageWithSameUri._id && pageWithSameUri._id !== this._id) {
|
||||
pageWithSameUriCount++;
|
||||
pageWithSameUri = yield Page.getByUri(uri + `-${pageWithSameUriCount}`);
|
||||
}
|
||||
}
|
||||
return pageWithSameUriCount ? uri + `-${pageWithSameUriCount}` : uri;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Return readable page data
|
||||
*
|
||||
* @returns {PageData}
|
||||
*/
|
||||
toJSON() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
return pageWithSameUriCount ? uri + `-${pageWithSameUriCount}` : uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return readable page data
|
||||
*
|
||||
* @returns {PageData}
|
||||
*/
|
||||
toJSON() {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Page;
|
||||
exports.default = Page;
|
||||
|
|
258
src/models/page.ts
Normal file
258
src/models/page.ts
Normal file
|
@ -0,0 +1,258 @@
|
|||
import urlify from '../utils/urlify';
|
||||
import database from '../utils/database/index';
|
||||
|
||||
const pagesDb = database['pages'];
|
||||
|
||||
/**
|
||||
* @typedef {object} PageData
|
||||
* @property {string} _id - page id
|
||||
* @property {string} title - page title
|
||||
* @property {string} uri - page uri
|
||||
* @property {*} body - page body
|
||||
* @property {string} parent - id of parent page
|
||||
*/
|
||||
export interface PageData {
|
||||
_id?: string;
|
||||
title?: string;
|
||||
uri?: string;
|
||||
body?: any;
|
||||
parent?: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class Page
|
||||
* @class Page model
|
||||
*
|
||||
* @property {string} _id - page id
|
||||
* @property {string} title - page title
|
||||
* @property {string} uri - page uri
|
||||
* @property {*} body - page body
|
||||
* @property {string} _parent - id of parent page
|
||||
*/
|
||||
class Page {
|
||||
_id?: string;
|
||||
body: any;
|
||||
title: any;
|
||||
uri: any;
|
||||
_parent: any;
|
||||
/**
|
||||
* Find and return model of page with given id
|
||||
*
|
||||
* @param {string} _id - page id
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static async get(_id: string): Promise<Page> {
|
||||
const data = await pagesDb.findOne({ _id });
|
||||
|
||||
if (data instanceof Error) {
|
||||
return new Page();
|
||||
}
|
||||
|
||||
return new Page(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return model of page with given uri
|
||||
*
|
||||
* @param {string} uri - page uri
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
static async getByUri(uri: string): Promise<Page> {
|
||||
const data = await pagesDb.findOne({ uri });
|
||||
|
||||
if (data instanceof Error) {
|
||||
return new Page();
|
||||
}
|
||||
|
||||
return new Page(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all pages which match passed query object
|
||||
*
|
||||
* @param {object} query
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
static async getAll(query: object = {}): Promise<Page[]> {
|
||||
const docs = await pagesDb.find(query);
|
||||
|
||||
if (docs instanceof Error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Promise.all(docs.map(doc => new Page(doc)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
* @param {PageData} data
|
||||
*/
|
||||
constructor(data: PageData = {}) {
|
||||
if (data === null) {
|
||||
data = {};
|
||||
}
|
||||
|
||||
if (data._id) {
|
||||
this._id = data._id;
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set PageData object fields to internal model fields
|
||||
*
|
||||
* @param {PageData} pageData
|
||||
*/
|
||||
set data(pageData: PageData) {
|
||||
const { body, parent, uri } = pageData;
|
||||
|
||||
this.body = body || this.body;
|
||||
this.title = this.extractTitleFromBody();
|
||||
this.uri = uri || '';
|
||||
this._parent = parent || this._parent || '0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return PageData object
|
||||
*
|
||||
* @returns {PageData}
|
||||
*/
|
||||
get data(): PageData {
|
||||
return {
|
||||
_id: this._id,
|
||||
title: this.title,
|
||||
uri: this.uri,
|
||||
body: this.body,
|
||||
parent: this._parent,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract first header from editor data
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
extractTitleFromBody() {
|
||||
const headerBlock = this.body ? this.body.blocks.find((block: any) => block.type === 'header') : '';
|
||||
|
||||
return headerBlock ? headerBlock.data.text : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform title for uri
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
transformTitleToUri() {
|
||||
return urlify(this.title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Link given page as parent
|
||||
*
|
||||
* @param {Page} parentPage
|
||||
*/
|
||||
set parent(parentPage: Page) {
|
||||
this._parent = parentPage._id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return parent page model
|
||||
*
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
get parent(): Page {
|
||||
const data = pagesDb.findOne({ _id: this._parent });
|
||||
|
||||
if (data instanceof Error) {
|
||||
return new Page();
|
||||
}
|
||||
return new Page(data as PageData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return child pages models
|
||||
*
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
get children(): Promise<Page[]> {
|
||||
return pagesDb.find({ parent: this._id })
|
||||
.then(data => {
|
||||
if (data instanceof Error) {
|
||||
return [];
|
||||
}
|
||||
return data.map(page => new Page(page))
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Save or update page data in the database
|
||||
*
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
async save(): Promise<Page> {
|
||||
this.uri = await this.composeUri(this.uri);
|
||||
|
||||
if (!this._id) {
|
||||
const insertedRow = await pagesDb.insert(this.data) as { _id: string };
|
||||
|
||||
this._id = insertedRow._id;
|
||||
} else {
|
||||
await pagesDb.update({ _id: this._id }, this.data);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove page data from the database
|
||||
*
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
async destroy(): Promise<Page> {
|
||||
await pagesDb.remove({ _id: this._id });
|
||||
|
||||
delete this._id;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return available uri
|
||||
*
|
||||
* @returns {Promise<string>}
|
||||
* @param uri
|
||||
*/
|
||||
async composeUri(uri: string) {
|
||||
let pageWithSameUriCount = 0;
|
||||
|
||||
if (!this._id) {
|
||||
uri = this.transformTitleToUri();
|
||||
}
|
||||
|
||||
if (uri) {
|
||||
let pageWithSameUri = await Page.getByUri(uri);
|
||||
|
||||
while (pageWithSameUri._id && pageWithSameUri._id !== this._id) {
|
||||
pageWithSameUriCount++;
|
||||
pageWithSameUri = await Page.getByUri(uri + `-${pageWithSameUriCount}`);
|
||||
}
|
||||
}
|
||||
|
||||
return pageWithSameUriCount ? uri + `-${pageWithSameUriCount}` : uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return readable page data
|
||||
*
|
||||
* @returns {PageData}
|
||||
*/
|
||||
toJSON() {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
|
||||
export default Page;
|
|
@ -1,12 +1,19 @@
|
|||
const { pagesOrder: db } = require('../utils/database/index');
|
||||
|
||||
/**
|
||||
* @typedef {object} PageOrderData
|
||||
* @property {string} _id - row unique id
|
||||
* @property {string} page - page id
|
||||
* @property {Array<string>} order - list of ordered pages
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const index_1 = __importDefault(require("../utils/database/index"));
|
||||
const db = index_1.default['pagesOrder'];
|
||||
/**
|
||||
* @class PageOrder
|
||||
* @classdesc PageOrder
|
||||
|
@ -14,201 +21,200 @@ const { pagesOrder: db } = require('../utils/database/index');
|
|||
* Creates order for Pages with children
|
||||
*/
|
||||
class PageOrder {
|
||||
/**
|
||||
* Returns current Page's children order
|
||||
*
|
||||
* @param {string} pageId - page's id
|
||||
* @returns {PageOrder}
|
||||
*/
|
||||
static async get(pageId) {
|
||||
const order = await db.findOne({ page: pageId });
|
||||
|
||||
let data = {};
|
||||
|
||||
if (!order) {
|
||||
data.page = pageId;
|
||||
} else {
|
||||
data = order;
|
||||
}
|
||||
|
||||
return new PageOrder(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all pages which match passed query object
|
||||
*
|
||||
* @param {object} query
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
static async getAll(query = {}) {
|
||||
const docs = await db.find(query);
|
||||
|
||||
return Promise.all(docs.map(doc => new PageOrder(doc)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
* @param {PageOrderData} data
|
||||
*/
|
||||
constructor(data = {}) {
|
||||
if (data === null) {
|
||||
data = {};
|
||||
}
|
||||
|
||||
if (data._id) {
|
||||
this._id = data._id;
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* constructor data setter
|
||||
*
|
||||
* @param {PageOrderData} pageOrderData
|
||||
*/
|
||||
set data(pageOrderData) {
|
||||
this._page = pageOrderData.page || 0;
|
||||
this._order = pageOrderData.order || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Page Children order
|
||||
*
|
||||
* @returns {PageOrderData}
|
||||
*/
|
||||
get data() {
|
||||
return {
|
||||
_id: this._id,
|
||||
page: '' + this._page,
|
||||
order: this._order,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes page id to the orders array
|
||||
*
|
||||
* @param {string} pageId - page's id
|
||||
*/
|
||||
push(pageId) {
|
||||
if (typeof pageId === 'string') {
|
||||
this._order.push(pageId);
|
||||
} else {
|
||||
throw new Error('given id is not string');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes page id from orders array
|
||||
*
|
||||
* @param {string} pageId - page's id
|
||||
*/
|
||||
remove(pageId) {
|
||||
const found = this._order.indexOf(pageId);
|
||||
|
||||
if (found >= 0) {
|
||||
this._order.splice(found, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} currentPageId - page's id that changes the order
|
||||
* @param {string} putAbovePageId - page's id above which we put the target page
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
putAbove(currentPageId, putAbovePageId) {
|
||||
const found1 = this.order.indexOf(putAbovePageId);
|
||||
const found2 = this.order.indexOf(currentPageId);
|
||||
|
||||
if (found1 === -1 || found2 === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const margin = found1 < found2 ? 1 : 0;
|
||||
|
||||
this.order.splice(found1, 0, currentPageId);
|
||||
this.order.splice(found2 + margin, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns page before passed page with id
|
||||
*
|
||||
* @param {string} pageId
|
||||
*/
|
||||
getPageBefore(pageId) {
|
||||
const currentPageInOrder = this.order.indexOf(pageId);
|
||||
|
||||
/**
|
||||
* If page not found or first return nothing
|
||||
* @class
|
||||
*
|
||||
* @param {PageOrderData} data
|
||||
*/
|
||||
if (currentPageInOrder <= 0) {
|
||||
return;
|
||||
constructor(data = {}) {
|
||||
if (data === null) {
|
||||
data = {};
|
||||
}
|
||||
if (data._id) {
|
||||
this._id = data._id;
|
||||
}
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
return this.order[currentPageInOrder - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns page before passed page with id
|
||||
*
|
||||
* @param pageId
|
||||
*/
|
||||
getPageAfter(pageId) {
|
||||
const currentPageInOrder = this.order.indexOf(pageId);
|
||||
|
||||
/**
|
||||
* If page not found or is last
|
||||
* Returns current Page's children order
|
||||
*
|
||||
* @param {string} pageId - page's id
|
||||
* @returns {PageOrder}
|
||||
*/
|
||||
if (currentPageInOrder === -1 || currentPageInOrder === this.order.length - 1) {
|
||||
return;
|
||||
static get(pageId) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const order = yield db.findOne({ page: pageId });
|
||||
let data = {};
|
||||
if (order instanceof Error || order === null) {
|
||||
data.page = pageId;
|
||||
}
|
||||
else {
|
||||
data = order;
|
||||
}
|
||||
return new PageOrder(data);
|
||||
});
|
||||
}
|
||||
|
||||
return this.order[currentPageInOrder + 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} order - define new order
|
||||
*/
|
||||
set order(order) {
|
||||
this._order = order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ordered list
|
||||
*
|
||||
* @returns {string[]}
|
||||
*/
|
||||
get order() {
|
||||
return this._order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save or update page data in the database
|
||||
*/
|
||||
async save() {
|
||||
if (!this._id) {
|
||||
const insertedRow = await db.insert(this.data);
|
||||
|
||||
this._id = insertedRow._id;
|
||||
} else {
|
||||
await db.update({ _id: this._id }, this.data);
|
||||
/**
|
||||
* Find all pages which match passed query object
|
||||
*
|
||||
* @param {object} query
|
||||
* @returns {Promise<PageOrder[]>}
|
||||
*/
|
||||
static getAll(query = {}) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const docs = yield db.find(query);
|
||||
if (docs === null || docs instanceof Error) {
|
||||
return [];
|
||||
}
|
||||
return Promise.all(docs.map(doc => new PageOrder(doc)));
|
||||
});
|
||||
}
|
||||
/**
|
||||
* constructor data setter
|
||||
*
|
||||
* @param {PageOrderData} pageOrderData
|
||||
*/
|
||||
set data(pageOrderData) {
|
||||
this.page = pageOrderData.page || '0';
|
||||
this.order = pageOrderData.order || [];
|
||||
}
|
||||
/**
|
||||
* Return Page Children order
|
||||
*
|
||||
* @returns {PageOrderData}
|
||||
*/
|
||||
get data() {
|
||||
return {
|
||||
_id: this._id,
|
||||
page: '' + this.page,
|
||||
order: this.order,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Pushes page id to the orders array
|
||||
*
|
||||
* @param {string} pageId - page's id
|
||||
*/
|
||||
push(pageId) {
|
||||
if (typeof pageId === 'string') {
|
||||
if (this.order === undefined) {
|
||||
this.order = [];
|
||||
}
|
||||
this.order.push(pageId);
|
||||
}
|
||||
else {
|
||||
throw new Error('given id is not string');
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Removes page id from orders array
|
||||
*
|
||||
* @param {string} pageId - page's id
|
||||
*/
|
||||
remove(pageId) {
|
||||
if (this.order === undefined) {
|
||||
return;
|
||||
}
|
||||
const found = this.order.indexOf(pageId);
|
||||
if (found >= 0) {
|
||||
this.order.splice(found, 1);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {string} currentPageId - page's id that changes the order
|
||||
* @param {string} putAbovePageId - page's id above which we put the target page
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
putAbove(currentPageId, putAbovePageId) {
|
||||
if (this.order === undefined) {
|
||||
return;
|
||||
}
|
||||
const found1 = this.order.indexOf(putAbovePageId);
|
||||
const found2 = this.order.indexOf(currentPageId);
|
||||
if (found1 === -1 || found2 === -1) {
|
||||
return;
|
||||
}
|
||||
const margin = found1 < found2 ? 1 : 0;
|
||||
this.order.splice(found1, 0, currentPageId);
|
||||
this.order.splice(found2 + margin, 1);
|
||||
}
|
||||
/**
|
||||
* Returns page before passed page with id
|
||||
*
|
||||
* @param {string} pageId
|
||||
*/
|
||||
getPageBefore(pageId) {
|
||||
if (this.order === undefined) {
|
||||
return;
|
||||
}
|
||||
const currentPageInOrder = this.order.indexOf(pageId);
|
||||
/**
|
||||
* If page not found or first return nothing
|
||||
*/
|
||||
if (currentPageInOrder <= 0) {
|
||||
return;
|
||||
}
|
||||
return this.order[currentPageInOrder - 1];
|
||||
}
|
||||
/**
|
||||
* Returns page before passed page with id
|
||||
*
|
||||
* @param pageId
|
||||
*/
|
||||
getPageAfter(pageId) {
|
||||
if (this.order === undefined) {
|
||||
return;
|
||||
}
|
||||
const currentPageInOrder = this.order.indexOf(pageId);
|
||||
/**
|
||||
* If page not found or is last
|
||||
*/
|
||||
if (currentPageInOrder === -1 || currentPageInOrder === this.order.length - 1) {
|
||||
return;
|
||||
}
|
||||
return this.order[currentPageInOrder + 1];
|
||||
}
|
||||
/**
|
||||
* @param {string[]} order - define new order
|
||||
*/
|
||||
set order(order) {
|
||||
this._order = order;
|
||||
}
|
||||
/**
|
||||
* Returns ordered list
|
||||
*
|
||||
* @returns {string[]}
|
||||
*/
|
||||
get order() {
|
||||
return this._order || [];
|
||||
}
|
||||
/**
|
||||
* Save or update page data in the database
|
||||
*/
|
||||
save() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (!this._id) {
|
||||
const insertedRow = yield db.insert(this.data);
|
||||
if (!(insertedRow instanceof Error)) {
|
||||
this._id = insertedRow._id;
|
||||
}
|
||||
}
|
||||
else {
|
||||
yield db.update({ _id: this._id }, this.data);
|
||||
}
|
||||
return this;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Remove page data from the database
|
||||
*/
|
||||
destroy() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
yield db.remove({ _id: this._id });
|
||||
delete this._id;
|
||||
return this;
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove page data from the database
|
||||
*/
|
||||
async destroy() {
|
||||
await db.remove({ _id: this._id });
|
||||
|
||||
delete this._id;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PageOrder;
|
||||
exports.default = PageOrder;
|
||||
|
|
250
src/models/pageOrder.ts
Normal file
250
src/models/pageOrder.ts
Normal file
|
@ -0,0 +1,250 @@
|
|||
import database from '../utils/database/index';
|
||||
|
||||
const db = database['pagesOrder'];
|
||||
|
||||
/**
|
||||
* @typedef {object} PageOrderData
|
||||
* @property {string} _id - row unique id
|
||||
* @property {string} page - page id
|
||||
* @property {Array<string>} order - list of ordered pages
|
||||
*/
|
||||
|
||||
export interface PageOrderData {
|
||||
_id?: string;
|
||||
page?: string;
|
||||
order?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @class PageOrder
|
||||
* @classdesc PageOrder
|
||||
*
|
||||
* Creates order for Pages with children
|
||||
*/
|
||||
class PageOrder {
|
||||
_id?: string;
|
||||
page?: string;
|
||||
_order?: string[];
|
||||
/**
|
||||
* Returns current Page's children order
|
||||
*
|
||||
* @param {string} pageId - page's id
|
||||
* @returns {PageOrder}
|
||||
*/
|
||||
static async get(pageId: string) {
|
||||
const order = await db.findOne({ page: pageId });
|
||||
|
||||
let data = {} as PageOrderData;
|
||||
|
||||
if (order instanceof Error || order === null) {
|
||||
data.page = pageId;
|
||||
} else {
|
||||
data = order;
|
||||
}
|
||||
|
||||
return new PageOrder(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all pages which match passed query object
|
||||
*
|
||||
* @param {object} query
|
||||
* @returns {Promise<PageOrder[]>}
|
||||
*/
|
||||
static async getAll(query: object = {}): Promise<PageOrder[]> {
|
||||
const docs = await db.find(query);
|
||||
|
||||
if (docs === null || docs instanceof Error) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Promise.all(docs.map(doc => new PageOrder(doc)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
* @param {PageOrderData} data
|
||||
*/
|
||||
constructor(data: PageOrderData = {}) {
|
||||
if (data === null) {
|
||||
data = {};
|
||||
}
|
||||
|
||||
if (data._id) {
|
||||
this._id = data._id;
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* constructor data setter
|
||||
*
|
||||
* @param {PageOrderData} pageOrderData
|
||||
*/
|
||||
set data(pageOrderData: PageOrderData) {
|
||||
this.page = pageOrderData.page || '0';
|
||||
this.order = pageOrderData.order || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Page Children order
|
||||
*
|
||||
* @returns {PageOrderData}
|
||||
*/
|
||||
get data(): PageOrderData {
|
||||
return {
|
||||
_id: this._id,
|
||||
page: '' + this.page,
|
||||
order: this.order,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes page id to the orders array
|
||||
*
|
||||
* @param {string} pageId - page's id
|
||||
*/
|
||||
push(pageId: string) {
|
||||
if (typeof pageId === 'string') {
|
||||
if (this.order === undefined) {
|
||||
this.order = []
|
||||
}
|
||||
this.order.push(pageId);
|
||||
} else {
|
||||
throw new Error('given id is not string');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes page id from orders array
|
||||
*
|
||||
* @param {string} pageId - page's id
|
||||
*/
|
||||
remove(pageId: string) {
|
||||
if (this.order === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const found = this.order.indexOf(pageId);
|
||||
|
||||
if (found >= 0) {
|
||||
this.order.splice(found, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} currentPageId - page's id that changes the order
|
||||
* @param {string} putAbovePageId - page's id above which we put the target page
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
putAbove(currentPageId: string, putAbovePageId: string) {
|
||||
if (this.order === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const found1 = this.order.indexOf(putAbovePageId);
|
||||
const found2 = this.order.indexOf(currentPageId);
|
||||
|
||||
if (found1 === -1 || found2 === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const margin = found1 < found2 ? 1 : 0;
|
||||
|
||||
this.order.splice(found1, 0, currentPageId);
|
||||
this.order.splice(found2 + margin, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns page before passed page with id
|
||||
*
|
||||
* @param {string} pageId
|
||||
*/
|
||||
getPageBefore(pageId: string) {
|
||||
if (this.order === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPageInOrder = this.order.indexOf(pageId);
|
||||
|
||||
/**
|
||||
* If page not found or first return nothing
|
||||
*/
|
||||
if (currentPageInOrder <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.order[currentPageInOrder - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns page before passed page with id
|
||||
*
|
||||
* @param pageId
|
||||
*/
|
||||
getPageAfter(pageId: string) {
|
||||
if (this.order === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPageInOrder = this.order.indexOf(pageId);
|
||||
|
||||
/**
|
||||
* If page not found or is last
|
||||
*/
|
||||
if (currentPageInOrder === -1 || currentPageInOrder === this.order.length - 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
return this.order[currentPageInOrder + 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string[]} order - define new order
|
||||
*/
|
||||
set order(order: string[]) {
|
||||
this._order = order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ordered list
|
||||
*
|
||||
* @returns {string[]}
|
||||
*/
|
||||
get order(): string[] {
|
||||
return this._order || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Save or update page data in the database
|
||||
*/
|
||||
async save() {
|
||||
if (!this._id) {
|
||||
const insertedRow = await db.insert(this.data) as { _id: string};
|
||||
|
||||
if (!(insertedRow instanceof Error)) {
|
||||
this._id = insertedRow._id;
|
||||
}
|
||||
} else {
|
||||
await db.update({ _id: this._id }, this.data);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove page data from the database
|
||||
*/
|
||||
async destroy() {
|
||||
await db.remove({ _id: this._id });
|
||||
|
||||
delete this._id;
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export default PageOrder;
|
|
@ -1,5 +1,19 @@
|
|||
const { password: db } = require('../utils/database/index');
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const index_1 = __importDefault(require("../utils/database/index"));
|
||||
const db = index_1.default['password'];
|
||||
/**
|
||||
* @class User
|
||||
* @class User model
|
||||
|
@ -7,30 +21,28 @@ const { password: db } = require('../utils/database/index');
|
|||
* @property {string} passHash - hashed password
|
||||
*/
|
||||
class User {
|
||||
/**
|
||||
* Find and return model of user.
|
||||
* User is only one.
|
||||
*
|
||||
* @returns {Promise<User>}
|
||||
*/
|
||||
static async get() {
|
||||
const data = await db.findOne({});
|
||||
|
||||
if (!data) {
|
||||
return null;
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
* @param {UserData} userData
|
||||
*/
|
||||
constructor(userData) {
|
||||
this.passHash = userData.passHash;
|
||||
}
|
||||
/**
|
||||
* Find and return model of user.
|
||||
* User is only one.
|
||||
*
|
||||
* @returns {Promise<User>}
|
||||
*/
|
||||
static get() {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const data = yield db.findOne({});
|
||||
if (data instanceof Error || data === null) {
|
||||
return new Error('User not found');
|
||||
}
|
||||
return new User(data);
|
||||
});
|
||||
}
|
||||
|
||||
return new User(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
* @param {object} userData
|
||||
*/
|
||||
constructor(userData) {
|
||||
this.passHash = userData.passHash;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = User;
|
||||
exports.default = User;
|
||||
|
|
43
src/models/user.ts
Normal file
43
src/models/user.ts
Normal file
|
@ -0,0 +1,43 @@
|
|||
import database from '../utils/database/index';
|
||||
|
||||
const db = database['password'];
|
||||
|
||||
interface UserData {
|
||||
passHash: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class User
|
||||
* @class User model
|
||||
*
|
||||
* @property {string} passHash - hashed password
|
||||
*/
|
||||
class User {
|
||||
passHash: string;
|
||||
/**
|
||||
* Find and return model of user.
|
||||
* User is only one.
|
||||
*
|
||||
* @returns {Promise<User>}
|
||||
*/
|
||||
static async get(): Promise<User|Error> {
|
||||
const data = await db.findOne({});
|
||||
|
||||
if (data instanceof Error || data === null) {
|
||||
return new Error('User not found');
|
||||
}
|
||||
|
||||
return new User(data as UserData);
|
||||
}
|
||||
|
||||
/**
|
||||
* @class
|
||||
*
|
||||
* @param {UserData} userData
|
||||
*/
|
||||
constructor(userData: UserData) {
|
||||
this.passHash = userData.passHash;
|
||||
}
|
||||
}
|
||||
|
||||
export default User;
|
|
@ -1,45 +1,56 @@
|
|||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const Aliases = require('../controllers/aliases');
|
||||
const Pages = require('../controllers/pages');
|
||||
const Alias = require('../models/alias');
|
||||
const verifyToken = require('./middlewares/token');
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = __importDefault(require("express"));
|
||||
const aliases_1 = __importDefault(require("../controllers/aliases"));
|
||||
const pages_1 = __importDefault(require("../controllers/pages"));
|
||||
const alias_1 = __importDefault(require("../models/alias"));
|
||||
const token_1 = __importDefault(require("./middlewares/token"));
|
||||
const router = express_1.default.Router();
|
||||
/**
|
||||
* GET /*
|
||||
*
|
||||
* Return document with given alias
|
||||
*/
|
||||
router.get('*', verifyToken, async (req, res) => {
|
||||
try {
|
||||
let url = req.originalUrl.slice(1); // Cuts first '/' character
|
||||
const queryParamsIndex = url.indexOf('?');
|
||||
|
||||
if (queryParamsIndex !== -1) {
|
||||
url = url.slice(0, queryParamsIndex); // Cuts off query params
|
||||
router.get('*', token_1.default, (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
try {
|
||||
let url = req.originalUrl.slice(1); // Cuts first '/' character
|
||||
const queryParamsIndex = url.indexOf('?');
|
||||
if (queryParamsIndex !== -1) {
|
||||
url = url.slice(0, queryParamsIndex); // Cuts off query params
|
||||
}
|
||||
const alias = yield aliases_1.default.get(url);
|
||||
if (alias.id === undefined) {
|
||||
throw new Error("Alias not found");
|
||||
}
|
||||
switch (alias.type) {
|
||||
case alias_1.default.types.PAGE: {
|
||||
const page = yield pages_1.default.get(alias.id);
|
||||
const pageParent = page.parent;
|
||||
res.render('pages/page', {
|
||||
page,
|
||||
pageParent,
|
||||
config: req.app.locals.config,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const alias = await Aliases.get(url);
|
||||
|
||||
switch (alias.type) {
|
||||
case Alias.types.PAGE: {
|
||||
const page = await Pages.get(alias.id);
|
||||
|
||||
const pageParent = await page.parent;
|
||||
|
||||
res.render('pages/page', {
|
||||
page,
|
||||
pageParent,
|
||||
config: req.app.locals.config,
|
||||
catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
}));
|
||||
exports.default = router;
|
||||
|
|
50
src/routes/aliases.ts
Normal file
50
src/routes/aliases.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
import express, { Request, Response } from 'express';
|
||||
import Aliases from '../controllers/aliases';
|
||||
import Pages from '../controllers/pages';
|
||||
import Alias from '../models/alias';
|
||||
import verifyToken from './middlewares/token';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
/**
|
||||
* GET /*
|
||||
*
|
||||
* Return document with given alias
|
||||
*/
|
||||
router.get('*', verifyToken, async (req: Request, res: Response) => {
|
||||
try {
|
||||
let url = req.originalUrl.slice(1); // Cuts first '/' character
|
||||
const queryParamsIndex = url.indexOf('?');
|
||||
|
||||
if (queryParamsIndex !== -1) {
|
||||
url = url.slice(0, queryParamsIndex); // Cuts off query params
|
||||
}
|
||||
|
||||
const alias = await Aliases.get(url);
|
||||
|
||||
if (alias.id === undefined){
|
||||
throw new Error("Alias not found");
|
||||
}
|
||||
|
||||
switch (alias.type) {
|
||||
case Alias.types.PAGE: {
|
||||
const page = await Pages.get(alias.id);
|
||||
|
||||
const pageParent = page.parent;
|
||||
|
||||
res.render('pages/page', {
|
||||
page,
|
||||
pageParent,
|
||||
config: req.app.locals.config,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
|
@ -1,12 +1,14 @@
|
|||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
const pagesAPI = require('./pages');
|
||||
const transportAPI = require('./transport');
|
||||
const linksAPI = require('./links');
|
||||
|
||||
router.use('/', pagesAPI);
|
||||
router.use('/', transportAPI);
|
||||
router.use('/', linksAPI);
|
||||
|
||||
module.exports = router;
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = __importDefault(require("express"));
|
||||
const router = express_1.default.Router();
|
||||
const pages_1 = __importDefault(require("./pages"));
|
||||
const transport_1 = __importDefault(require("./transport"));
|
||||
const links_1 = __importDefault(require("./links"));
|
||||
router.use('/', pages_1.default);
|
||||
router.use('/', transport_1.default);
|
||||
router.use('/', links_1.default);
|
||||
exports.default = router;
|
||||
|
|
12
src/routes/api/index.ts
Normal file
12
src/routes/api/index.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import express from 'express';
|
||||
const router = express.Router();
|
||||
|
||||
import pagesAPI from './pages';
|
||||
import transportAPI from './transport';
|
||||
import linksAPI from './links';
|
||||
|
||||
router.use('/', pagesAPI);
|
||||
router.use('/', transportAPI);
|
||||
router.use('/', linksAPI);
|
||||
|
||||
export default router;
|
|
@ -1,38 +1,56 @@
|
|||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const ogs = require('open-graph-scraper');
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = __importDefault(require("express"));
|
||||
const open_graph_scraper_1 = __importDefault(require("open-graph-scraper"));
|
||||
const router = express_1.default.Router();
|
||||
/**
|
||||
* Accept file url to fetch
|
||||
*/
|
||||
router.get('/fetchUrl', async (req, res) => {
|
||||
const response = {
|
||||
success: 0
|
||||
};
|
||||
|
||||
if (!req.query.url) {
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const linkData = (await ogs({ url: req.query.url })).result;
|
||||
|
||||
response.success = 1;
|
||||
response.meta = {
|
||||
title: linkData.ogTitle,
|
||||
description: linkData.ogDescription,
|
||||
site_name: linkData.ogSiteName,
|
||||
image: {
|
||||
url: linkData.ogImage.url
|
||||
}
|
||||
router.get('/fetchUrl', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const response = {
|
||||
success: 0
|
||||
};
|
||||
|
||||
res.status(200).json(response);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
res.status(500).json(response);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
if (!req.query.url) {
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
if (typeof req.query.url !== 'string') {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const linkData = (yield open_graph_scraper_1.default({ url: req.query.url })).result;
|
||||
if (!linkData.success) {
|
||||
return;
|
||||
}
|
||||
response.success = 1;
|
||||
response.meta = {
|
||||
title: linkData.ogTitle,
|
||||
description: linkData.ogDescription,
|
||||
site_name: linkData.ogSiteName,
|
||||
image: {
|
||||
url: undefined
|
||||
}
|
||||
};
|
||||
if (linkData.ogImage !== undefined) {
|
||||
response.meta.image = { url: linkData.ogImage.toString() };
|
||||
}
|
||||
res.status(200).json(response);
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
res.status(500).json(response);
|
||||
}
|
||||
}));
|
||||
exports.default = router;
|
||||
|
|
61
src/routes/api/links.ts
Normal file
61
src/routes/api/links.ts
Normal file
|
@ -0,0 +1,61 @@
|
|||
import express, { Request, Response } from 'express';
|
||||
import ogs from 'open-graph-scraper';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
interface ResponseData {
|
||||
success: number;
|
||||
meta?: {
|
||||
title: string | undefined;
|
||||
description: string | undefined;
|
||||
site_name: string | undefined;
|
||||
image: { url: string | undefined }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept file url to fetch
|
||||
*/
|
||||
router.get('/fetchUrl', async (req: Request, res: Response) => {
|
||||
const response: ResponseData = {
|
||||
success: 0
|
||||
};
|
||||
|
||||
if (!req.query.url) {
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof req.query.url !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const linkData = (await ogs({ url: req.query.url })).result;
|
||||
|
||||
if (!linkData.success) {
|
||||
return;
|
||||
}
|
||||
|
||||
response.success = 1;
|
||||
response.meta = {
|
||||
title: linkData.ogTitle,
|
||||
description: linkData.ogDescription,
|
||||
site_name: linkData.ogSiteName,
|
||||
image: {
|
||||
url: undefined
|
||||
}
|
||||
};
|
||||
|
||||
if (linkData.ogImage !== undefined) {
|
||||
response.meta.image = { url: linkData.ogImage.toString()}
|
||||
}
|
||||
|
||||
res.status(200).json(response);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
res.status(500).json(response);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
|
@ -1,177 +1,192 @@
|
|||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const multer = require('multer')();
|
||||
const Pages = require('../../controllers/pages');
|
||||
const PagesOrder = require('../../controllers/pagesOrder');
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = __importDefault(require("express"));
|
||||
const pages_1 = __importDefault(require("../../controllers/pages"));
|
||||
const pagesOrder_1 = __importDefault(require("../../controllers/pagesOrder"));
|
||||
const multer_1 = __importDefault(require("multer"));
|
||||
const router = express_1.default.Router();
|
||||
const multer = multer_1.default();
|
||||
/**
|
||||
* GET /page/:id
|
||||
*
|
||||
* Return PageData of page with given id
|
||||
*/
|
||||
|
||||
router.get('/page/:id', async (req, res) => {
|
||||
try {
|
||||
const page = await Pages.get(req.params.id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
result: page.data
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/page/:id', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
try {
|
||||
const page = yield pages_1.default.get(req.params.id);
|
||||
res.json({
|
||||
success: true,
|
||||
result: page.data
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
}));
|
||||
/**
|
||||
* GET /pages
|
||||
*
|
||||
* Return PageData for all pages
|
||||
*/
|
||||
router.get('/pages', async (req, res) => {
|
||||
try {
|
||||
const pages = await Pages.getAll();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
result: pages
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/pages', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
try {
|
||||
const pages = yield pages_1.default.getAll();
|
||||
res.json({
|
||||
success: true,
|
||||
result: pages
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
}));
|
||||
/**
|
||||
* PUT /page
|
||||
*
|
||||
* Create new page in the database
|
||||
*/
|
||||
router.put('/page', multer.none(), async (req, res) => {
|
||||
try {
|
||||
const { title, body, parent } = req.body;
|
||||
const page = await Pages.insert({ title, body, parent });
|
||||
|
||||
/** push to the orders array */
|
||||
await PagesOrder.push(parent, page._id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
result: page
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.put('/page', multer.none(), (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
try {
|
||||
const { title, body, parent } = req.body;
|
||||
const page = yield pages_1.default.insert({ title, body, parent });
|
||||
if (page._id === undefined) {
|
||||
throw new Error("Page not found");
|
||||
}
|
||||
/** push to the orders array */
|
||||
yield pagesOrder_1.default.push(parent, page._id);
|
||||
res.json({
|
||||
success: true,
|
||||
result: page
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
}));
|
||||
/**
|
||||
* POST /page/:id
|
||||
*
|
||||
* Update page data in the database
|
||||
*/
|
||||
router.post('/page/:id', multer.none(), async (req, res) => {
|
||||
const { id } = req.params;
|
||||
|
||||
try {
|
||||
const { title, body, parent, putAbovePageId, uri } = req.body;
|
||||
const pages = await Pages.getAll();
|
||||
let page = await Pages.get(id);
|
||||
|
||||
if (page._parent !== parent) {
|
||||
await PagesOrder.move(page._parent, parent, id);
|
||||
} else {
|
||||
if (putAbovePageId && putAbovePageId !== '0') {
|
||||
const unordered = pages.filter(_page => _page._parent === page._parent).map(_page => _page._id);
|
||||
|
||||
await PagesOrder.update(unordered, page._id, page._parent, putAbovePageId);
|
||||
}
|
||||
router.post('/page/:id', multer.none(), (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const { id } = req.params;
|
||||
try {
|
||||
const { title, body, parent, putAbovePageId, uri } = req.body;
|
||||
const pages = yield pages_1.default.getAll();
|
||||
let page = yield pages_1.default.get(id);
|
||||
if (page._id === undefined) {
|
||||
throw new Error("Page not found");
|
||||
}
|
||||
if (page._parent !== parent) {
|
||||
yield pagesOrder_1.default.move(page._parent, parent, id);
|
||||
}
|
||||
else {
|
||||
if (putAbovePageId && putAbovePageId !== '0') {
|
||||
const unordered = pages.filter(_page => _page._parent === page._parent).map(_page => _page._id);
|
||||
const unOrdered = [];
|
||||
unordered.forEach((item, index) => {
|
||||
if (typeof item === 'string') {
|
||||
unOrdered.push(item);
|
||||
}
|
||||
});
|
||||
yield pagesOrder_1.default.update(unOrdered, page._id, page._parent, putAbovePageId);
|
||||
}
|
||||
}
|
||||
page = yield pages_1.default.update(id, { title, body, parent, uri });
|
||||
res.json({
|
||||
success: true,
|
||||
result: page
|
||||
});
|
||||
}
|
||||
|
||||
page = await Pages.update(id, { title, body, parent, uri });
|
||||
res.json({
|
||||
success: true,
|
||||
result: page
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
}));
|
||||
/**
|
||||
* DELETE /page/:id
|
||||
*
|
||||
* Remove page from the database
|
||||
*/
|
||||
router.delete('/page/:id', async (req, res) => {
|
||||
try {
|
||||
const pageId = req.params.id;
|
||||
const page = await Pages.get(pageId);
|
||||
const parentPageOrder = await PagesOrder.get(page._parent);
|
||||
const pageBeforeId = parentPageOrder.getPageBefore(page._id);
|
||||
const pageAfterId = parentPageOrder.getPageAfter(page._id);
|
||||
|
||||
let pageToRedirect;
|
||||
|
||||
if (pageBeforeId) {
|
||||
pageToRedirect = await Pages.get(pageBeforeId);
|
||||
} else if (pageAfterId) {
|
||||
pageToRedirect = await Pages.get(pageAfterId);
|
||||
} else {
|
||||
pageToRedirect = page._parent !== '0' ? await Pages.get(page._parent) : null;
|
||||
router.delete('/page/:id', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
try {
|
||||
const pageId = req.params.id;
|
||||
const page = yield pages_1.default.get(pageId);
|
||||
if (page._id === undefined) {
|
||||
throw new Error("Page not found");
|
||||
}
|
||||
const parentPageOrder = yield pagesOrder_1.default.get(page._parent);
|
||||
const pageBeforeId = parentPageOrder.getPageBefore(page._id);
|
||||
const pageAfterId = parentPageOrder.getPageAfter(page._id);
|
||||
let pageToRedirect;
|
||||
if (pageBeforeId) {
|
||||
pageToRedirect = yield pages_1.default.get(pageBeforeId);
|
||||
}
|
||||
else if (pageAfterId) {
|
||||
pageToRedirect = yield pages_1.default.get(pageAfterId);
|
||||
}
|
||||
else {
|
||||
pageToRedirect = page._parent !== '0' ? yield pages_1.default.get(page._parent) : null;
|
||||
}
|
||||
/**
|
||||
* remove current page and go deeper to remove children with orders
|
||||
*
|
||||
* @param {string} startFrom
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const deleteRecursively = (startFrom) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
let order = [];
|
||||
try {
|
||||
const children = yield pagesOrder_1.default.get(startFrom);
|
||||
order = children.order;
|
||||
}
|
||||
catch (e) { }
|
||||
order.forEach((id) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
yield deleteRecursively(id);
|
||||
}));
|
||||
yield pages_1.default.remove(startFrom);
|
||||
try {
|
||||
yield pagesOrder_1.default.remove(startFrom);
|
||||
}
|
||||
catch (e) { }
|
||||
});
|
||||
yield deleteRecursively(req.params.id);
|
||||
// remove also from parent's order
|
||||
parentPageOrder.remove(req.params.id);
|
||||
yield parentPageOrder.save();
|
||||
res.json({
|
||||
success: true,
|
||||
result: pageToRedirect
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* remove current page and go deeper to remove children with orders
|
||||
*
|
||||
* @param startFrom
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const deleteRecursively = async (startFrom) => {
|
||||
let order = [];
|
||||
|
||||
try {
|
||||
const children = await PagesOrder.get(startFrom);
|
||||
|
||||
order = children.order;
|
||||
} catch (e) {}
|
||||
|
||||
order.forEach(async id => {
|
||||
await deleteRecursively(id);
|
||||
});
|
||||
|
||||
await Pages.remove(startFrom);
|
||||
try {
|
||||
await PagesOrder.remove(startFrom);
|
||||
} catch (e) {}
|
||||
};
|
||||
|
||||
await deleteRecursively(req.params.id);
|
||||
|
||||
// remove also from parent's order
|
||||
parentPageOrder.remove(req.params.id);
|
||||
await parentPageOrder.save();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
result: pageToRedirect
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
}));
|
||||
exports.default = router;
|
||||
|
|
200
src/routes/api/pages.ts
Normal file
200
src/routes/api/pages.ts
Normal file
|
@ -0,0 +1,200 @@
|
|||
import express, { Request, Response } from 'express';
|
||||
import Pages from '../../controllers/pages';
|
||||
import PagesOrder from '../../controllers/pagesOrder';
|
||||
import multerFunc from 'multer';
|
||||
|
||||
const router = express.Router();
|
||||
const multer = multerFunc();
|
||||
|
||||
/**
|
||||
* GET /page/:id
|
||||
*
|
||||
* Return PageData of page with given id
|
||||
*/
|
||||
|
||||
router.get('/page/:id', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const page = await Pages.get(req.params.id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
result: page.data
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /pages
|
||||
*
|
||||
* Return PageData for all pages
|
||||
*/
|
||||
router.get('/pages', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const pages = await Pages.getAll();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
result: pages
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* PUT /page
|
||||
*
|
||||
* Create new page in the database
|
||||
*/
|
||||
router.put('/page', multer.none(), async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { title, body, parent } = req.body;
|
||||
const page = await Pages.insert({ title, body, parent });
|
||||
|
||||
if (page._id === undefined) {
|
||||
throw new Error("Page not found");
|
||||
}
|
||||
|
||||
/** push to the orders array */
|
||||
await PagesOrder.push(parent, page._id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
result: page
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /page/:id
|
||||
*
|
||||
* Update page data in the database
|
||||
*/
|
||||
router.post('/page/:id', multer.none(), async (req: Request, res: Response) => {
|
||||
const { id } = req.params;
|
||||
|
||||
try {
|
||||
const { title, body, parent, putAbovePageId, uri } = req.body;
|
||||
const pages = await Pages.getAll();
|
||||
let page = await Pages.get(id);
|
||||
|
||||
if (page._id === undefined) {
|
||||
throw new Error("Page not found");
|
||||
}
|
||||
|
||||
if (page._parent !== parent) {
|
||||
await PagesOrder.move(page._parent, parent, id);
|
||||
} else {
|
||||
if (putAbovePageId && putAbovePageId !== '0') {
|
||||
const unordered = pages.filter(_page => _page._parent === page._parent).map(_page => _page._id);
|
||||
|
||||
const unOrdered: string[] = [];
|
||||
|
||||
unordered.forEach((item, index) => {
|
||||
if (typeof item === 'string') {
|
||||
unOrdered.push(item);
|
||||
}
|
||||
})
|
||||
|
||||
await PagesOrder.update(unOrdered, page._id, page._parent, putAbovePageId);
|
||||
}
|
||||
}
|
||||
|
||||
page = await Pages.update(id, { title, body, parent, uri });
|
||||
res.json({
|
||||
success: true,
|
||||
result: page
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* DELETE /page/:id
|
||||
*
|
||||
* Remove page from the database
|
||||
*/
|
||||
router.delete('/page/:id', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const pageId = req.params.id;
|
||||
const page = await Pages.get(pageId);
|
||||
|
||||
if (page._id === undefined) {
|
||||
throw new Error("Page not found");
|
||||
}
|
||||
|
||||
const parentPageOrder = await PagesOrder.get(page._parent);
|
||||
const pageBeforeId = parentPageOrder.getPageBefore(page._id);
|
||||
const pageAfterId = parentPageOrder.getPageAfter(page._id);
|
||||
|
||||
let pageToRedirect;
|
||||
|
||||
if (pageBeforeId) {
|
||||
pageToRedirect = await Pages.get(pageBeforeId);
|
||||
} else if (pageAfterId) {
|
||||
pageToRedirect = await Pages.get(pageAfterId);
|
||||
} else {
|
||||
pageToRedirect = page._parent !== '0' ? await Pages.get(page._parent) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove current page and go deeper to remove children with orders
|
||||
*
|
||||
* @param {string} startFrom
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const deleteRecursively = async (startFrom: string) => {
|
||||
let order: string[] = [];
|
||||
|
||||
try {
|
||||
const children = await PagesOrder.get(startFrom);
|
||||
|
||||
order = children.order;
|
||||
} catch (e) {}
|
||||
|
||||
order.forEach(async id => {
|
||||
await deleteRecursively(id);
|
||||
});
|
||||
|
||||
await Pages.remove(startFrom);
|
||||
try {
|
||||
await PagesOrder.remove(startFrom);
|
||||
} catch (e) {}
|
||||
};
|
||||
|
||||
await deleteRecursively(req.params.id);
|
||||
|
||||
// remove also from parent's order
|
||||
parentPageOrder.remove(req.params.id);
|
||||
await parentPageOrder.save();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
result: pageToRedirect
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err.message
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
|
@ -1,119 +1,113 @@
|
|||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const multer = require('multer');
|
||||
const mime = require('mime');
|
||||
const mkdirp = require('mkdirp');
|
||||
const Transport = require('../../controllers/transport');
|
||||
const { random16 } = require('../../utils/crypto');
|
||||
const config = require('../../../config');
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = __importDefault(require("express"));
|
||||
const multer_1 = __importDefault(require("multer"));
|
||||
const mime_1 = __importDefault(require("mime"));
|
||||
const mkdirp_1 = __importDefault(require("mkdirp"));
|
||||
const transport_1 = __importDefault(require("../../controllers/transport"));
|
||||
const crypto_1 = __importDefault(require("../../utils/crypto"));
|
||||
const config_1 = __importDefault(require("config"));
|
||||
const router = express_1.default.Router();
|
||||
const random16 = crypto_1.default.random16;
|
||||
/**
|
||||
* Multer storage for uploaded files and images
|
||||
* @type {DiskStorage|DiskStorage}
|
||||
*/
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
const dir = config.uploads || 'public/uploads';
|
||||
|
||||
mkdirp(dir, err => cb(err, dir));
|
||||
},
|
||||
filename: async (req, file, cb) => {
|
||||
const filename = await random16();
|
||||
|
||||
cb(null, `${filename}.${mime.getExtension(file.mimetype)}`);
|
||||
}
|
||||
const storage = multer_1.default.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
const dir = config_1.default.get('uploads') || 'public/uploads';
|
||||
// mkdirp(dir, err => cb(err, dir));
|
||||
mkdirp_1.default(dir);
|
||||
},
|
||||
filename: (req, file, cb) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const filename = yield random16();
|
||||
cb(null, `${filename}.${mime_1.default.extension(file.mimetype)}`);
|
||||
})
|
||||
});
|
||||
|
||||
/**
|
||||
* Multer middleware for image uploading
|
||||
*/
|
||||
const imageUploader = multer({
|
||||
storage,
|
||||
fileFilter: (req, file, cb) => {
|
||||
if (!/image/.test(file.mimetype) && !/video\/mp4/.test(file.mimetype)) {
|
||||
cb(null, false);
|
||||
return;
|
||||
const imageUploader = multer_1.default({
|
||||
storage,
|
||||
fileFilter: (req, file, cb) => {
|
||||
if (!/image/.test(file.mimetype) && !/video\/mp4/.test(file.mimetype)) {
|
||||
cb(null, false);
|
||||
return;
|
||||
}
|
||||
cb(null, true);
|
||||
}
|
||||
|
||||
cb(null, true);
|
||||
}
|
||||
}).fields([ { name: 'image', maxCount: 1 } ]);
|
||||
|
||||
}).fields([{ name: 'image', maxCount: 1 }]);
|
||||
/**
|
||||
* Multer middleware for file uploading
|
||||
*/
|
||||
const fileUploader = multer({
|
||||
storage
|
||||
}).fields([ { name: 'file', maxCount: 1 } ]);
|
||||
|
||||
const fileUploader = multer_1.default({
|
||||
storage
|
||||
}).fields([{ name: 'file', maxCount: 1 }]);
|
||||
/**
|
||||
* Accepts images to upload
|
||||
*/
|
||||
router.post('/transport/image', imageUploader, async (req, res) => {
|
||||
let response = { success: 0 };
|
||||
|
||||
if (!req.files || !req.files.image) {
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Object.assign(
|
||||
response,
|
||||
await Transport.save(req.files.image[0], req.body.map ? JSON.parse(req.body.map) : undefined)
|
||||
);
|
||||
|
||||
response.success = 1;
|
||||
res.status(200).json(response);
|
||||
} catch (e) {
|
||||
res.status(500).json(response);
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/transport/image', imageUploader, (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
let response = { success: 0 };
|
||||
if (!req.files || Array.isArray(req.files) || !req.files.image) {
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Object.assign(response, yield transport_1.default.save(req.files.image[0], req.body.map ? JSON.parse(req.body.map) : undefined));
|
||||
response.success = 1;
|
||||
res.status(200).json(response);
|
||||
}
|
||||
catch (e) {
|
||||
res.status(500).json(response);
|
||||
}
|
||||
}));
|
||||
/**
|
||||
* Accepts files to upload
|
||||
*/
|
||||
router.post('/transport/file', fileUploader, async (req, res) => {
|
||||
let response = { success: 0 };
|
||||
|
||||
if (!req.files || !req.files.file) {
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Object.assign(
|
||||
response,
|
||||
await Transport.save(req.files.file[0], req.body.map ? JSON.parse(req.body.map) : undefined)
|
||||
);
|
||||
|
||||
response.success = 1;
|
||||
res.status(200).json(response);
|
||||
} catch (e) {
|
||||
res.status(500).json(response);
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/transport/file', fileUploader, (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
let response = { success: 0 };
|
||||
if (!req.files || Array.isArray(req.files) || !req.files.file) {
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Object.assign(response, yield transport_1.default.save(req.files.file[0], req.body.map ? JSON.parse(req.body.map) : undefined));
|
||||
response.success = 1;
|
||||
res.status(200).json(response);
|
||||
}
|
||||
catch (e) {
|
||||
res.status(500).json(response);
|
||||
}
|
||||
}));
|
||||
/**
|
||||
* Accept file url to fetch
|
||||
*/
|
||||
router.post('/transport/fetch', multer().none(), async (req, res) => {
|
||||
let response = { success: 0 };
|
||||
|
||||
if (!req.body.url) {
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Object.assign(response, await Transport.fetch(req.body.url, req.body.map ? JSON.parse(req.body.map) : undefined));
|
||||
|
||||
response.success = 1;
|
||||
res.status(200).json(response);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
res.status(500).json(response);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
router.post('/transport/fetch', multer_1.default().none(), (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
let response = { success: 0 };
|
||||
if (!req.body.url) {
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Object.assign(response, yield transport_1.default.fetch(req.body.url, req.body.map ? JSON.parse(req.body.map) : undefined));
|
||||
response.success = 1;
|
||||
res.status(200).json(response);
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
res.status(500).json(response);
|
||||
}
|
||||
}));
|
||||
exports.default = router;
|
||||
|
|
122
src/routes/api/transport.ts
Normal file
122
src/routes/api/transport.ts
Normal file
|
@ -0,0 +1,122 @@
|
|||
import express, { Request, Response, Express } from 'express';
|
||||
import multer from 'multer';
|
||||
import mime from 'mime';
|
||||
import mkdirp from 'mkdirp';
|
||||
import Transport from '../../controllers/transport';
|
||||
import crypto from '../../utils/crypto';
|
||||
import config from 'config';
|
||||
|
||||
const router = express.Router();
|
||||
const random16 = crypto.random16;
|
||||
|
||||
/**
|
||||
* Multer storage for uploaded files and images
|
||||
* @type {DiskStorage|DiskStorage}
|
||||
*/
|
||||
const storage = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
const dir: string = config.get('uploads') || 'public/uploads';
|
||||
|
||||
// mkdirp(dir, err => cb(err, dir));
|
||||
mkdirp(dir);
|
||||
},
|
||||
filename: async (req, file, cb) => {
|
||||
const filename = await random16();
|
||||
|
||||
cb(null, `${filename}.${mime.extension(file.mimetype)}`);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Multer middleware for image uploading
|
||||
*/
|
||||
const imageUploader = multer({
|
||||
storage,
|
||||
fileFilter: (req, file, cb) => {
|
||||
if (!/image/.test(file.mimetype) && !/video\/mp4/.test(file.mimetype)) {
|
||||
cb(null, false);
|
||||
return;
|
||||
}
|
||||
|
||||
cb(null, true);
|
||||
}
|
||||
}).fields([ { name: 'image', maxCount: 1 } ]);
|
||||
|
||||
/**
|
||||
* Multer middleware for file uploading
|
||||
*/
|
||||
const fileUploader = multer({
|
||||
storage
|
||||
}).fields([ { name: 'file', maxCount: 1 } ]);
|
||||
|
||||
/**
|
||||
* Accepts images to upload
|
||||
*/
|
||||
router.post('/transport/image', imageUploader, async (req: Request, res: Response) => {
|
||||
let response = { success: 0 };
|
||||
|
||||
if (!req.files || Array.isArray(req.files) ||!req.files.image) {
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Object.assign(
|
||||
response,
|
||||
await Transport.save(req.files.image[0], req.body.map ? JSON.parse(req.body.map) : undefined)
|
||||
);
|
||||
|
||||
response.success = 1;
|
||||
res.status(200).json(response);
|
||||
} catch (e) {
|
||||
res.status(500).json(response);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Accepts files to upload
|
||||
*/
|
||||
router.post('/transport/file', fileUploader, async (req: Request, res: Response) => {
|
||||
let response = { success: 0 };
|
||||
|
||||
if (!req.files || Array.isArray(req.files) || !req.files.file) {
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Object.assign(
|
||||
response,
|
||||
await Transport.save(req.files.file[0], req.body.map ? JSON.parse(req.body.map) : undefined)
|
||||
);
|
||||
|
||||
response.success = 1;
|
||||
res.status(200).json(response);
|
||||
} catch (e) {
|
||||
res.status(500).json(response);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Accept file url to fetch
|
||||
*/
|
||||
router.post('/transport/fetch', multer().none(), async (req: Request, res: Response) => {
|
||||
let response = { success: 0 };
|
||||
|
||||
if (!req.body.url) {
|
||||
res.status(400).json(response);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Object.assign(response, await Transport.fetch(req.body.url, req.body.map ? JSON.parse(req.body.map) : undefined));
|
||||
|
||||
response.success = 1;
|
||||
res.status(200).json(response);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
res.status(500).json(response);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
|
@ -1,63 +1,68 @@
|
|||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
require('dotenv').config();
|
||||
|
||||
const express = require('express');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const router = express.Router();
|
||||
const Users = require('../controllers/users');
|
||||
const config = require('../../config/index');
|
||||
const bcrypt = require('bcrypt');
|
||||
const csrf = require('csurf');
|
||||
const csrfProtection = csrf({ cookie: true });
|
||||
const parseForm = express.urlencoded({ extended: false });
|
||||
|
||||
const express_1 = __importDefault(require("express"));
|
||||
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
||||
const users_1 = __importDefault(require("../controllers/users"));
|
||||
const config_1 = __importDefault(require("config"));
|
||||
const bcrypt_1 = __importDefault(require("bcrypt"));
|
||||
const csurf_1 = __importDefault(require("csurf"));
|
||||
const router = express_1.default.Router();
|
||||
const csrfProtection = csurf_1.default({ cookie: true });
|
||||
const parseForm = express_1.default.urlencoded({ extended: false });
|
||||
/**
|
||||
* Authorization page
|
||||
*/
|
||||
router.get('/auth', csrfProtection, function (req, res) {
|
||||
res.render('auth', {
|
||||
title: 'Login page',
|
||||
csrfToken: req.csrfToken(),
|
||||
});
|
||||
res.render('auth', {
|
||||
title: 'Login page',
|
||||
csrfToken: req.csrfToken(),
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Process given password
|
||||
*/
|
||||
router.post('/auth', parseForm, csrfProtection, async (req, res) => {
|
||||
const userDoc = await Users.get();
|
||||
|
||||
if (!userDoc) {
|
||||
res.render('auth', {
|
||||
title: 'Login page',
|
||||
header: 'Password not set',
|
||||
csrfToken: req.csrfToken(),
|
||||
});
|
||||
}
|
||||
|
||||
const passHash = userDoc.passHash;
|
||||
|
||||
bcrypt.compare(req.body.password, passHash, async (err, result) => {
|
||||
if (err || result === false) {
|
||||
res.render('auth', {
|
||||
title: 'Login page',
|
||||
header: 'Wrong password',
|
||||
csrfToken: req.csrfToken(),
|
||||
});
|
||||
router.post('/auth', parseForm, csrfProtection, (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const userDoc = yield users_1.default.get();
|
||||
if (!userDoc || userDoc instanceof Error) {
|
||||
res.render('auth', {
|
||||
title: 'Login page',
|
||||
header: 'Password not set',
|
||||
csrfToken: req.csrfToken(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const token = jwt.sign({
|
||||
iss: 'Codex Team',
|
||||
sub: 'auth',
|
||||
iat: Date.now(),
|
||||
}, passHash + config.secret);
|
||||
|
||||
res.cookie('authToken', token, {
|
||||
httpOnly: true,
|
||||
expires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year
|
||||
});
|
||||
|
||||
res.redirect('/');
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
const passHash = userDoc.passHash;
|
||||
bcrypt_1.default.compare(req.body.password, passHash, (err, result) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
if (err || result === false) {
|
||||
res.render('auth', {
|
||||
title: 'Login page',
|
||||
header: 'Wrong password',
|
||||
csrfToken: req.csrfToken(),
|
||||
});
|
||||
}
|
||||
const token = jsonwebtoken_1.default.sign({
|
||||
iss: 'Codex Team',
|
||||
sub: 'auth',
|
||||
iat: Date.now(),
|
||||
}, passHash + config_1.default.get('secret'));
|
||||
res.cookie('authToken', token, {
|
||||
httpOnly: true,
|
||||
expires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year
|
||||
});
|
||||
res.redirect('/');
|
||||
}));
|
||||
}));
|
||||
exports.default = router;
|
||||
|
|
65
src/routes/auth.ts
Normal file
65
src/routes/auth.ts
Normal file
|
@ -0,0 +1,65 @@
|
|||
require('dotenv').config();
|
||||
|
||||
import express, { Request, Response } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import Users from '../controllers/users';
|
||||
import config from 'config';
|
||||
import bcrypt from 'bcrypt';
|
||||
import csrf from 'csurf';
|
||||
|
||||
const router = express.Router();
|
||||
const csrfProtection = csrf({ cookie: true });
|
||||
const parseForm = express.urlencoded({ extended: false });
|
||||
|
||||
/**
|
||||
* Authorization page
|
||||
*/
|
||||
router.get('/auth', csrfProtection, function (req: Request, res: Response) {
|
||||
res.render('auth', {
|
||||
title: 'Login page',
|
||||
csrfToken: req.csrfToken(),
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Process given password
|
||||
*/
|
||||
router.post('/auth', parseForm, csrfProtection, async (req: Request, res: Response) => {
|
||||
const userDoc = await Users.get();
|
||||
|
||||
if (!userDoc || userDoc instanceof Error) {
|
||||
res.render('auth', {
|
||||
title: 'Login page',
|
||||
header: 'Password not set',
|
||||
csrfToken: req.csrfToken(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const passHash = userDoc.passHash;
|
||||
|
||||
bcrypt.compare(req.body.password, passHash, async (err, result) => {
|
||||
if (err || result === false) {
|
||||
res.render('auth', {
|
||||
title: 'Login page',
|
||||
header: 'Wrong password',
|
||||
csrfToken: req.csrfToken(),
|
||||
});
|
||||
}
|
||||
|
||||
const token = jwt.sign({
|
||||
iss: 'Codex Team',
|
||||
sub: 'auth',
|
||||
iat: Date.now(),
|
||||
}, passHash + config.get('secret'));
|
||||
|
||||
res.cookie('authToken', token, {
|
||||
httpOnly: true,
|
||||
expires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year
|
||||
});
|
||||
|
||||
res.redirect('/');
|
||||
});
|
||||
});
|
||||
|
||||
export default router;
|
|
@ -1,15 +1,26 @@
|
|||
const express = require('express');
|
||||
const verifyToken = require('./middlewares/token');
|
||||
const router = express.Router();
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = __importDefault(require("express"));
|
||||
const token_1 = __importDefault(require("./middlewares/token"));
|
||||
const router = express_1.default.Router();
|
||||
/* GET home page. */
|
||||
router.get('/', verifyToken, async (req, res) => {
|
||||
const config = req.app.locals.config;
|
||||
|
||||
if (config.startPage) {
|
||||
return res.redirect(config.startPage);
|
||||
}
|
||||
res.render('pages/index', { isAuthorized: res.locals.isAuthorized });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
router.get('/', token_1.default, (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const config = req.app.locals.config;
|
||||
if (config.startPage) {
|
||||
return res.redirect(config.startPage);
|
||||
}
|
||||
res.render('pages/index', { isAuthorized: res.locals.isAuthorized });
|
||||
}));
|
||||
exports.default = router;
|
||||
|
|
16
src/routes/home.ts
Normal file
16
src/routes/home.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import express, { Request, Response } from 'express';
|
||||
import verifyToken from './middlewares/token';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
/* GET home page. */
|
||||
router.get('/', verifyToken, async (req: Request, res: Response) => {
|
||||
const config = req.app.locals.config;
|
||||
|
||||
if (config.startPage) {
|
||||
return res.redirect(config.startPage);
|
||||
}
|
||||
res.render('pages/index', { isAuthorized: res.locals.isAuthorized });
|
||||
});
|
||||
|
||||
export default router;
|
|
@ -1,18 +1,19 @@
|
|||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
const home = require('./home');
|
||||
const pages = require('./pages');
|
||||
const auth = require('./auth');
|
||||
const aliases = require('./aliases');
|
||||
const api = require('./api');
|
||||
|
||||
const pagesMiddleware = require('./middlewares/pages');
|
||||
|
||||
router.use('/', pagesMiddleware, home);
|
||||
router.use('/', pagesMiddleware, pages);
|
||||
router.use('/', pagesMiddleware, auth);
|
||||
router.use('/api', api);
|
||||
router.use('/', aliases);
|
||||
|
||||
module.exports = router;
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = __importDefault(require("express"));
|
||||
const router = express_1.default.Router();
|
||||
const home_1 = __importDefault(require("./home"));
|
||||
const pages_1 = __importDefault(require("./pages"));
|
||||
const auth_1 = __importDefault(require("./auth"));
|
||||
const aliases_1 = __importDefault(require("./aliases"));
|
||||
const api_1 = __importDefault(require("./api"));
|
||||
const pages_2 = __importDefault(require("./middlewares/pages"));
|
||||
router.use('/', pages_2.default, home_1.default);
|
||||
router.use('/', pages_2.default, pages_1.default);
|
||||
router.use('/', pages_2.default, auth_1.default);
|
||||
router.use('/api', api_1.default);
|
||||
router.use('/', aliases_1.default);
|
||||
exports.default = router;
|
||||
|
|
18
src/routes/index.ts
Normal file
18
src/routes/index.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import express from 'express';
|
||||
const router = express.Router();
|
||||
|
||||
import home from './home';
|
||||
import pages from './pages';
|
||||
import auth from './auth';
|
||||
import aliases from './aliases';
|
||||
import api from './api';
|
||||
|
||||
import pagesMiddleware from './middlewares/pages';
|
||||
|
||||
router.use('/', pagesMiddleware, home);
|
||||
router.use('/', pagesMiddleware, pages);
|
||||
router.use('/', pagesMiddleware, auth);
|
||||
router.use('/api', api);
|
||||
router.use('/', aliases);
|
||||
|
||||
export default router;
|
|
@ -1,13 +1,18 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
/**
|
||||
* Middleware for checking locals.isAuthorized property, which allows to edit/create pages
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
*/
|
||||
module.exports = function allowEdit(req, res, next) {
|
||||
if (res.locals.isAuthorized) {
|
||||
next();
|
||||
} else {
|
||||
res.redirect('/auth');
|
||||
}
|
||||
};
|
||||
function allowEdit(req, res, next) {
|
||||
if (res.locals.isAuthorized) {
|
||||
next();
|
||||
}
|
||||
else {
|
||||
res.redirect('/auth');
|
||||
}
|
||||
}
|
||||
exports.default = allowEdit;
|
||||
;
|
||||
|
|
15
src/routes/middlewares/locals.ts
Normal file
15
src/routes/middlewares/locals.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { NextFunction, Request, Response } from "express";
|
||||
|
||||
/**
|
||||
* Middleware for checking locals.isAuthorized property, which allows to edit/create pages
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
*/
|
||||
export default function allowEdit(req: Request, res: Response, next: NextFunction) {
|
||||
if (res.locals.isAuthorized) {
|
||||
next();
|
||||
} else {
|
||||
res.redirect('/auth');
|
||||
}
|
||||
};
|
|
@ -1,7 +1,20 @@
|
|||
const Pages = require('../../controllers/pages');
|
||||
const PagesOrder = require('../../controllers/pagesOrder');
|
||||
const asyncMiddleware = require('../../utils/asyncMiddleware');
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const pages_1 = __importDefault(require("../../controllers/pages"));
|
||||
const pagesOrder_1 = __importDefault(require("../../controllers/pagesOrder"));
|
||||
const asyncMiddleware_1 = __importDefault(require("../../utils/asyncMiddleware"));
|
||||
/**
|
||||
* Process one-level pages list to parent-children list
|
||||
*
|
||||
|
@ -14,61 +27,54 @@ const asyncMiddleware = require('../../utils/asyncMiddleware');
|
|||
* @return {Page[]}
|
||||
*/
|
||||
function createMenuTree(parentPageId, pages, pagesOrder, level = 1, currentLevel = 1) {
|
||||
const childrenOrder = pagesOrder.find(order => order.data.page === parentPageId);
|
||||
|
||||
/**
|
||||
* branch is a page children in tree
|
||||
* if we got some children order on parents tree, then we push found pages in order sequence
|
||||
* otherwise just find all pages includes parent tree
|
||||
*/
|
||||
let ordered = [];
|
||||
|
||||
if (childrenOrder) {
|
||||
ordered = childrenOrder.order.map(pageId => {
|
||||
return pages.find(page => page._id === pageId);
|
||||
const childrenOrder = pagesOrder.find(order => order.data.page === parentPageId);
|
||||
/**
|
||||
* branch is a page children in tree
|
||||
* if we got some children order on parents tree, then we push found pages in order sequence
|
||||
* otherwise just find all pages includes parent tree
|
||||
*/
|
||||
let ordered = [];
|
||||
if (childrenOrder) {
|
||||
ordered = childrenOrder.order.map((pageId) => {
|
||||
return pages.find(page => page._id === pageId);
|
||||
});
|
||||
}
|
||||
const unordered = pages.filter(page => page._parent === parentPageId);
|
||||
const branch = Array.from(new Set([...ordered, ...unordered]));
|
||||
/**
|
||||
* stop recursion when we got the passed max level
|
||||
*/
|
||||
if (currentLevel === level + 1) {
|
||||
return [];
|
||||
}
|
||||
/**
|
||||
* Each parents children can have subbranches
|
||||
*/
|
||||
return branch.filter(page => page && page._id).map(page => {
|
||||
return Object.assign({
|
||||
children: createMenuTree(page._id, pages, pagesOrder, level, currentLevel + 1)
|
||||
}, page.data);
|
||||
});
|
||||
}
|
||||
|
||||
const unordered = pages.filter(page => page._parent === parentPageId);
|
||||
const branch = [ ...new Set([...ordered, ...unordered]) ];
|
||||
|
||||
/**
|
||||
* stop recursion when we got the passed max level
|
||||
*/
|
||||
if (currentLevel === level + 1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Each parents children can have subbranches
|
||||
*/
|
||||
return branch.filter(page => page && page._id).map(page => {
|
||||
return Object.assign({
|
||||
children: createMenuTree(page._id, pages, pagesOrder, level, currentLevel + 1)
|
||||
}, page.data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware for all /page/... routes
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
module.exports = asyncMiddleware(async (req, res, next) => {
|
||||
/**
|
||||
* Pages without parent
|
||||
* @type {string}
|
||||
*/
|
||||
const parentIdOfRootPages = '0';
|
||||
|
||||
try {
|
||||
const pages = await Pages.getAll();
|
||||
const pagesOrder = await PagesOrder.getAll();
|
||||
|
||||
res.locals.menu = createMenuTree(parentIdOfRootPages, pages, pagesOrder, 2);
|
||||
} catch (error) {
|
||||
console.log('Can not load menu:', error);
|
||||
}
|
||||
next();
|
||||
});
|
||||
exports.default = asyncMiddleware_1.default((req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
/**
|
||||
* Pages without parent
|
||||
* @type {string}
|
||||
*/
|
||||
const parentIdOfRootPages = '0';
|
||||
try {
|
||||
const pages = yield pages_1.default.getAll();
|
||||
const pagesOrder = yield pagesOrder_1.default.getAll();
|
||||
res.locals.menu = createMenuTree(parentIdOfRootPages, pages, pagesOrder, 2);
|
||||
}
|
||||
catch (error) {
|
||||
console.log('Can not load menu:', error);
|
||||
}
|
||||
next();
|
||||
}));
|
||||
|
|
77
src/routes/middlewares/pages.ts
Normal file
77
src/routes/middlewares/pages.ts
Normal file
|
@ -0,0 +1,77 @@
|
|||
import { NextFunction, Request, Response } from 'express';
|
||||
import Pages from '../../controllers/pages';
|
||||
import PagesOrder from '../../controllers/pagesOrder';
|
||||
import Page from '../../models/page';
|
||||
import asyncMiddleware from '../../utils/asyncMiddleware';
|
||||
import PageOrder from '../../models/pageOrder';
|
||||
|
||||
/**
|
||||
* Process one-level pages list to parent-children list
|
||||
*
|
||||
* @param {string} parentPageId - parent page id
|
||||
* @param {Page[]} pages - list of all available pages
|
||||
* @param {PagesOrder[]} pagesOrder - list of pages order
|
||||
* @param {number} level
|
||||
* @param {number} currentLevel
|
||||
*
|
||||
* @return {Page[]}
|
||||
*/
|
||||
function createMenuTree(parentPageId: string, pages: Page[], pagesOrder: PageOrder[], level: number = 1, currentLevel: number = 1): Page[] {
|
||||
const childrenOrder = pagesOrder.find(order => order.data.page === parentPageId);
|
||||
|
||||
/**
|
||||
* branch is a page children in tree
|
||||
* if we got some children order on parents tree, then we push found pages in order sequence
|
||||
* otherwise just find all pages includes parent tree
|
||||
*/
|
||||
let ordered: any = [];
|
||||
|
||||
if (childrenOrder) {
|
||||
ordered = childrenOrder.order.map((pageId: string) => {
|
||||
return pages.find(page => page._id === pageId);
|
||||
});
|
||||
}
|
||||
|
||||
const unordered = pages.filter(page => page._parent === parentPageId);
|
||||
const branch = Array.from(new Set([...ordered, ...unordered]));
|
||||
|
||||
/**
|
||||
* stop recursion when we got the passed max level
|
||||
*/
|
||||
if (currentLevel === level + 1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Each parents children can have subbranches
|
||||
*/
|
||||
return branch.filter(page => page && page._id).map(page => {
|
||||
return Object.assign({
|
||||
children: createMenuTree(page._id, pages, pagesOrder, level, currentLevel + 1)
|
||||
}, page.data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware for all /page/... routes
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
export default asyncMiddleware(async (req: Request, res: Response, next: NextFunction) => {
|
||||
/**
|
||||
* Pages without parent
|
||||
* @type {string}
|
||||
*/
|
||||
const parentIdOfRootPages: string = '0';
|
||||
|
||||
try {
|
||||
const pages = await Pages.getAll();
|
||||
const pagesOrder = await PagesOrder.getAll();
|
||||
|
||||
res.locals.menu = createMenuTree(parentIdOfRootPages, pages, pagesOrder, 2);
|
||||
} catch (error) {
|
||||
console.log('Can not load menu:', error);
|
||||
}
|
||||
next();
|
||||
});
|
|
@ -1,26 +1,45 @@
|
|||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
require('dotenv').config();
|
||||
const config = require('../../../config/index');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const Users = require('../../controllers/users');
|
||||
|
||||
const config_1 = __importDefault(require("config"));
|
||||
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
||||
const users_1 = __importDefault(require("../../controllers/users"));
|
||||
/**
|
||||
* Middleware for checking jwt token
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
*/
|
||||
module.exports = async function verifyToken(req, res, next) {
|
||||
let token = req.cookies.authToken;
|
||||
const userDoc = await Users.get();
|
||||
|
||||
if (!userDoc) {
|
||||
res.locals.isAuthorized = false;
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
jwt.verify(token, userDoc.passHash + config.secret, (err, decodedToken) => {
|
||||
res.locals.isAuthorized = !(err || !decodedToken);
|
||||
next();
|
||||
});
|
||||
};
|
||||
function verifyToken(req, res, next) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let token = req.cookies.authToken;
|
||||
const userDoc = yield users_1.default.get();
|
||||
if (!userDoc || userDoc instanceof Error) {
|
||||
res.locals.isAuthorized = false;
|
||||
next();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const decodedToken = jsonwebtoken_1.default.verify(token, userDoc.passHash + config_1.default.get('secret'));
|
||||
res.locals.isAuthorized = !!decodedToken;
|
||||
}
|
||||
catch (e) {
|
||||
res.locals.isAuthorized = false;
|
||||
}
|
||||
next();
|
||||
});
|
||||
}
|
||||
exports.default = verifyToken;
|
||||
;
|
||||
|
|
30
src/routes/middlewares/token.ts
Normal file
30
src/routes/middlewares/token.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
require('dotenv').config();
|
||||
import config from 'config';
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import Users from '../../controllers/users';
|
||||
|
||||
/**
|
||||
* Middleware for checking jwt token
|
||||
* @param req
|
||||
* @param res
|
||||
* @param next
|
||||
*/
|
||||
export default async function verifyToken(req: Request, res: Response, next: NextFunction) {
|
||||
let token = req.cookies.authToken;
|
||||
const userDoc = await Users.get();
|
||||
|
||||
if (!userDoc || userDoc instanceof Error) {
|
||||
res.locals.isAuthorized = false;
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
try{
|
||||
const decodedToken = jwt.verify(token, userDoc.passHash + config.get('secret'));
|
||||
res.locals.isAuthorized = !! decodedToken;
|
||||
} catch (e) {
|
||||
res.locals.isAuthorized = false;
|
||||
}
|
||||
next();
|
||||
};
|
|
@ -1,65 +1,70 @@
|
|||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const Pages = require('../controllers/pages');
|
||||
const PagesOrder = require('../controllers/pagesOrder');
|
||||
|
||||
const verifyToken = require('./middlewares/token');
|
||||
const allowEdit = require('./middlewares/locals');
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const express_1 = __importDefault(require("express"));
|
||||
const pages_1 = __importDefault(require("../controllers/pages"));
|
||||
const pagesOrder_1 = __importDefault(require("../controllers/pagesOrder"));
|
||||
const token_1 = __importDefault(require("./middlewares/token"));
|
||||
const locals_1 = __importDefault(require("./middlewares/locals"));
|
||||
const router = express_1.default.Router();
|
||||
/**
|
||||
* Create new page form
|
||||
*/
|
||||
router.get('/page/new', verifyToken, allowEdit, async (req, res, next) => {
|
||||
const pagesAvailable = await Pages.getAll();
|
||||
|
||||
res.render('pages/form', {
|
||||
pagesAvailable,
|
||||
page: null,
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/page/new', token_1.default, locals_1.default, (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const pagesAvailable = yield pages_1.default.getAll();
|
||||
res.render('pages/form', {
|
||||
pagesAvailable,
|
||||
page: null,
|
||||
});
|
||||
}));
|
||||
/**
|
||||
* Edit page form
|
||||
*/
|
||||
router.get('/page/edit/:id', verifyToken, allowEdit, async (req, res, next) => {
|
||||
const pageId = req.params.id;
|
||||
|
||||
try {
|
||||
const page = await Pages.get(pageId);
|
||||
const pagesAvailable = await Pages.getAllExceptChildren(pageId);
|
||||
const parentsChildrenOrdered = await PagesOrder.getOrderedChildren(pagesAvailable, pageId, page._parent, true);
|
||||
|
||||
res.render('pages/form', {
|
||||
page,
|
||||
parentsChildrenOrdered,
|
||||
pagesAvailable,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(404);
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/page/edit/:id', token_1.default, locals_1.default, (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const pageId = req.params.id;
|
||||
try {
|
||||
const page = yield pages_1.default.get(pageId);
|
||||
const pagesAvailable = yield pages_1.default.getAllExceptChildren(pageId);
|
||||
const parentsChildrenOrdered = yield pagesOrder_1.default.getOrderedChildren(pagesAvailable, pageId, page._parent, true);
|
||||
res.render('pages/form', {
|
||||
page,
|
||||
parentsChildrenOrdered,
|
||||
pagesAvailable,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
res.status(404);
|
||||
next(error);
|
||||
}
|
||||
}));
|
||||
/**
|
||||
* View page
|
||||
*/
|
||||
router.get('/page/:id', verifyToken, async (req, res, next) => {
|
||||
const pageId = req.params.id;
|
||||
|
||||
try {
|
||||
const page = await Pages.get(pageId);
|
||||
|
||||
const pageParent = await page.parent;
|
||||
|
||||
res.render('pages/page', {
|
||||
page,
|
||||
pageParent,
|
||||
config: req.app.locals.config,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(404);
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
router.get('/page/:id', token_1.default, (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
|
||||
const pageId = req.params.id;
|
||||
try {
|
||||
const page = yield pages_1.default.get(pageId);
|
||||
const pageParent = yield page.parent;
|
||||
res.render('pages/page', {
|
||||
page,
|
||||
pageParent,
|
||||
config: req.app.locals.config,
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
res.status(404);
|
||||
next(error);
|
||||
}
|
||||
}));
|
||||
exports.default = router;
|
||||
|
|
65
src/routes/pages.ts
Normal file
65
src/routes/pages.ts
Normal file
|
@ -0,0 +1,65 @@
|
|||
import express, { NextFunction, Request, Response } from 'express';
|
||||
import Pages from '../controllers/pages';
|
||||
import PagesOrder from '../controllers/pagesOrder';
|
||||
import verifyToken from './middlewares/token';
|
||||
import allowEdit from './middlewares/locals';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
/**
|
||||
* Create new page form
|
||||
*/
|
||||
router.get('/page/new', verifyToken, allowEdit, async (req: Request, res: Response, next: NextFunction) => {
|
||||
const pagesAvailable = await Pages.getAll();
|
||||
|
||||
res.render('pages/form', {
|
||||
pagesAvailable,
|
||||
page: null,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Edit page form
|
||||
*/
|
||||
router.get('/page/edit/:id', verifyToken, allowEdit, async (req: Request, res: Response, next: NextFunction) => {
|
||||
const pageId = req.params.id;
|
||||
|
||||
try {
|
||||
const page = await Pages.get(pageId);
|
||||
const pagesAvailable = await Pages.getAllExceptChildren(pageId);
|
||||
const parentsChildrenOrdered = await PagesOrder.getOrderedChildren(pagesAvailable, pageId, page._parent, true);
|
||||
|
||||
res.render('pages/form', {
|
||||
page,
|
||||
parentsChildrenOrdered,
|
||||
pagesAvailable,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(404);
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* View page
|
||||
*/
|
||||
router.get('/page/:id', verifyToken, async (req: Request, res: Response, next: NextFunction) => {
|
||||
const pageId = req.params.id;
|
||||
|
||||
try {
|
||||
const page = await Pages.get(pageId);
|
||||
|
||||
const pageParent = await page.parent;
|
||||
|
||||
res.render('pages/page', {
|
||||
page,
|
||||
pageParent,
|
||||
config: req.app.locals.config,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(404);
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
|
@ -1,12 +1,16 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
/**
|
||||
* Helper for making async middlewares for express router
|
||||
*
|
||||
* @param fn
|
||||
* @returns {function(*=, *=, *=)}
|
||||
*/
|
||||
module.exports = function asyncMiddleware(fn) {
|
||||
return (req, res, next) => {
|
||||
Promise.resolve(fn(req, res, next))
|
||||
.catch(next);
|
||||
};
|
||||
};
|
||||
function asyncMiddleware(fn) {
|
||||
return (req, res, next) => {
|
||||
Promise.resolve(fn(req, res, next))
|
||||
.catch(next);
|
||||
};
|
||||
}
|
||||
exports.default = asyncMiddleware;
|
||||
;
|
||||
|
|
14
src/utils/asyncMiddleware.ts
Normal file
14
src/utils/asyncMiddleware.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { NextFunction, Request, Response } from "express";
|
||||
|
||||
/**
|
||||
* Helper for making async middlewares for express router
|
||||
*
|
||||
* @param fn
|
||||
* @returns {function(*=, *=, *=)}
|
||||
*/
|
||||
export default function asyncMiddleware(fn: Function): (...params: any) => void {
|
||||
return (req: Request, res: Response, next: NextFunction) => {
|
||||
Promise.resolve(fn(req, res, next))
|
||||
.catch(next);
|
||||
};
|
||||
};
|
|
@ -1,5 +1,17 @@
|
|||
const crypto = require('crypto');
|
||||
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const crypto_1 = __importDefault(require("crypto"));
|
||||
/**
|
||||
*
|
||||
* @param {string} hexStr - input hex string
|
||||
* @returns {string} - output binary string
|
||||
*/
|
||||
function hexToBinary(hexStr) {
|
||||
return (parseInt(hexStr, 16).toString(2)).padStart(8, '0');
|
||||
}
|
||||
/**
|
||||
* Create binary md5
|
||||
*
|
||||
|
@ -7,29 +19,26 @@ const crypto = require('crypto');
|
|||
* @returns {string} - binary hash of argument
|
||||
*/
|
||||
function binaryMD5(stringToHash) {
|
||||
return crypto.createHash('md5')
|
||||
.update(stringToHash)
|
||||
.digest('binary');
|
||||
return hexToBinary(crypto_1.default.createHash('md5')
|
||||
.update(stringToHash)
|
||||
.digest('hex'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 16 random bytes in hex format
|
||||
*
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function random16() {
|
||||
return new Promise((resolve, reject) => {
|
||||
crypto.randomBytes(16, (err, raw) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve(raw.toString('hex'));
|
||||
return new Promise((resolve, reject) => {
|
||||
crypto_1.default.randomBytes(16, (err, raw) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(raw.toString('hex'));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
binaryMD5,
|
||||
random16,
|
||||
exports.default = {
|
||||
binaryMD5,
|
||||
random16,
|
||||
};
|
||||
|
|
44
src/utils/crypto.ts
Normal file
44
src/utils/crypto.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
import crypto from "crypto";
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} hexStr - input hex string
|
||||
* @returns {string} - output binary string
|
||||
*/
|
||||
function hexToBinary(hexStr: string): string {
|
||||
return (parseInt(hexStr, 16).toString(2)).padStart(8, '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create binary md5
|
||||
*
|
||||
* @param stringToHash - string to hash
|
||||
* @returns {string} - binary hash of argument
|
||||
*/
|
||||
function binaryMD5(stringToHash: string): string {
|
||||
return hexToBinary(crypto.createHash('md5')
|
||||
.update(stringToHash)
|
||||
.digest('hex'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns 16 random bytes in hex format
|
||||
*
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
function random16(): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
crypto.randomBytes(16, (err, raw) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve(raw.toString('hex'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
binaryMD5,
|
||||
random16,
|
||||
};
|
|
@ -1,6 +1,9 @@
|
|||
const Datastore = require('nedb');
|
||||
const config = require('../../../config');
|
||||
|
||||
const db = new Datastore({ filename: `./${config.database}/aliases.db`, autoload: true });
|
||||
|
||||
module.exports = db;
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const nedb_1 = __importDefault(require("nedb"));
|
||||
const config_1 = __importDefault(require("config"));
|
||||
const db = new nedb_1.default({ filename: `./${config_1.default.get('database')}/aliases.db`, autoload: true });
|
||||
exports.default = db;
|
||||
|
|
6
src/utils/database/aliases.ts
Normal file
6
src/utils/database/aliases.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import Datastore from "nedb";
|
||||
import config from "config";
|
||||
|
||||
const db = new Datastore({ filename: `./${config.get('database')}/aliases.db`, autoload: true });
|
||||
|
||||
export default db;
|
|
@ -1,6 +1,9 @@
|
|||
const Datastore = require('nedb');
|
||||
const config = require('../../../config');
|
||||
|
||||
const db = new Datastore({ filename: `./${config.database}/files.db`, autoload: true });
|
||||
|
||||
module.exports = db;
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const nedb_1 = __importDefault(require("nedb"));
|
||||
const config_1 = __importDefault(require("config"));
|
||||
const db = new nedb_1.default({ filename: `./${config_1.default.get('database')}/files.db`, autoload: true });
|
||||
exports.default = db;
|
||||
|
|
6
src/utils/database/files.ts
Normal file
6
src/utils/database/files.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import Datastore from "nedb";
|
||||
import config from "config";
|
||||
|
||||
const db = new Datastore({ filename: `./${config.get('database')}/files.db`, autoload: true });
|
||||
|
||||
export default db;
|
|
@ -1,9 +1,22 @@
|
|||
const pages = require('./pages');
|
||||
const files = require('./files');
|
||||
const password = require('./password');
|
||||
const aliases = require('./aliases');
|
||||
const pagesOrder = require('./pagesOrder');
|
||||
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const pages_1 = __importDefault(require("./pages"));
|
||||
const files_1 = __importDefault(require("./files"));
|
||||
const password_1 = __importDefault(require("./password"));
|
||||
const aliases_1 = __importDefault(require("./aliases"));
|
||||
const pagesOrder_1 = __importDefault(require("./pagesOrder"));
|
||||
/**
|
||||
* @class Database
|
||||
* @classdesc Simple decorator class to work with nedb datastore
|
||||
|
@ -11,144 +24,143 @@ const pagesOrder = require('./pagesOrder');
|
|||
* @property db - nedb Datastore object
|
||||
*/
|
||||
class Database {
|
||||
/**
|
||||
* @constructor
|
||||
*
|
||||
* @param {Object} nedbInstance - nedb Datastore object
|
||||
*/
|
||||
constructor(nedbInstance) {
|
||||
this.db = nedbInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert new document into the database
|
||||
* @see https://github.com/louischatriot/nedb#inserting-documents
|
||||
*
|
||||
* @param {Object} doc - object to insert
|
||||
* @returns {Promise<Object|Error>} - inserted doc or Error object
|
||||
*/
|
||||
async insert(doc) {
|
||||
return new Promise((resolve, reject) => this.db.insert(doc, (err, newDoc) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve(newDoc);
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find documents that match passed query
|
||||
* @see https://github.com/louischatriot/nedb#finding-documents
|
||||
*
|
||||
* @param {Object} query - query object
|
||||
* @param {Object} projection - projection object
|
||||
* @returns {Promise<Array<Object>|Error>} - found docs or Error object
|
||||
*/
|
||||
async find(query, projection) {
|
||||
const cbk = (resolve, reject) => (err, docs) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve(docs);
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (projection) {
|
||||
this.db.find(query, projection, cbk(resolve, reject));
|
||||
} else {
|
||||
this.db.find(query, cbk(resolve, reject));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find one document matches passed query
|
||||
* @see https://github.com/louischatriot/nedb#finding-documents
|
||||
*
|
||||
* @param {Object} query - query object
|
||||
* @param {Object} projection - projection object
|
||||
* @returns {Promise<Object|Error>} - found doc or Error object
|
||||
*/
|
||||
async findOne(query, projection) {
|
||||
const cbk = (resolve, reject) => (err, doc) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve(doc);
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (projection) {
|
||||
this.db.findOne(query, projection, cbk(resolve, reject));
|
||||
} else {
|
||||
this.db.findOne(query, cbk(resolve, reject));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update document matches query
|
||||
* @see https://github.com/louischatriot/nedb#updating-documents
|
||||
*
|
||||
* @param {Object} query - query object
|
||||
* @param {Object} update - fields to update
|
||||
* @param {Object} options
|
||||
* @param {Boolean} options.multi - (false) allows update several documents
|
||||
* @param {Boolean} options.upsert - (false) if true, upsert document with update fields.
|
||||
* Method will return inserted doc or number of affected docs if doc hasn't been inserted
|
||||
* @param {Boolean} options.returnUpdatedDocs - (false) if true, returns affected docs
|
||||
* @returns {Promise<number|Object|Object[]|Error>} - number of updated rows or affected docs or Error object
|
||||
*/
|
||||
async update(query, update, options = {}) {
|
||||
return new Promise((resolve, reject) => this.db.update(query, update, options, (err, result, affectedDocs) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case options.returnUpdatedDocs:
|
||||
resolve(affectedDocs);
|
||||
break;
|
||||
case options.upsert:
|
||||
if (affectedDocs) {
|
||||
resolve(affectedDocs);
|
||||
}
|
||||
resolve(result);
|
||||
break;
|
||||
default:
|
||||
resolve(result);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove document matches passed query
|
||||
* @see https://github.com/louischatriot/nedb#removing-documents
|
||||
*
|
||||
* @param {Object} query - query object
|
||||
* @param {Object} options
|
||||
* @param {Boolean} options.multi - (false) if true, remove several docs
|
||||
* @returns {Promise<number|Error>} - number of removed rows or Error object
|
||||
*/
|
||||
async remove(query, options = {}) {
|
||||
return new Promise((resolve, reject) => this.db.remove(query, options, (err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve(result);
|
||||
}));
|
||||
}
|
||||
/**
|
||||
* @constructor
|
||||
*
|
||||
* @param {Object} nedbInstance - nedb Datastore object
|
||||
*/
|
||||
constructor(nedbInstance) {
|
||||
this.db = nedbInstance;
|
||||
}
|
||||
/**
|
||||
* Insert new document into the database
|
||||
* @see https://github.com/louischatriot/nedb#inserting-documents
|
||||
*
|
||||
* @param {Object} doc - object to insert
|
||||
* @returns {Promise<Object|Error>} - inserted doc or Error object
|
||||
*/
|
||||
insert(doc) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return new Promise((resolve, reject) => this.db.insert(doc, (err, newDoc) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(newDoc);
|
||||
}));
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Find documents that match passed query
|
||||
* @see https://github.com/louischatriot/nedb#finding-documents
|
||||
*
|
||||
* @param {Object} query - query object
|
||||
* @param {Object} projection - projection object
|
||||
* @returns {Promise<Array<Object>|Error>} - found docs or Error object
|
||||
*/
|
||||
find(query, projection) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const cbk = (resolve, reject) => (err, docs) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(docs);
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
if (projection) {
|
||||
this.db.find(query, projection, cbk(resolve, reject));
|
||||
}
|
||||
else {
|
||||
this.db.find(query, cbk(resolve, reject));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Find one document matches passed query
|
||||
* @see https://github.com/louischatriot/nedb#finding-documents
|
||||
*
|
||||
* @param {Object} query - query object
|
||||
* @param {Object} projection - projection object
|
||||
* @returns {Promise<Object|Error>} - found doc or Error object
|
||||
*/
|
||||
findOne(query, projection) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const cbk = (resolve, reject) => (err, doc) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(doc);
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
if (projection) {
|
||||
this.db.findOne(query, projection, cbk(resolve, reject));
|
||||
}
|
||||
else {
|
||||
this.db.findOne(query, cbk(resolve, reject));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Update document matches query
|
||||
* @see https://github.com/louischatriot/nedb#updating-documents
|
||||
*
|
||||
* @param {Object} query - query object
|
||||
* @param {Object} update - fields to update
|
||||
* @param {Object} options
|
||||
* @param {Boolean} options.multi - (false) allows update several documents
|
||||
* @param {Boolean} options.upsert - (false) if true, upsert document with update fields.
|
||||
* Method will return inserted doc or number of affected docs if doc hasn't been inserted
|
||||
* @param {Boolean} options.returnUpdatedDocs - (false) if true, returns affected docs
|
||||
* @returns {Promise<number|Object|Object[]|Error>} - number of updated rows or affected docs or Error object
|
||||
*/
|
||||
update(query, update, options = {}) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return new Promise((resolve, reject) => this.db.update(query, update, options, (err, result, affectedDocs) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
switch (true) {
|
||||
case options.returnUpdatedDocs:
|
||||
resolve(affectedDocs);
|
||||
break;
|
||||
case options.upsert:
|
||||
if (affectedDocs) {
|
||||
resolve(affectedDocs);
|
||||
}
|
||||
resolve(result);
|
||||
break;
|
||||
default:
|
||||
resolve(result);
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Remove document matches passed query
|
||||
* @see https://github.com/louischatriot/nedb#removing-documents
|
||||
*
|
||||
* @param {Object} query - query object
|
||||
* @param {Object} options
|
||||
* @param {Boolean} options.multi - (false) if true, remove several docs
|
||||
* @returns {Promise<number|Error>} - number of removed rows or Error object
|
||||
*/
|
||||
remove(query, options = {}) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
return new Promise((resolve, reject) => this.db.remove(query, options, (err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(result);
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
class: Database,
|
||||
pages: new Database(pages),
|
||||
password: new Database(password),
|
||||
aliases: new Database(aliases),
|
||||
pagesOrder: new Database(pagesOrder),
|
||||
files: new Database(files)
|
||||
exports.default = {
|
||||
class: Database,
|
||||
pages: new Database(pages_1.default),
|
||||
password: new Database(password_1.default),
|
||||
aliases: new Database(aliases_1.default),
|
||||
pagesOrder: new Database(pagesOrder_1.default),
|
||||
files: new Database(files_1.default)
|
||||
};
|
||||
|
|
162
src/utils/database/index.ts
Normal file
162
src/utils/database/index.ts
Normal file
|
@ -0,0 +1,162 @@
|
|||
import pages from './pages';
|
||||
import files from './files';
|
||||
import password from './password';
|
||||
import aliases from './aliases';
|
||||
import pagesOrder from './pagesOrder';
|
||||
import Datastore from 'nedb';
|
||||
|
||||
interface Options {
|
||||
upsert?: boolean;
|
||||
returnUpdatedDocs?: boolean;
|
||||
multi?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @class Database
|
||||
* @classdesc Simple decorator class to work with nedb datastore
|
||||
*
|
||||
* @property db - nedb Datastore object
|
||||
*/
|
||||
class Database {
|
||||
db: Datastore;
|
||||
/**
|
||||
* @constructor
|
||||
*
|
||||
* @param {Object} nedbInstance - nedb Datastore object
|
||||
*/
|
||||
constructor(nedbInstance: Datastore) {
|
||||
this.db = nedbInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert new document into the database
|
||||
* @see https://github.com/louischatriot/nedb#inserting-documents
|
||||
*
|
||||
* @param {Object} doc - object to insert
|
||||
* @returns {Promise<Object|Error>} - inserted doc or Error object
|
||||
*/
|
||||
async insert(doc: object): Promise<object | Error> {
|
||||
return new Promise((resolve, reject) => this.db.insert(doc, (err, newDoc) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve(newDoc);
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find documents that match passed query
|
||||
* @see https://github.com/louischatriot/nedb#finding-documents
|
||||
*
|
||||
* @param {Object} query - query object
|
||||
* @param {Object} projection - projection object
|
||||
* @returns {Promise<Array<Object>|Error>} - found docs or Error object
|
||||
*/
|
||||
async find(query: object, projection?: object): Promise<Array<object> | Error> {
|
||||
const cbk = (resolve: Function, reject: Function) => (err: Error | null, docs: any[]) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve(docs);
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (projection) {
|
||||
this.db.find(query, projection, cbk(resolve, reject));
|
||||
} else {
|
||||
this.db.find(query, cbk(resolve, reject));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find one document matches passed query
|
||||
* @see https://github.com/louischatriot/nedb#finding-documents
|
||||
*
|
||||
* @param {Object} query - query object
|
||||
* @param {Object} projection - projection object
|
||||
* @returns {Promise<Object|Error>} - found doc or Error object
|
||||
*/
|
||||
async findOne(query: object, projection?: object): Promise<object | Error> {
|
||||
const cbk = (resolve: Function, reject: Function) => (err: Error | null, doc: any) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve(doc);
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
if (projection) {
|
||||
this.db.findOne(query, projection, cbk(resolve, reject));
|
||||
} else {
|
||||
this.db.findOne(query, cbk(resolve, reject));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update document matches query
|
||||
* @see https://github.com/louischatriot/nedb#updating-documents
|
||||
*
|
||||
* @param {Object} query - query object
|
||||
* @param {Object} update - fields to update
|
||||
* @param {Object} options
|
||||
* @param {Boolean} options.multi - (false) allows update several documents
|
||||
* @param {Boolean} options.upsert - (false) if true, upsert document with update fields.
|
||||
* Method will return inserted doc or number of affected docs if doc hasn't been inserted
|
||||
* @param {Boolean} options.returnUpdatedDocs - (false) if true, returns affected docs
|
||||
* @returns {Promise<number|Object|Object[]|Error>} - number of updated rows or affected docs or Error object
|
||||
*/
|
||||
async update(query: object, update: object, options: Options = {}) {
|
||||
return new Promise((resolve, reject) => this.db.update(query, update, options, (err, result, affectedDocs) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case options.returnUpdatedDocs:
|
||||
resolve(affectedDocs);
|
||||
break;
|
||||
case options.upsert:
|
||||
if (affectedDocs) {
|
||||
resolve(affectedDocs);
|
||||
}
|
||||
resolve(result);
|
||||
break;
|
||||
default:
|
||||
resolve(result);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove document matches passed query
|
||||
* @see https://github.com/louischatriot/nedb#removing-documents
|
||||
*
|
||||
* @param {Object} query - query object
|
||||
* @param {Object} options
|
||||
* @param {Boolean} options.multi - (false) if true, remove several docs
|
||||
* @returns {Promise<number|Error>} - number of removed rows or Error object
|
||||
*/
|
||||
async remove(query: object, options = {}) {
|
||||
return new Promise((resolve, reject) => this.db.remove(query, options, (err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
|
||||
resolve(result);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
class: Database,
|
||||
pages: new Database(pages),
|
||||
password: new Database(password),
|
||||
aliases: new Database(aliases),
|
||||
pagesOrder: new Database(pagesOrder),
|
||||
files: new Database(files)
|
||||
};
|
|
@ -1,6 +1,9 @@
|
|||
const Datastore = require('nedb');
|
||||
const config = require('../../../config');
|
||||
|
||||
const db = new Datastore({ filename: `./${config.database}/pages.db`, autoload: true });
|
||||
|
||||
module.exports = db;
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const nedb_1 = __importDefault(require("nedb"));
|
||||
const config_1 = __importDefault(require("config"));
|
||||
const db = new nedb_1.default({ filename: `./${config_1.default.get('database')}/pages.db`, autoload: true });
|
||||
exports.default = db;
|
||||
|
|
6
src/utils/database/pages.ts
Normal file
6
src/utils/database/pages.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import Datastore from "nedb";
|
||||
import config from "config";
|
||||
|
||||
const db = new Datastore({ filename: `./${config.get('database')}/pages.db`, autoload: true });
|
||||
|
||||
export default db;
|
|
@ -1,5 +1,9 @@
|
|||
const Datastore = require('nedb');
|
||||
const config = require('../../../config');
|
||||
const db = new Datastore({ filename: `./${config.database}/pagesOrder.db`, autoload: true });
|
||||
|
||||
module.exports = db;
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const nedb_1 = __importDefault(require("nedb"));
|
||||
const config_1 = __importDefault(require("config"));
|
||||
const db = new nedb_1.default({ filename: `./${config_1.default.get('database')}/pagesOrder.db`, autoload: true });
|
||||
exports.default = db;
|
||||
|
|
5
src/utils/database/pagesOrder.ts
Normal file
5
src/utils/database/pagesOrder.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import Datastore from "nedb";
|
||||
import config from "config";
|
||||
const db = new Datastore({ filename: `./${config.get('database')}/pagesOrder.db`, autoload: true });
|
||||
|
||||
export default db;
|
|
@ -1,6 +1,9 @@
|
|||
const Datastore = require('nedb');
|
||||
const config = require('../../../config');
|
||||
|
||||
const db = new Datastore({ filename: `./${config.database}/password.db`, autoload: true });
|
||||
|
||||
module.exports = db;
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const nedb_1 = __importDefault(require("nedb"));
|
||||
const config_1 = __importDefault(require("config"));
|
||||
const db = new nedb_1.default({ filename: `./${config_1.default.get('database')}/password.db`, autoload: true });
|
||||
exports.default = db;
|
||||
|
|
6
src/utils/database/password.ts
Normal file
6
src/utils/database/password.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import Datastore from "nedb";
|
||||
import config from "config";
|
||||
|
||||
const db = new Datastore({ filename: `./${config.get('database')}/password.db`, autoload: true });
|
||||
|
||||
export default db;
|
|
@ -1,3 +1,5 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
/**
|
||||
* Merge to objects recursively
|
||||
*
|
||||
|
@ -6,30 +8,24 @@
|
|||
* @returns {object}
|
||||
*/
|
||||
function deepMerge(target, ...sources) {
|
||||
const isObject = item => item && typeof item === 'object' && !Array.isArray(item);
|
||||
|
||||
if (!sources.length) {
|
||||
return target;
|
||||
}
|
||||
const source = sources.shift();
|
||||
|
||||
if (isObject(target) && isObject(source)) {
|
||||
for (const key in source) {
|
||||
if (isObject(source[key])) {
|
||||
if (!target[key]) {
|
||||
Object.assign(target, { [key]: {} });
|
||||
}
|
||||
|
||||
deepMerge(target[key], source[key]);
|
||||
} else {
|
||||
Object.assign(target, { [key]: source[key] });
|
||||
}
|
||||
const isObject = (item) => item && typeof item === 'object' && !Array.isArray(item);
|
||||
if (!sources.length) {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
return deepMerge(target, ...sources);
|
||||
const source = sources.shift();
|
||||
if (isObject(target) && isObject(source)) {
|
||||
for (const key in source) {
|
||||
if (isObject(source[key])) {
|
||||
if (!target[key]) {
|
||||
Object.assign(target, { [key]: {} });
|
||||
}
|
||||
deepMerge(target[key], source[key]);
|
||||
}
|
||||
else {
|
||||
Object.assign(target, { [key]: source[key] });
|
||||
}
|
||||
}
|
||||
}
|
||||
return deepMerge(target, ...sources);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
deepMerge,
|
||||
};
|
||||
exports.default = deepMerge;
|
||||
|
|
33
src/utils/objects.ts
Normal file
33
src/utils/objects.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* Merge to objects recursively
|
||||
*
|
||||
* @param {object} target
|
||||
* @param {object[]} sources
|
||||
* @returns {object}
|
||||
*/
|
||||
function deepMerge(target: any, ...sources: any[]): object {
|
||||
const isObject = (item: any) => item && typeof item === 'object' && !Array.isArray(item);
|
||||
|
||||
if (!sources.length) {
|
||||
return target;
|
||||
}
|
||||
const source = sources.shift();
|
||||
|
||||
if (isObject(target) && isObject(source)) {
|
||||
for (const key in source) {
|
||||
if (isObject(source[key])) {
|
||||
if (!target[key]) {
|
||||
Object.assign(target, { [key]: {} });
|
||||
}
|
||||
|
||||
deepMerge(target[key], source[key]);
|
||||
} else {
|
||||
Object.assign(target, { [key]: source[key] });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return deepMerge(target, ...sources);
|
||||
}
|
||||
|
||||
export default deepMerge;
|
|
@ -1,8 +1,12 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const config = require('../../config');
|
||||
const rcPath = path.resolve(__dirname, '../../', config.rcFile || './.codexdocsrc');
|
||||
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs_1 = __importDefault(require("fs"));
|
||||
const path_1 = __importDefault(require("path"));
|
||||
const config_1 = __importDefault(require("config"));
|
||||
const rcPath = path_1.default.resolve(__dirname, '../../', config_1.default.get('rcFile') || './.codexdocsrc');
|
||||
/**
|
||||
* @typedef {object} RCData
|
||||
* @property {string} title - website title
|
||||
|
@ -10,100 +14,84 @@ const rcPath = path.resolve(__dirname, '../../', config.rcFile || './.codexdocsr
|
|||
* @property {string} menu[].title - menu option title
|
||||
* @property {string} menu[].uri - menu option href
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class RCParser
|
||||
* @classdesc Class to parse runtime configuration file for CodeX Docs engine
|
||||
*/
|
||||
module.exports = class RCParser {
|
||||
/**
|
||||
* Default CodeX Docs configuration
|
||||
*
|
||||
* @static
|
||||
* @returns {{title: string, menu: Array}}
|
||||
*/
|
||||
static get DEFAULTS() {
|
||||
return {
|
||||
title: 'CodeX Docs',
|
||||
menu: [],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and parse runtime configuration file
|
||||
*
|
||||
* @static
|
||||
* @returns {{title: string, menu: []}}
|
||||
*/
|
||||
static getConfiguration() {
|
||||
if (!fs.existsSync(rcPath)) {
|
||||
return RCParser.DEFAULTS;
|
||||
}
|
||||
|
||||
const file = fs.readFileSync(rcPath, { encoding: 'UTF-8' });
|
||||
const rConfig = RCParser.DEFAULTS;
|
||||
let userConfig;
|
||||
|
||||
try {
|
||||
userConfig = JSON.parse(file);
|
||||
} catch (e) {
|
||||
console.log('CodeX Docs rc file should be in JSON format.');
|
||||
|
||||
return RCParser.DEFAULTS;
|
||||
}
|
||||
|
||||
for (const option in userConfig) {
|
||||
if (Object.prototype.hasOwnProperty.call(userConfig, option)) {
|
||||
rConfig[option] = userConfig[option] || RCParser.DEFAULTS[option] || undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(rConfig.menu instanceof Array)) {
|
||||
console.log('Menu section in the rc file must be an array.');
|
||||
rConfig.menu = RCParser.DEFAULTS.menu;
|
||||
}
|
||||
|
||||
rConfig.menu = rConfig.menu.filter((option, i) => {
|
||||
i = i + 1;
|
||||
if (typeof option === 'string') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!option || option instanceof Array || typeof option !== 'object') {
|
||||
console.log(`Menu option #${i} in rc file must be a string or an object`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const { title, uri } = option;
|
||||
|
||||
if (!title || typeof title !== 'string') {
|
||||
console.log(`Menu option #${i} title must be a string.`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!uri || typeof uri !== 'string') {
|
||||
console.log(`Menu option #${i} uri must be a string.`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
rConfig.menu = rConfig.menu.map(option => {
|
||||
if (typeof option === 'string') {
|
||||
class RCParser {
|
||||
/**
|
||||
* Default CodeX Docs configuration
|
||||
*
|
||||
* @static
|
||||
* @returns {{title: string, menu: Array}}
|
||||
*/
|
||||
static get DEFAULTS() {
|
||||
return {
|
||||
title: option,
|
||||
/* Replace all non alpha- and numeric-symbols with '-' */
|
||||
uri: '/' + option.toLowerCase().replace(/[ -/:-@[-`{-~]+/, '-'),
|
||||
title: 'CodeX Docs',
|
||||
menu: [],
|
||||
};
|
||||
}
|
||||
|
||||
return option;
|
||||
});
|
||||
|
||||
return rConfig;
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Find and parse runtime configuration file
|
||||
*
|
||||
* @static
|
||||
* @returns {{title: string, menu: []}}
|
||||
*/
|
||||
static getConfiguration() {
|
||||
if (!fs_1.default.existsSync(rcPath)) {
|
||||
return RCParser.DEFAULTS;
|
||||
}
|
||||
const file = fs_1.default.readFileSync(rcPath, "utf-8");
|
||||
const rConfig = RCParser.DEFAULTS;
|
||||
let userConfig;
|
||||
try {
|
||||
userConfig = JSON.parse(file);
|
||||
}
|
||||
catch (e) {
|
||||
console.log('CodeX Docs rc file should be in JSON format.');
|
||||
return RCParser.DEFAULTS;
|
||||
}
|
||||
for (const option in userConfig) {
|
||||
if (Object.prototype.hasOwnProperty.call(userConfig, option)) {
|
||||
rConfig[option] = userConfig[option] || RCParser.DEFAULTS[option] || undefined;
|
||||
}
|
||||
}
|
||||
if (!(rConfig.menu instanceof Array)) {
|
||||
console.log('Menu section in the rc file must be an array.');
|
||||
rConfig.menu = RCParser.DEFAULTS.menu;
|
||||
}
|
||||
rConfig.menu = rConfig.menu.filter((option, i) => {
|
||||
i = i + 1;
|
||||
if (typeof option === 'string') {
|
||||
return true;
|
||||
}
|
||||
if (!option || option instanceof Array || typeof option !== 'object') {
|
||||
console.log(`Menu option #${i} in rc file must be a string or an object`);
|
||||
return false;
|
||||
}
|
||||
const { title, uri } = option;
|
||||
if (!title || typeof title !== 'string') {
|
||||
console.log(`Menu option #${i} title must be a string.`);
|
||||
return false;
|
||||
}
|
||||
if (!uri || typeof uri !== 'string') {
|
||||
console.log(`Menu option #${i} uri must be a string.`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
rConfig.menu = rConfig.menu.map((option) => {
|
||||
if (typeof option === 'string') {
|
||||
return {
|
||||
title: option,
|
||||
/* Replace all non alpha- and numeric-symbols with '-' */
|
||||
uri: '/' + option.toLowerCase().replace(/[ -/:-@[-`{-~]+/, '-'),
|
||||
};
|
||||
}
|
||||
return option;
|
||||
});
|
||||
return rConfig;
|
||||
}
|
||||
}
|
||||
exports.default = RCParser;
|
||||
;
|
||||
|
|
114
src/utils/rcparser.ts
Normal file
114
src/utils/rcparser.ts
Normal file
|
@ -0,0 +1,114 @@
|
|||
import fs from "fs";
|
||||
import path from "path";
|
||||
import config from "config";
|
||||
|
||||
const rcPath = path.resolve(__dirname, '../../', config.get('rcFile') || './.codexdocsrc');
|
||||
|
||||
interface RConfig {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} RCData
|
||||
* @property {string} title - website title
|
||||
* @property {object[]} menu - options for website menu
|
||||
* @property {string} menu[].title - menu option title
|
||||
* @property {string} menu[].uri - menu option href
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class RCParser
|
||||
* @classdesc Class to parse runtime configuration file for CodeX Docs engine
|
||||
*/
|
||||
export default class RCParser {
|
||||
/**
|
||||
* Default CodeX Docs configuration
|
||||
*
|
||||
* @static
|
||||
* @returns {{title: string, menu: Array}}
|
||||
*/
|
||||
static get DEFAULTS() {
|
||||
return {
|
||||
title: 'CodeX Docs',
|
||||
menu: [],
|
||||
} as RConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and parse runtime configuration file
|
||||
*
|
||||
* @static
|
||||
* @returns {{title: string, menu: []}}
|
||||
*/
|
||||
static getConfiguration() {
|
||||
if (!fs.existsSync(rcPath)) {
|
||||
return RCParser.DEFAULTS;
|
||||
}
|
||||
|
||||
const file = fs.readFileSync(rcPath, "utf-8");
|
||||
const rConfig = RCParser.DEFAULTS;
|
||||
let userConfig;
|
||||
|
||||
try {
|
||||
userConfig = JSON.parse(file);
|
||||
} catch (e) {
|
||||
console.log('CodeX Docs rc file should be in JSON format.');
|
||||
|
||||
return RCParser.DEFAULTS;
|
||||
}
|
||||
|
||||
for (const option in userConfig) {
|
||||
if (Object.prototype.hasOwnProperty.call(userConfig, option)) {
|
||||
rConfig[option] = userConfig[option] || RCParser.DEFAULTS[option] || undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(rConfig.menu instanceof Array)) {
|
||||
console.log('Menu section in the rc file must be an array.');
|
||||
rConfig.menu = RCParser.DEFAULTS.menu;
|
||||
}
|
||||
|
||||
rConfig.menu = rConfig.menu.filter((option: any, i:number) => {
|
||||
i = i + 1;
|
||||
if (typeof option === 'string') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!option || option instanceof Array || typeof option !== 'object') {
|
||||
console.log(`Menu option #${i} in rc file must be a string or an object`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const { title, uri } = option;
|
||||
|
||||
if (!title || typeof title !== 'string') {
|
||||
console.log(`Menu option #${i} title must be a string.`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!uri || typeof uri !== 'string') {
|
||||
console.log(`Menu option #${i} uri must be a string.`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
rConfig.menu = rConfig.menu.map((option: any) => {
|
||||
if (typeof option === 'string') {
|
||||
return {
|
||||
title: option,
|
||||
/* Replace all non alpha- and numeric-symbols with '-' */
|
||||
uri: '/' + option.toLowerCase().replace(/[ -/:-@[-`{-~]+/, '-'),
|
||||
};
|
||||
}
|
||||
|
||||
return option;
|
||||
});
|
||||
|
||||
return rConfig;
|
||||
}
|
||||
};
|
|
@ -1,70 +1,72 @@
|
|||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const translationTable = {
|
||||
а: 'a',
|
||||
б: 'b',
|
||||
в: 'v',
|
||||
г: 'g',
|
||||
д: 'd',
|
||||
е: 'e',
|
||||
ж: 'g',
|
||||
з: 'z',
|
||||
и: 'i',
|
||||
й: 'y',
|
||||
к: 'k',
|
||||
л: 'l',
|
||||
м: 'm',
|
||||
н: 'n',
|
||||
о: 'o',
|
||||
п: 'p',
|
||||
р: 'r',
|
||||
с: 's',
|
||||
т: 't',
|
||||
у: 'u',
|
||||
ф: 'f',
|
||||
ы: 'i',
|
||||
э: 'e',
|
||||
А: 'A',
|
||||
Б: 'B',
|
||||
В: 'V',
|
||||
Г: 'G',
|
||||
Д: 'D',
|
||||
Е: 'E',
|
||||
Ж: 'G',
|
||||
З: 'Z',
|
||||
И: 'I',
|
||||
Й: 'Y',
|
||||
К: 'K',
|
||||
Л: 'L',
|
||||
М: 'M',
|
||||
Н: 'N',
|
||||
О: 'O',
|
||||
П: 'P',
|
||||
Р: 'R',
|
||||
С: 'S',
|
||||
Т: 'T',
|
||||
У: 'U',
|
||||
Ф: 'F',
|
||||
Ы: 'I',
|
||||
Э: 'E',
|
||||
ё: 'yo',
|
||||
х: 'h',
|
||||
ц: 'ts',
|
||||
ч: 'ch',
|
||||
ш: 'sh',
|
||||
щ: 'shch',
|
||||
ъ: "''",
|
||||
ь: "'",
|
||||
ю: 'yu',
|
||||
я: 'ya',
|
||||
Ё: 'YO',
|
||||
Х: 'H',
|
||||
Ц: 'TS',
|
||||
Ч: 'CH',
|
||||
Ш: 'SH',
|
||||
Щ: 'SHCH',
|
||||
Ъ: "''",
|
||||
Ь: "'",
|
||||
Ю: 'YU',
|
||||
Я: 'YA',
|
||||
а: 'a',
|
||||
б: 'b',
|
||||
в: 'v',
|
||||
г: 'g',
|
||||
д: 'd',
|
||||
е: 'e',
|
||||
ж: 'g',
|
||||
з: 'z',
|
||||
и: 'i',
|
||||
й: 'y',
|
||||
к: 'k',
|
||||
л: 'l',
|
||||
м: 'm',
|
||||
н: 'n',
|
||||
о: 'o',
|
||||
п: 'p',
|
||||
р: 'r',
|
||||
с: 's',
|
||||
т: 't',
|
||||
у: 'u',
|
||||
ф: 'f',
|
||||
ы: 'i',
|
||||
э: 'e',
|
||||
А: 'A',
|
||||
Б: 'B',
|
||||
В: 'V',
|
||||
Г: 'G',
|
||||
Д: 'D',
|
||||
Е: 'E',
|
||||
Ж: 'G',
|
||||
З: 'Z',
|
||||
И: 'I',
|
||||
Й: 'Y',
|
||||
К: 'K',
|
||||
Л: 'L',
|
||||
М: 'M',
|
||||
Н: 'N',
|
||||
О: 'O',
|
||||
П: 'P',
|
||||
Р: 'R',
|
||||
С: 'S',
|
||||
Т: 'T',
|
||||
У: 'U',
|
||||
Ф: 'F',
|
||||
Ы: 'I',
|
||||
Э: 'E',
|
||||
ё: 'yo',
|
||||
х: 'h',
|
||||
ц: 'ts',
|
||||
ч: 'ch',
|
||||
ш: 'sh',
|
||||
щ: 'shch',
|
||||
ъ: "''",
|
||||
ь: "'",
|
||||
ю: 'yu',
|
||||
я: 'ya',
|
||||
Ё: 'YO',
|
||||
Х: 'H',
|
||||
Ц: 'TS',
|
||||
Ч: 'CH',
|
||||
Ш: 'SH',
|
||||
Щ: 'SHCH',
|
||||
Ъ: "''",
|
||||
Ь: "'",
|
||||
Ю: 'YU',
|
||||
Я: 'YA',
|
||||
};
|
||||
/**
|
||||
* Function to translate string
|
||||
|
@ -72,7 +74,8 @@ const translationTable = {
|
|||
* @param string - string to translate
|
||||
* @returns {string} - translated string
|
||||
*/
|
||||
|
||||
module.exports = function translateString(string) {
|
||||
return string.replace(/[А-яёЁ]/g, (char) => translationTable[char] || char);
|
||||
};
|
||||
function translateString(string) {
|
||||
return string.replace(/[А-яёЁ]/g, (char) => translationTable[char] || char);
|
||||
}
|
||||
exports.default = translateString;
|
||||
;
|
||||
|
|
81
src/utils/translation.ts
Normal file
81
src/utils/translation.ts
Normal file
|
@ -0,0 +1,81 @@
|
|||
interface TransTable {
|
||||
[key: string]: string;
|
||||
}
|
||||
const translationTable: TransTable = {
|
||||
а: 'a',
|
||||
б: 'b',
|
||||
в: 'v',
|
||||
г: 'g',
|
||||
д: 'd',
|
||||
е: 'e',
|
||||
ж: 'g',
|
||||
з: 'z',
|
||||
и: 'i',
|
||||
й: 'y',
|
||||
к: 'k',
|
||||
л: 'l',
|
||||
м: 'm',
|
||||
н: 'n',
|
||||
о: 'o',
|
||||
п: 'p',
|
||||
р: 'r',
|
||||
с: 's',
|
||||
т: 't',
|
||||
у: 'u',
|
||||
ф: 'f',
|
||||
ы: 'i',
|
||||
э: 'e',
|
||||
А: 'A',
|
||||
Б: 'B',
|
||||
В: 'V',
|
||||
Г: 'G',
|
||||
Д: 'D',
|
||||
Е: 'E',
|
||||
Ж: 'G',
|
||||
З: 'Z',
|
||||
И: 'I',
|
||||
Й: 'Y',
|
||||
К: 'K',
|
||||
Л: 'L',
|
||||
М: 'M',
|
||||
Н: 'N',
|
||||
О: 'O',
|
||||
П: 'P',
|
||||
Р: 'R',
|
||||
С: 'S',
|
||||
Т: 'T',
|
||||
У: 'U',
|
||||
Ф: 'F',
|
||||
Ы: 'I',
|
||||
Э: 'E',
|
||||
ё: 'yo',
|
||||
х: 'h',
|
||||
ц: 'ts',
|
||||
ч: 'ch',
|
||||
ш: 'sh',
|
||||
щ: 'shch',
|
||||
ъ: "''",
|
||||
ь: "'",
|
||||
ю: 'yu',
|
||||
я: 'ya',
|
||||
Ё: 'YO',
|
||||
Х: 'H',
|
||||
Ц: 'TS',
|
||||
Ч: 'CH',
|
||||
Ш: 'SH',
|
||||
Щ: 'SHCH',
|
||||
Ъ: "''",
|
||||
Ь: "'",
|
||||
Ю: 'YU',
|
||||
Я: 'YA',
|
||||
};
|
||||
/**
|
||||
* Function to translate string
|
||||
*
|
||||
* @param string - string to translate
|
||||
* @returns {string} - translated string
|
||||
*/
|
||||
|
||||
export default function translateString(string: string): string {
|
||||
return string.replace(/[А-яёЁ]/g, (char) => translationTable[char] || char);
|
||||
};
|
|
@ -1,46 +1,49 @@
|
|||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
/**
|
||||
* Twig extensions
|
||||
*/
|
||||
const twig = require('twig');
|
||||
const fs = require('fs');
|
||||
const urlify = require('./urlify');
|
||||
|
||||
module.exports = (function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Function for include svg on page
|
||||
*
|
||||
* @example svg('path/from/root/dir')
|
||||
* @param filename - name of icon
|
||||
* @returns {string} - svg code
|
||||
*/
|
||||
twig.extendFunction('svg', function (filename) {
|
||||
return fs.readFileSync(`${__dirname}/../frontend/svg/${filename}.svg`, 'utf-8');
|
||||
});
|
||||
|
||||
/**
|
||||
* Convert text to URL-like string
|
||||
* Example: "What is <mark>clean data</mark>" -> "what-is-clean-data"
|
||||
*
|
||||
* @param {string} string - source string with HTML
|
||||
* @returns {string} alias-like string
|
||||
*/
|
||||
twig.extendFilter('urlify', function (string) {
|
||||
return urlify(string);
|
||||
});
|
||||
|
||||
/**
|
||||
* Parse link as URL object
|
||||
*
|
||||
* @param {string} linkUrl - link to be processed
|
||||
* @returns {UrlWithStringQuery} — url data
|
||||
*/
|
||||
twig.extendFunction('parseLink', function (linkUrl) {
|
||||
try {
|
||||
return new URL(linkUrl);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
});
|
||||
const twig_1 = __importDefault(require("twig"));
|
||||
const fs_1 = __importDefault(require("fs"));
|
||||
const urlify_1 = __importDefault(require("./urlify"));
|
||||
exports.default = (function () {
|
||||
'use strict';
|
||||
/**
|
||||
* Function for include svg on page
|
||||
*
|
||||
* @example svg('path/from/root/dir')
|
||||
* @param {string} filename - name of icon
|
||||
* @returns {string} - svg code
|
||||
*/
|
||||
twig_1.default.extendFunction('svg', function (filename) {
|
||||
return fs_1.default.readFileSync(`${__dirname}/../frontend/svg/${filename}.svg`, 'utf-8');
|
||||
});
|
||||
/**
|
||||
* Convert text to URL-like string
|
||||
* Example: "What is <mark>clean data</mark>" -> "what-is-clean-data"
|
||||
*
|
||||
* @param {string} string - source string with HTML
|
||||
* @returns {string} alias-like string
|
||||
*/
|
||||
twig_1.default.extendFilter('urlify', function (string) {
|
||||
return urlify_1.default(string);
|
||||
});
|
||||
/**
|
||||
* Parse link as URL object
|
||||
*
|
||||
* @param {string} linkUrl - link to be processed
|
||||
* @returns {UrlWithStringQuery} — url data
|
||||
*/
|
||||
twig_1.default.extendFunction('parseLink', function (linkUrl) {
|
||||
try {
|
||||
return new URL(linkUrl).toString();
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
return "";
|
||||
}
|
||||
});
|
||||
}());
|
||||
|
|
47
src/utils/twig.ts
Normal file
47
src/utils/twig.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* Twig extensions
|
||||
*/
|
||||
import twig from "twig";
|
||||
import fs from "fs";
|
||||
import urlify from "./urlify";
|
||||
|
||||
export default (function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Function for include svg on page
|
||||
*
|
||||
* @example svg('path/from/root/dir')
|
||||
* @param {string} filename - name of icon
|
||||
* @returns {string} - svg code
|
||||
*/
|
||||
twig.extendFunction('svg', function (filename: string) {
|
||||
return fs.readFileSync(`${__dirname}/../frontend/svg/${filename}.svg`, 'utf-8');
|
||||
});
|
||||
|
||||
/**
|
||||
* Convert text to URL-like string
|
||||
* Example: "What is <mark>clean data</mark>" -> "what-is-clean-data"
|
||||
*
|
||||
* @param {string} string - source string with HTML
|
||||
* @returns {string} alias-like string
|
||||
*/
|
||||
twig.extendFilter('urlify', function (string: string) {
|
||||
return urlify(string);
|
||||
});
|
||||
|
||||
/**
|
||||
* Parse link as URL object
|
||||
*
|
||||
* @param {string} linkUrl - link to be processed
|
||||
* @returns {UrlWithStringQuery} — url data
|
||||
*/
|
||||
twig.extendFunction('parseLink', function (linkUrl: string): string{
|
||||
try {
|
||||
return new URL(linkUrl).toString();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return "";
|
||||
}
|
||||
});
|
||||
}());
|
|
@ -1,5 +1,9 @@
|
|||
const translateString = require('./translation');
|
||||
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const translation_1 = __importDefault(require("./translation"));
|
||||
/**
|
||||
* Convert text to URL-like string
|
||||
* Example: "What is <mark>clean data</mark>" -> "what-is-clean-data"
|
||||
|
@ -7,27 +11,22 @@ const translateString = require('./translation');
|
|||
* @param {string} string - source string with HTML
|
||||
* @returns {string} alias-like string
|
||||
*/
|
||||
module.exports = function urlify(string) {
|
||||
// strip tags
|
||||
string = string.replace(/(<([^>]+)>)/ig, '');
|
||||
|
||||
// remove nbsp
|
||||
string = string.replace(/ /g, ' ');
|
||||
|
||||
// remove all symbols except chars
|
||||
string = string.replace(/[^a-zA-Z0-9А-Яа-яЁё ]/g, ' ');
|
||||
|
||||
// remove whitespaces
|
||||
string = string.replace(/ +/g, ' ').trim();
|
||||
|
||||
// lowercase
|
||||
string = string.toLowerCase();
|
||||
|
||||
// join words with hyphens
|
||||
string = string.split(' ').join('-');
|
||||
|
||||
// translate
|
||||
string = translateString(string);
|
||||
|
||||
return string;
|
||||
};
|
||||
function urlify(string) {
|
||||
// strip tags
|
||||
string = string.replace(/(<([^>]+)>)/ig, '');
|
||||
// remove nbsp
|
||||
string = string.replace(/ /g, ' ');
|
||||
// remove all symbols except chars
|
||||
string = string.replace(/[^a-zA-Z0-9А-Яа-яЁё ]/g, ' ');
|
||||
// remove whitespaces
|
||||
string = string.replace(/ +/g, ' ').trim();
|
||||
// lowercase
|
||||
string = string.toLowerCase();
|
||||
// join words with hyphens
|
||||
string = string.split(' ').join('-');
|
||||
// translate
|
||||
string = translation_1.default(string);
|
||||
return string;
|
||||
}
|
||||
exports.default = urlify;
|
||||
;
|
||||
|
|
33
src/utils/urlify.ts
Normal file
33
src/utils/urlify.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import translateString from "./translation";
|
||||
|
||||
/**
|
||||
* Convert text to URL-like string
|
||||
* Example: "What is <mark>clean data</mark>" -> "what-is-clean-data"
|
||||
*
|
||||
* @param {string} string - source string with HTML
|
||||
* @returns {string} alias-like string
|
||||
*/
|
||||
export default function urlify(string: string) {
|
||||
// strip tags
|
||||
string = string.replace(/(<([^>]+)>)/ig, '');
|
||||
|
||||
// remove nbsp
|
||||
string = string.replace(/ /g, ' ');
|
||||
|
||||
// remove all symbols except chars
|
||||
string = string.replace(/[^a-zA-Z0-9А-Яа-яЁё ]/g, ' ');
|
||||
|
||||
// remove whitespaces
|
||||
string = string.replace(/ +/g, ' ').trim();
|
||||
|
||||
// lowercase
|
||||
string = string.toLowerCase();
|
||||
|
||||
// join words with hyphens
|
||||
string = string.split(' ').join('-');
|
||||
|
||||
// translate
|
||||
string = translateString(string);
|
||||
|
||||
return string;
|
||||
};
|
72
tsconfig.json
Normal file
72
tsconfig.json
Normal file
|
@ -0,0 +1,72 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "ES2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
// "outDir": "./", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "composite": true, /* Enable project compilation */
|
||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
|
||||
/* Advanced Options */
|
||||
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue