mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-08-08 06:55:26 +02:00
fix bugs
This commit is contained in:
parent
6fc17913ce
commit
2feed218a0
23 changed files with 246 additions and 178 deletions
|
@ -24,8 +24,8 @@ hawk:
|
||||||
# backendToken: "123"
|
# backendToken: "123"
|
||||||
|
|
||||||
database:
|
database:
|
||||||
driver: local
|
driver: mongodb
|
||||||
local:
|
local:
|
||||||
path: ./db
|
path: ./db
|
||||||
mongodb:
|
mongodb:
|
||||||
uri: mongodb://localhost:27017
|
uri: mongodb://localhost:27017/docs1
|
||||||
|
|
|
@ -12,3 +12,12 @@ services:
|
||||||
- ./public/uploads:/uploads
|
- ./public/uploads:/uploads
|
||||||
- ./db:/usr/src/app/db
|
- ./db:/usr/src/app/db
|
||||||
- ./app-config.yaml:/usr/src/app/app-config.yaml
|
- ./app-config.yaml:/usr/src/app/app-config.yaml
|
||||||
|
mongodb:
|
||||||
|
image: mongo:6.0.1
|
||||||
|
ports:
|
||||||
|
- "27017:27017"
|
||||||
|
volumes:
|
||||||
|
- mongodb_data:/data/db
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
mongodb_data:
|
||||||
|
|
|
@ -8,5 +8,5 @@
|
||||||
"watch": [
|
"watch": [
|
||||||
"**/*"
|
"**/*"
|
||||||
],
|
],
|
||||||
"ext": "js,twig"
|
"ext": "js,twig,ts"
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import * as dotenv from 'dotenv';
|
||||||
import HawkCatcher from '@hawk.so/nodejs';
|
import HawkCatcher from '@hawk.so/nodejs';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
import { downloadFavicon, FaviconData } from './utils/downloadFavicon.js';
|
import { downloadFavicon, FaviconData } from './utils/downloadFavicon.js';
|
||||||
import appConfig from "./utils/appConfig.js";
|
import appConfig from './utils/appConfig.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The __dirname CommonJS variables are not available in ES modules.
|
* The __dirname CommonJS variables are not available in ES modules.
|
||||||
|
@ -28,7 +28,7 @@ if (appConfig.hawk?.backendToken) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get url to upload favicon from config
|
// Get url to upload favicon from config
|
||||||
const favicon = appConfig.favicon
|
const favicon = appConfig.favicon;
|
||||||
|
|
||||||
app.locals.config = localConfig;
|
app.locals.config = localConfig;
|
||||||
// Set client error tracking token as app local.
|
// Set client error tracking token as app local.
|
||||||
|
|
|
@ -4,6 +4,8 @@ import PagesOrder from './pagesOrder.js';
|
||||||
import PageOrder from '../models/pageOrder.js';
|
import PageOrder from '../models/pageOrder.js';
|
||||||
import HttpException from '../exceptions/httpException.js';
|
import HttpException from '../exceptions/httpException.js';
|
||||||
import PagesFlatArray from '../models/pagesFlatArray.js';
|
import PagesFlatArray from '../models/pagesFlatArray.js';
|
||||||
|
import {EntityId} from '../utils/database/types.js';
|
||||||
|
import {isEqualIds} from "../utils/database/index.js";
|
||||||
|
|
||||||
type PageDataFields = keyof PageData;
|
type PageDataFields = keyof PageData;
|
||||||
|
|
||||||
|
@ -27,7 +29,7 @@ class Pages {
|
||||||
* @param {string} id - page id
|
* @param {string} id - page id
|
||||||
* @returns {Promise<Page>}
|
* @returns {Promise<Page>}
|
||||||
*/
|
*/
|
||||||
public static async get(id: string): Promise<Page> {
|
public static async get(id: EntityId): Promise<Page> {
|
||||||
const page = await Page.get(id);
|
const page = await Page.get(id);
|
||||||
|
|
||||||
if (!page._id) {
|
if (!page._id) {
|
||||||
|
@ -42,7 +44,7 @@ class Pages {
|
||||||
*
|
*
|
||||||
* @returns {Promise<Page[]>}
|
* @returns {Promise<Page[]>}
|
||||||
*/
|
*/
|
||||||
public static async getAll(): Promise<Page[]> {
|
public static async getAllPages(): Promise<Page[]> {
|
||||||
return Page.getAll();
|
return Page.getAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,8 +54,8 @@ class Pages {
|
||||||
* @param {string} parent - id of current page
|
* @param {string} parent - id of current page
|
||||||
* @returns {Promise<Page[]>}
|
* @returns {Promise<Page[]>}
|
||||||
*/
|
*/
|
||||||
public static async getAllExceptChildren(parent: string): Promise<Page[]> {
|
public static async getAllExceptChildren(parent: EntityId): Promise<Page[]> {
|
||||||
const pagesAvailable = this.removeChildren(await Pages.getAll(), parent);
|
const pagesAvailable = this.removeChildren(await Pages.getAllPages(), parent);
|
||||||
|
|
||||||
const nullFilteredPages: Page[] = [];
|
const nullFilteredPages: Page[] = [];
|
||||||
|
|
||||||
|
@ -66,6 +68,21 @@ class Pages {
|
||||||
return nullFilteredPages;
|
return nullFilteredPages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async getPagesMap(): Promise<Map<string, Page>> {
|
||||||
|
const pages = await Pages.getAllPages();
|
||||||
|
const pagesMap = new Map<string, Page>();
|
||||||
|
|
||||||
|
pages.forEach(page => {
|
||||||
|
if (page._id) {
|
||||||
|
pagesMap.set(page._id.toString(), page);
|
||||||
|
} else {
|
||||||
|
throw new Error('Page id is not defined');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return pagesMap;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Group all pages by their parents
|
* Group all pages by their parents
|
||||||
* If the pageId is passed, it excludes passed page from result pages
|
* If the pageId is passed, it excludes passed page from result pages
|
||||||
|
@ -73,11 +90,9 @@ class Pages {
|
||||||
* @param {string} pageId - pageId to exclude from result pages
|
* @param {string} pageId - pageId to exclude from result pages
|
||||||
* @returns {Page[]}
|
* @returns {Page[]}
|
||||||
*/
|
*/
|
||||||
public static async groupByParent(pageId = ''): Promise<Page[]> {
|
public static async groupByParent(pageId: EntityId = ''): Promise<Page[]> {
|
||||||
const result: Page[] = [];
|
const rootPageOrder = await PagesOrder.getRootPageOrder(); // get order of the root pages
|
||||||
const orderGroupedByParent: Record<string, string[]> = {};
|
const childPageOrder = await PagesOrder.getChildPageOrder(); // get order of the all other pages
|
||||||
const rootPageOrder = await PagesOrder.getRootPageOrder();
|
|
||||||
const childPageOrder = await PagesOrder.getChildPageOrder();
|
|
||||||
const orphanPageOrder: PageOrder[] = [];
|
const orphanPageOrder: PageOrder[] = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -87,21 +102,17 @@ class Pages {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const pages = (await this.getAll()).reduce((map, _page) => {
|
const pagesMap = await this.getPagesMap();
|
||||||
map.set(_page._id, _page);
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}, new Map);
|
|
||||||
const idsOfRootPages = rootPageOrder.order;
|
const idsOfRootPages = rootPageOrder.order;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It groups root pages and 1 level pages by its parent
|
* It groups root pages and 1 level pages by its parent
|
||||||
*/
|
*/
|
||||||
idsOfRootPages.reduce((prev, curr, idx) => {
|
const orderGroupedByParent = idsOfRootPages.reduce((acc, curr, idx) => {
|
||||||
const childPages: PageOrder[] = [];
|
const childPages: PageOrder[] = [];
|
||||||
|
|
||||||
childPageOrder.forEach((pageOrder, _idx) => {
|
childPageOrder.forEach((pageOrder, _idx) => {
|
||||||
if (pageOrder.page === curr) {
|
if (isEqualIds(pageOrder.page, curr)) {
|
||||||
childPages.push(pageOrder);
|
childPages.push(pageOrder);
|
||||||
childPageOrder.splice(_idx, 1);
|
childPageOrder.splice(_idx, 1);
|
||||||
}
|
}
|
||||||
|
@ -109,14 +120,14 @@ class Pages {
|
||||||
|
|
||||||
const hasChildPage = childPages.length > 0;
|
const hasChildPage = childPages.length > 0;
|
||||||
|
|
||||||
prev[curr] = [];
|
acc[curr.toString()] = [];
|
||||||
prev[curr].push(curr);
|
acc[curr.toString()].push(curr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It attaches 1 level page id to its parent page id
|
* It attaches 1 level page id to its parent page id
|
||||||
*/
|
*/
|
||||||
if (hasChildPage) {
|
if (hasChildPage) {
|
||||||
prev[curr].push(...childPages[0].order);
|
acc[curr.toString()].push(...childPages[0].order);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -127,8 +138,8 @@ class Pages {
|
||||||
orphanPageOrder.push(...childPageOrder);
|
orphanPageOrder.push(...childPageOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
return prev;
|
return acc;
|
||||||
}, orderGroupedByParent);
|
}, {} as Record<string, EntityId[]>);
|
||||||
|
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
|
||||||
|
@ -159,9 +170,10 @@ class Pages {
|
||||||
/**
|
/**
|
||||||
* It converts grouped pages(object) to array
|
* It converts grouped pages(object) to array
|
||||||
*/
|
*/
|
||||||
Object.values(orderGroupedByParent).flatMap(arr => [ ...arr ])
|
const result = Object.values(orderGroupedByParent)
|
||||||
.forEach(arr => {
|
.flatMap(arr => [ ...arr ])
|
||||||
result.push(pages.get(arr));
|
.map(arr => {
|
||||||
|
return pagesMap.get(arr.toString()) as Page;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -188,9 +200,9 @@ class Pages {
|
||||||
* @param {string} parent - id of parent page
|
* @param {string} parent - id of parent page
|
||||||
* @returns {Array<?Page>}
|
* @returns {Array<?Page>}
|
||||||
*/
|
*/
|
||||||
public static removeChildren(pagesAvailable: Array<Page | null>, parent: string | undefined): Array<Page | null> {
|
public static removeChildren(pagesAvailable: Array<Page | null>, parent: EntityId | undefined): Array<Page | null> {
|
||||||
pagesAvailable.forEach(async (item, index) => {
|
pagesAvailable.forEach(async (item, index) => {
|
||||||
if (item === null || item._parent !== parent) {
|
if (item === null || !isEqualIds(item._parent, parent)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pagesAvailable[index] = null;
|
pagesAvailable[index] = null;
|
||||||
|
@ -278,7 +290,7 @@ class Pages {
|
||||||
* @param {string} id - page id
|
* @param {string} id - page id
|
||||||
* @returns {Promise<Page>}
|
* @returns {Promise<Page>}
|
||||||
*/
|
*/
|
||||||
public static async remove(id: string): Promise<Page> {
|
public static async remove(id: EntityId): Promise<Page> {
|
||||||
const page = await Page.get(id);
|
const page = await Page.get(id);
|
||||||
|
|
||||||
if (!page._id) {
|
if (!page._id) {
|
||||||
|
@ -291,6 +303,7 @@ class Pages {
|
||||||
await alias.destroy();
|
await alias.destroy();
|
||||||
}
|
}
|
||||||
const removedPage = page.destroy();
|
const removedPage = page.destroy();
|
||||||
|
|
||||||
await PagesFlatArray.regenerate();
|
await PagesFlatArray.regenerate();
|
||||||
|
|
||||||
return removedPage;
|
return removedPage;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import PageOrder from '../models/pageOrder.js';
|
import PageOrder from '../models/pageOrder.js';
|
||||||
import Page from '../models/page.js';
|
import Page from '../models/page.js';
|
||||||
import PagesFlatArray from '../models/pagesFlatArray.js';
|
import PagesFlatArray from '../models/pagesFlatArray.js';
|
||||||
|
import { EntityId } from '../utils/database/types.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class PagesOrder
|
* @class PagesOrder
|
||||||
|
@ -15,7 +16,7 @@ class PagesOrder {
|
||||||
* @param {string} parentId - of which page we want to get children order
|
* @param {string} parentId - of which page we want to get children order
|
||||||
* @returns {Promise<PageOrder>}
|
* @returns {Promise<PageOrder>}
|
||||||
*/
|
*/
|
||||||
public static async get(parentId: string): Promise<PageOrder> {
|
public static async get(parentId: EntityId): Promise<PageOrder> {
|
||||||
const order = await PageOrder.get(parentId);
|
const order = await PageOrder.get(parentId);
|
||||||
|
|
||||||
if (!order._id) {
|
if (!order._id) {
|
||||||
|
@ -58,7 +59,7 @@ class PagesOrder {
|
||||||
* @param {string} parentId - parent page's id
|
* @param {string} parentId - parent page's id
|
||||||
* @param {string} childId - new page pushed to the order
|
* @param {string} childId - new page pushed to the order
|
||||||
*/
|
*/
|
||||||
public static async push(parentId: string, childId: string): Promise<void> {
|
public static async push(parentId: EntityId, childId: EntityId): Promise<void> {
|
||||||
const order = await PageOrder.get(parentId);
|
const order = await PageOrder.get(parentId);
|
||||||
|
|
||||||
order.push(childId);
|
order.push(childId);
|
||||||
|
@ -73,7 +74,7 @@ class PagesOrder {
|
||||||
* @param {string} newParentId - new parent page's id
|
* @param {string} newParentId - new parent page's id
|
||||||
* @param {string} targetPageId - page's id which is changing the parent page
|
* @param {string} targetPageId - page's id which is changing the parent page
|
||||||
*/
|
*/
|
||||||
public static async move(oldParentId: string, newParentId: string, targetPageId: string): Promise<void> {
|
public static async move(oldParentId: EntityId, newParentId: EntityId, targetPageId: EntityId): Promise<void> {
|
||||||
const oldParentOrder = await PageOrder.get(oldParentId);
|
const oldParentOrder = await PageOrder.get(oldParentId);
|
||||||
|
|
||||||
oldParentOrder.remove(targetPageId);
|
oldParentOrder.remove(targetPageId);
|
||||||
|
@ -96,8 +97,9 @@ class PagesOrder {
|
||||||
* @param {boolean} ignoreSelf - should we ignore current page in list or not
|
* @param {boolean} ignoreSelf - should we ignore current page in list or not
|
||||||
* @returns {Page[]}
|
* @returns {Page[]}
|
||||||
*/
|
*/
|
||||||
public static async getOrderedChildren(pages: Page[], currentPageId: string, parentPageId: string, ignoreSelf = false): Promise<Page[]> {
|
public static async getOrderedChildren(pages: Page[], currentPageId: EntityId, parentPageId: EntityId, ignoreSelf = false): Promise<Page[]> {
|
||||||
const children = await PageOrder.get(parentPageId);
|
const children = await PageOrder.get(parentPageId);
|
||||||
|
console.log({children})
|
||||||
const unordered = pages.filter(page => page._parent === parentPageId).map(page => page._id);
|
const unordered = pages.filter(page => page._parent === parentPageId).map(page => page._id);
|
||||||
|
|
||||||
// Create unique array with ordered and unordered pages id
|
// Create unique array with ordered and unordered pages id
|
||||||
|
@ -122,7 +124,7 @@ class PagesOrder {
|
||||||
* @param {string} parentPageId - parent page's id that contains both two pages
|
* @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
|
* @param {string} putAbovePageId - page's id above which we put the target page
|
||||||
*/
|
*/
|
||||||
public static async update(unordered: string[], currentPageId: string, parentPageId: string, putAbovePageId: string): Promise<void> {
|
public static async update(unordered: string[], currentPageId: EntityId, parentPageId: EntityId, putAbovePageId: EntityId): Promise<void> {
|
||||||
const pageOrder = await PageOrder.get(parentPageId);
|
const pageOrder = await PageOrder.get(parentPageId);
|
||||||
|
|
||||||
// Create unique array with ordered and unordered pages id
|
// Create unique array with ordered and unordered pages id
|
||||||
|
@ -136,7 +138,7 @@ class PagesOrder {
|
||||||
* @param {string} parentId - identity of parent page
|
* @param {string} parentId - identity of parent page
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
public static async remove(parentId: string): Promise<void> {
|
public static async remove(parentId: EntityId): Promise<void> {
|
||||||
const order = await PageOrder.get(parentId);
|
const order = await PageOrder.get(parentId);
|
||||||
|
|
||||||
if (!order._id) {
|
if (!order._id) {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import nodePath from 'path';
|
||||||
import File, { FileData } from '../models/file.js';
|
import File, { FileData } from '../models/file.js';
|
||||||
import crypto from '../utils/crypto.js';
|
import crypto from '../utils/crypto.js';
|
||||||
import deepMerge from '../utils/objects.js';
|
import deepMerge from '../utils/objects.js';
|
||||||
import appConfig from "../utils/appConfig.js";
|
import appConfig from '../utils/appConfig.js';
|
||||||
|
|
||||||
const random16 = crypto.random16;
|
const random16 = crypto.random16;
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ class Transport {
|
||||||
* @param {string} multerData.path - path to the uploaded file
|
* @param {string} multerData.path - path to the uploaded file
|
||||||
* @param {number} multerData.size - size of the uploaded file
|
* @param {number} multerData.size - size of the uploaded file
|
||||||
* @param {string} multerData.mimetype - MIME type 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
|
* @param {object} map - object that represents how should fields of File object should be mapped to response
|
||||||
* @returns {Promise<FileData>}
|
* @returns {Promise<FileData>}
|
||||||
*/
|
*/
|
||||||
|
@ -108,11 +107,10 @@ class Transport {
|
||||||
*
|
*
|
||||||
* @param {File} file - file object
|
* @param {File} file - file object
|
||||||
* @param {object} map - object that represents how should fields of File object should be mapped to response
|
* @param {object} map - object that represents how should fields of File object should be mapped to response
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public static composeResponse(file: File, map: Dict): Dict {
|
public static composeResponse(file: File, map: Dict): Dict {
|
||||||
const response: Dict = {};
|
const response: Dict = {};
|
||||||
const { data } = file;
|
const data = file.data as Record<string, string | number | undefined>;
|
||||||
|
|
||||||
Object.entries(map).forEach(([name, path]) => {
|
Object.entries(map).forEach(([name, path]) => {
|
||||||
const fields: string[] = path.split(':');
|
const fields: string[] = path.split(':');
|
||||||
|
|
|
@ -1,46 +1,71 @@
|
||||||
import crypto from '../utils/crypto.js';
|
import crypto from '../utils/crypto.js';
|
||||||
import database from '../utils/database/index.js';
|
import database from '../utils/database/index.js';
|
||||||
|
import { EntityId } from '../utils/database/types.js';
|
||||||
|
|
||||||
const binaryMD5 = crypto.binaryMD5;
|
const binaryMD5 = crypto.binaryMD5;
|
||||||
const aliasesDb = database['aliases'];
|
const aliasesDb = database['aliases'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} AliasData
|
* Describe an alias
|
||||||
* @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
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
export interface AliasData {
|
export interface AliasData {
|
||||||
_id?: string;
|
/**
|
||||||
|
* Alias id
|
||||||
|
*/
|
||||||
|
_id?: EntityId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias binary hash
|
||||||
|
*/
|
||||||
hash?: string;
|
hash?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entity type
|
||||||
|
*/
|
||||||
type?: string;
|
type?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate if alias deprecated
|
||||||
|
*/
|
||||||
deprecated?: boolean;
|
deprecated?: boolean;
|
||||||
id?: string;
|
|
||||||
|
/**
|
||||||
|
* Entity id
|
||||||
|
*/
|
||||||
|
id?: EntityId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Alias
|
* Alias model
|
||||||
* @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 {
|
class Alias {
|
||||||
public _id?: string;
|
/**
|
||||||
|
* Alias id
|
||||||
|
*/
|
||||||
|
public _id?: EntityId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias binary hash
|
||||||
|
*/
|
||||||
public hash?: string;
|
public hash?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entity type
|
||||||
|
*/
|
||||||
public type?: string;
|
public type?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate if alias deprecated
|
||||||
|
*/
|
||||||
public deprecated?: boolean;
|
public deprecated?: boolean;
|
||||||
public id?: string;
|
|
||||||
|
/**
|
||||||
|
* Entity id
|
||||||
|
*/
|
||||||
|
public id?: EntityId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class
|
* @class
|
||||||
*
|
|
||||||
* @param {AliasData} data - info about alias
|
* @param {AliasData} data - info about alias
|
||||||
* @param {string} aliasName - alias of entity
|
* @param {string} aliasName - alias of entity
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import database from '../utils/database/index.js';
|
import database from '../utils/database/index.js';
|
||||||
|
import { EntityId } from '../utils/database/types.js';
|
||||||
|
|
||||||
const filesDb = database['files'];
|
const filesDb = database['files'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} FileData
|
* @typedef {object} FileData
|
||||||
*
|
|
||||||
* @property {string} _id - file id
|
* @property {string} _id - file id
|
||||||
* @property {string} name - original file name
|
* @property {string} name - original file name
|
||||||
* @property {string} filename - name of uploaded file
|
* @property {string} filename - name of uploaded file
|
||||||
|
@ -14,20 +14,18 @@ const filesDb = database['files'];
|
||||||
* @property {number} size - size of the file in
|
* @property {number} size - size of the file in
|
||||||
*/
|
*/
|
||||||
export interface FileData {
|
export interface FileData {
|
||||||
_id?: string;
|
_id?: EntityId;
|
||||||
name?: string;
|
name?: string;
|
||||||
filename?: string;
|
filename?: string;
|
||||||
path?: string;
|
path?: string;
|
||||||
mimetype?: string;
|
mimetype?: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
size?: number;
|
size?: number;
|
||||||
[key: string]: string | number | undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class File
|
* @class File
|
||||||
* @class File model
|
* @class File model
|
||||||
*
|
|
||||||
* @property {string} _id - file id
|
* @property {string} _id - file id
|
||||||
* @property {string} name - original file name
|
* @property {string} name - original file name
|
||||||
* @property {string} filename - name of uploaded file
|
* @property {string} filename - name of uploaded file
|
||||||
|
@ -36,7 +34,7 @@ export interface FileData {
|
||||||
* @property {number} size - size of the file in
|
* @property {number} size - size of the file in
|
||||||
*/
|
*/
|
||||||
class File {
|
class File {
|
||||||
public _id?: string;
|
public _id?: EntityId;
|
||||||
public name?: string;
|
public name?: string;
|
||||||
public filename?: string;
|
public filename?: string;
|
||||||
public path?: string;
|
public path?: string;
|
||||||
|
@ -46,7 +44,6 @@ class File {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class
|
* @class
|
||||||
*
|
|
||||||
* @param {FileData} data - info about file
|
* @param {FileData} data - info about file
|
||||||
*/
|
*/
|
||||||
constructor(data: FileData = {}) {
|
constructor(data: FileData = {}) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import urlify from '../utils/urlify.js';
|
import urlify from '../utils/urlify.js';
|
||||||
import database from '../utils/database/index.js';
|
import database from '../utils/database/index.js';
|
||||||
|
import { EntityId } from '../utils/database/types.js';
|
||||||
|
|
||||||
const pagesDb = database['pages'];
|
const pagesDb = database['pages'];
|
||||||
|
|
||||||
|
@ -12,17 +13,16 @@ const pagesDb = database['pages'];
|
||||||
* @property {string} parent - id of parent page
|
* @property {string} parent - id of parent page
|
||||||
*/
|
*/
|
||||||
export interface PageData {
|
export interface PageData {
|
||||||
_id?: string;
|
_id?: EntityId;
|
||||||
title?: string;
|
title?: string;
|
||||||
uri?: string;
|
uri?: string;
|
||||||
body?: any;
|
body?: any;
|
||||||
parent?: string;
|
parent?: EntityId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Page
|
* @class Page
|
||||||
* @class Page model
|
* @class Page model
|
||||||
*
|
|
||||||
* @property {string} _id - page id
|
* @property {string} _id - page id
|
||||||
* @property {string} title - page title
|
* @property {string} title - page title
|
||||||
* @property {string} uri - page uri
|
* @property {string} uri - page uri
|
||||||
|
@ -30,15 +30,14 @@ export interface PageData {
|
||||||
* @property {string} _parent - id of parent page
|
* @property {string} _parent - id of parent page
|
||||||
*/
|
*/
|
||||||
class Page {
|
class Page {
|
||||||
public _id?: string;
|
public _id?: EntityId;
|
||||||
public body?: any;
|
public body?: any;
|
||||||
public title?: string;
|
public title?: string;
|
||||||
public uri?: string;
|
public uri?: string;
|
||||||
public _parent?: string;
|
public _parent?: EntityId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class
|
* @class
|
||||||
*
|
|
||||||
* @param {PageData} data - page's data
|
* @param {PageData} data - page's data
|
||||||
*/
|
*/
|
||||||
constructor(data: PageData = {}) {
|
constructor(data: PageData = {}) {
|
||||||
|
@ -59,7 +58,7 @@ class Page {
|
||||||
* @param {string} _id - page id
|
* @param {string} _id - page id
|
||||||
* @returns {Promise<Page>}
|
* @returns {Promise<Page>}
|
||||||
*/
|
*/
|
||||||
public static async get(_id: string): Promise<Page> {
|
public static async get(_id: EntityId): Promise<Page> {
|
||||||
const data = await pagesDb.findOne({ _id });
|
const data = await pagesDb.findOne({ _id });
|
||||||
|
|
||||||
return new Page(data);
|
return new Page(data);
|
||||||
|
@ -86,7 +85,7 @@ class Page {
|
||||||
public static async getAll(query: Record<string, unknown> = {}): Promise<Page[]> {
|
public static async getAll(query: Record<string, unknown> = {}): Promise<Page[]> {
|
||||||
const docs = await pagesDb.find(query);
|
const docs = await pagesDb.find(query);
|
||||||
|
|
||||||
return Promise.all(docs.map(doc => new Page(doc)));
|
return docs.map(doc => new Page(doc));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import database from '../utils/database/index.js';
|
import database from '../utils/database/index.js';
|
||||||
|
import { ObjectId } from 'mongodb';
|
||||||
|
import { EntityId } from '../utils/database/types.js';
|
||||||
|
|
||||||
const db = database['pagesOrder'];
|
const db = database['pagesOrder'];
|
||||||
|
|
||||||
|
@ -9,9 +11,9 @@ const db = database['pagesOrder'];
|
||||||
* @property {Array<string>} order - list of ordered pages
|
* @property {Array<string>} order - list of ordered pages
|
||||||
*/
|
*/
|
||||||
export interface PageOrderData {
|
export interface PageOrderData {
|
||||||
_id?: string;
|
_id?: EntityId;
|
||||||
page?: string;
|
page?: EntityId;
|
||||||
order?: string[];
|
order?: EntityId[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,14 +23,13 @@ export interface PageOrderData {
|
||||||
* Creates order for Pages with children
|
* Creates order for Pages with children
|
||||||
*/
|
*/
|
||||||
class PageOrder {
|
class PageOrder {
|
||||||
public _id?: string;
|
public _id?: EntityId;
|
||||||
public page?: string;
|
public page?: EntityId;
|
||||||
private _order?: string[];
|
private _order?: EntityId[];
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class
|
* @class
|
||||||
*
|
|
||||||
* @param {PageOrderData} data - info about pageOrder
|
* @param {PageOrderData} data - info about pageOrder
|
||||||
*/
|
*/
|
||||||
constructor(data: PageOrderData = {}) {
|
constructor(data: PageOrderData = {}) {
|
||||||
|
@ -49,7 +50,7 @@ class PageOrder {
|
||||||
* @param {string} pageId - page's id
|
* @param {string} pageId - page's id
|
||||||
* @returns {Promise<PageOrder>}
|
* @returns {Promise<PageOrder>}
|
||||||
*/
|
*/
|
||||||
public static async get(pageId: string): Promise<PageOrder> {
|
public static async get(pageId: EntityId): Promise<PageOrder> {
|
||||||
const order = await db.findOne({ page: pageId });
|
const order = await db.findOne({ page: pageId });
|
||||||
|
|
||||||
let data: PageOrderData = {};
|
let data: PageOrderData = {};
|
||||||
|
@ -125,8 +126,8 @@ class PageOrder {
|
||||||
*
|
*
|
||||||
* @param {string} pageId - page's id
|
* @param {string} pageId - page's id
|
||||||
*/
|
*/
|
||||||
public push(pageId: string | number): void {
|
public push(pageId: EntityId): void {
|
||||||
if (typeof pageId === 'string') {
|
if (typeof pageId === 'string' || pageId instanceof ObjectId) {
|
||||||
if (this.order === undefined) {
|
if (this.order === undefined) {
|
||||||
this.order = [];
|
this.order = [];
|
||||||
}
|
}
|
||||||
|
@ -141,7 +142,7 @@ class PageOrder {
|
||||||
*
|
*
|
||||||
* @param {string} pageId - page's id
|
* @param {string} pageId - page's id
|
||||||
*/
|
*/
|
||||||
public remove(pageId: string): void {
|
public remove(pageId: EntityId): void {
|
||||||
if (this.order === undefined) {
|
if (this.order === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -156,10 +157,9 @@ class PageOrder {
|
||||||
/**
|
/**
|
||||||
* @param {string} currentPageId - page's id that changes the order
|
* @param {string} currentPageId - page's id that changes the order
|
||||||
* @param {string} putAbovePageId - page's id above which we put the target page
|
* @param {string} putAbovePageId - page's id above which we put the target page
|
||||||
*
|
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
public putAbove(currentPageId: string, putAbovePageId: string): void {
|
public putAbove(currentPageId: EntityId, putAbovePageId: EntityId): void {
|
||||||
if (this.order === undefined) {
|
if (this.order === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,7 @@ class PageOrder {
|
||||||
*
|
*
|
||||||
* @param {string} pageId - identity of page
|
* @param {string} pageId - identity of page
|
||||||
*/
|
*/
|
||||||
public getSubPageBefore(pageId: string): string | null {
|
public getSubPageBefore(pageId: EntityId): EntityId | null {
|
||||||
if (this.order === undefined) {
|
if (this.order === undefined) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -204,7 +204,7 @@ class PageOrder {
|
||||||
*
|
*
|
||||||
* @param pageId - identity of page
|
* @param pageId - identity of page
|
||||||
*/
|
*/
|
||||||
public getSubPageAfter(pageId: string): string | null {
|
public getSubPageAfter(pageId: EntityId): EntityId | null {
|
||||||
if (this.order === undefined) {
|
if (this.order === undefined) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -224,7 +224,7 @@ class PageOrder {
|
||||||
/**
|
/**
|
||||||
* @param {string[]} order - define new order
|
* @param {string[]} order - define new order
|
||||||
*/
|
*/
|
||||||
public set order(order: string[]) {
|
public set order(order: EntityId[]) {
|
||||||
this._order = order;
|
this._order = order;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,7 +233,7 @@ class PageOrder {
|
||||||
*
|
*
|
||||||
* @returns {string[]}
|
* @returns {string[]}
|
||||||
*/
|
*/
|
||||||
public get order(): string[] {
|
public get order(): EntityId[] {
|
||||||
return this._order || [];
|
return this._order || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import Page from './page.js';
|
import Page from './page.js';
|
||||||
import PageOrder from './pageOrder.js';
|
import PageOrder from './pageOrder.js';
|
||||||
import NodeCache from 'node-cache';
|
import NodeCache from 'node-cache';
|
||||||
|
import { EntityId } from '../utils/database/types.js';
|
||||||
|
|
||||||
// Create cache for flat array
|
// Create cache for flat array
|
||||||
const cache = new NodeCache({ stdTTL: 120 });
|
const cache = new NodeCache({ stdTTL: 120 });
|
||||||
|
@ -14,12 +15,12 @@ export interface PagesFlatArrayData {
|
||||||
/**
|
/**
|
||||||
* Page id
|
* Page id
|
||||||
*/
|
*/
|
||||||
id: string;
|
id: EntityId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page parent id
|
* Page parent id
|
||||||
*/
|
*/
|
||||||
parentId?: string;
|
parentId?: EntityId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* id of parent with parent id '0'
|
* id of parent with parent id '0'
|
||||||
|
@ -105,7 +106,7 @@ class PagesFlatArray {
|
||||||
* @param pageId - page id
|
* @param pageId - page id
|
||||||
* @returns {Promise<PagesFlatArrayData | undefined>}
|
* @returns {Promise<PagesFlatArrayData | undefined>}
|
||||||
*/
|
*/
|
||||||
public static async getPageBefore(pageId: string): Promise<PagesFlatArrayData | undefined> {
|
public static async getPageBefore(pageId: EntityId): Promise<PagesFlatArrayData | undefined> {
|
||||||
const arr = await this.get();
|
const arr = await this.get();
|
||||||
|
|
||||||
const pageIndex = arr.findIndex( (item) => item.id == pageId);
|
const pageIndex = arr.findIndex( (item) => item.id == pageId);
|
||||||
|
@ -125,7 +126,7 @@ class PagesFlatArray {
|
||||||
* @param pageId - page id
|
* @param pageId - page id
|
||||||
* @returns {Promise<PagesFlatArrayData | undefined>}
|
* @returns {Promise<PagesFlatArrayData | undefined>}
|
||||||
*/
|
*/
|
||||||
public static async getPageAfter(pageId: string): Promise<PagesFlatArrayData | undefined> {
|
public static async getPageAfter(pageId: EntityId): Promise<PagesFlatArrayData | undefined> {
|
||||||
const arr = await this.get();
|
const arr = await this.get();
|
||||||
|
|
||||||
const pageIndex = arr.findIndex( (item) => item.id == pageId );
|
const pageIndex = arr.findIndex( (item) => item.id == pageId );
|
||||||
|
@ -148,7 +149,7 @@ class PagesFlatArray {
|
||||||
* @param orders - all page orders
|
* @param orders - all page orders
|
||||||
* @returns {Promise<Array<PagesFlatArrayData>>}
|
* @returns {Promise<Array<PagesFlatArrayData>>}
|
||||||
*/
|
*/
|
||||||
private static getChildrenFlatArray(pageId: string, level: number,
|
private static getChildrenFlatArray(pageId: EntityId, level: number,
|
||||||
pages: Array<Page>, orders: Array<PageOrder>): Array<PagesFlatArrayData> {
|
pages: Array<Page>, orders: Array<PageOrder>): Array<PagesFlatArrayData> {
|
||||||
let arr: Array<PagesFlatArrayData> = new Array<PagesFlatArrayData>();
|
let arr: Array<PagesFlatArrayData> = new Array<PagesFlatArrayData>();
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import express, { Request, Response } from 'express';
|
||||||
import multerFunc from 'multer';
|
import multerFunc from 'multer';
|
||||||
import Pages from '../../controllers/pages.js';
|
import Pages from '../../controllers/pages.js';
|
||||||
import PagesOrder from '../../controllers/pagesOrder.js';
|
import PagesOrder from '../../controllers/pagesOrder.js';
|
||||||
|
import { EntityId } from '../../utils/database/types.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const multer = multerFunc();
|
const multer = multerFunc();
|
||||||
|
@ -35,7 +36,7 @@ router.get('/page/:id', async (req: Request, res: Response) => {
|
||||||
*/
|
*/
|
||||||
router.get('/pages', async (req: Request, res: Response) => {
|
router.get('/pages', async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const pages = await Pages.getAll();
|
const pages = await Pages.getAllPages();
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
|
@ -92,7 +93,7 @@ router.post('/page/:id', multer.none(), async (req: Request, res: Response) => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { title, body, parent, putAbovePageId, uri } = req.body;
|
const { title, body, parent, putAbovePageId, uri } = req.body;
|
||||||
const pages = await Pages.getAll();
|
const pages = await Pages.getAllPages();
|
||||||
let page = await Pages.get(id);
|
let page = await Pages.get(id);
|
||||||
|
|
||||||
if (page._id === undefined) {
|
if (page._id === undefined) {
|
||||||
|
@ -177,8 +178,8 @@ router.delete('/page/:id', async (req: Request, res: Response) => {
|
||||||
* @param {string} startFrom - start point to delete
|
* @param {string} startFrom - start point to delete
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
const deleteRecursively = async (startFrom: string): Promise<void> => {
|
const deleteRecursively = async (startFrom: EntityId): Promise<void> => {
|
||||||
let order: string[] = [];
|
let order: EntityId[] = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const children = await PagesOrder.get(startFrom);
|
const children = await PagesOrder.get(startFrom);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import mime from 'mime';
|
||||||
import mkdirp from 'mkdirp';
|
import mkdirp from 'mkdirp';
|
||||||
import Transport from '../../controllers/transport.js';
|
import Transport from '../../controllers/transport.js';
|
||||||
import { random16 } from '../../utils/crypto.js';
|
import { random16 } from '../../utils/crypto.js';
|
||||||
import appConfig from "../../utils/appConfig.js";
|
import appConfig from '../../utils/appConfig.js';
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import express, { Request, Response } from 'express';
|
import express, { Request, Response } from 'express';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import csrf from 'csurf';
|
import csrf from 'csurf';
|
||||||
import appConfig from "../utils/appConfig.js";
|
import appConfig from '../utils/appConfig.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
const csrfProtection = csrf({ cookie: true });
|
const csrfProtection = csrf({ cookie: true });
|
||||||
|
|
|
@ -4,6 +4,8 @@ import PagesOrder from '../../controllers/pagesOrder.js';
|
||||||
import Page from '../../models/page.js';
|
import Page from '../../models/page.js';
|
||||||
import asyncMiddleware from '../../utils/asyncMiddleware.js';
|
import asyncMiddleware from '../../utils/asyncMiddleware.js';
|
||||||
import PageOrder from '../../models/pageOrder.js';
|
import PageOrder from '../../models/pageOrder.js';
|
||||||
|
import { EntityId } from '../../utils/database/types.js';
|
||||||
|
import {isEqualIds} from "../../utils/database/index.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process one-level pages list to parent-children list
|
* Process one-level pages list to parent-children list
|
||||||
|
@ -13,11 +15,10 @@ import PageOrder from '../../models/pageOrder.js';
|
||||||
* @param {PagesOrder[]} pagesOrder - list of pages order
|
* @param {PagesOrder[]} pagesOrder - list of pages order
|
||||||
* @param {number} level - max level recursion
|
* @param {number} level - max level recursion
|
||||||
* @param {number} currentLevel - current level of element
|
* @param {number} currentLevel - current level of element
|
||||||
*
|
|
||||||
* @returns {Page[]}
|
* @returns {Page[]}
|
||||||
*/
|
*/
|
||||||
function createMenuTree(parentPageId: string, pages: Page[], pagesOrder: PageOrder[], level = 1, currentLevel = 1): Page[] {
|
function createMenuTree(parentPageId: string, pages: Page[], pagesOrder: PageOrder[], level = 1, currentLevel = 1): Page[] {
|
||||||
const childrenOrder = pagesOrder.find(order => order.data.page === parentPageId);
|
const childrenOrder = pagesOrder.find(order => isEqualIds(order.data.page, parentPageId));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* branch is a page children in tree
|
* branch is a page children in tree
|
||||||
|
@ -27,12 +28,12 @@ function createMenuTree(parentPageId: string, pages: Page[], pagesOrder: PageOrd
|
||||||
let ordered: any[] = [];
|
let ordered: any[] = [];
|
||||||
|
|
||||||
if (childrenOrder) {
|
if (childrenOrder) {
|
||||||
ordered = childrenOrder.order.map((pageId: string) => {
|
ordered = childrenOrder.order.map((pageId: EntityId) => {
|
||||||
return pages.find(page => page._id === pageId);
|
return pages.find(page => isEqualIds(page._id, pageId));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const unordered = pages.filter(page => page._parent === parentPageId);
|
const unordered = pages.filter(page => isEqualIds(page._parent, parentPageId));
|
||||||
const branch = Array.from(new Set([...ordered, ...unordered]));
|
const branch = Array.from(new Set([...ordered, ...unordered]));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,7 +69,7 @@ export default asyncMiddleware(async (req: Request, res: Response, next: NextFun
|
||||||
const parentIdOfRootPages = '0';
|
const parentIdOfRootPages = '0';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const pages = await Pages.getAll();
|
const pages = await Pages.getAllPages();
|
||||||
const pagesOrder = await PagesOrder.getAll();
|
const pagesOrder = await PagesOrder.getAll();
|
||||||
|
|
||||||
res.locals.menu = createMenuTree(parentIdOfRootPages, pages, pagesOrder, 2);
|
res.locals.menu = createMenuTree(parentIdOfRootPages, pages, pagesOrder, 2);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { NextFunction, Request, Response } from 'express';
|
import { NextFunction, Request, Response } from 'express';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import appConfig from "../../utils/appConfig.js";
|
import appConfig from '../../utils/appConfig.js';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,6 +4,7 @@ import PagesOrder from '../controllers/pagesOrder.js';
|
||||||
import verifyToken from './middlewares/token.js';
|
import verifyToken from './middlewares/token.js';
|
||||||
import allowEdit from './middlewares/locals.js';
|
import allowEdit from './middlewares/locals.js';
|
||||||
import PagesFlatArray from '../models/pagesFlatArray.js';
|
import PagesFlatArray from '../models/pagesFlatArray.js';
|
||||||
|
import { toEntityId } from '../utils/database/index.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
|
@ -28,7 +29,7 @@ router.get('/page/new', verifyToken, allowEdit, async (req: Request, res: Respon
|
||||||
* Edit page form
|
* Edit page form
|
||||||
*/
|
*/
|
||||||
router.get('/page/edit/:id', verifyToken, allowEdit, async (req: Request, res: Response, next: NextFunction) => {
|
router.get('/page/edit/:id', verifyToken, allowEdit, async (req: Request, res: Response, next: NextFunction) => {
|
||||||
const pageId = req.params.id;
|
const pageId = toEntityId(req.params.id);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const page = await Pages.get(pageId);
|
const page = await Pages.get(pageId);
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { loadConfig } from 'config-loader';
|
||||||
import * as process from 'process';
|
import * as process from 'process';
|
||||||
import arg from 'arg';
|
import arg from 'arg';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { z } from "zod";
|
import { z } from 'zod';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration for Hawk errors catcher
|
* Configuration for Hawk errors catcher
|
||||||
|
@ -10,25 +10,25 @@ import { z } from "zod";
|
||||||
const HawkConfig = z.object({
|
const HawkConfig = z.object({
|
||||||
backendToken: z.string().optional(), // Hawk backend token
|
backendToken: z.string().optional(), // Hawk backend token
|
||||||
frontendToken: z.string().optional(), // Hawk frontend token
|
frontendToken: z.string().optional(), // Hawk frontend token
|
||||||
})
|
});
|
||||||
|
|
||||||
const LocalDatabaseConfig = z.object({
|
const LocalDatabaseConfig = z.object({
|
||||||
driver: z.literal('local'),
|
driver: z.literal('local'),
|
||||||
local: z.object({
|
local: z.object({
|
||||||
path: z.string()
|
path: z.string(),
|
||||||
})
|
}),
|
||||||
})
|
});
|
||||||
|
|
||||||
const MongoDatabaseConfig = z.object({
|
const MongoDatabaseConfig = z.object({
|
||||||
driver: z.literal('mongodb'),
|
driver: z.literal('mongodb'),
|
||||||
mongodb: z.object({
|
mongodb: z.object({
|
||||||
uri: z.string()
|
uri: z.string(),
|
||||||
})
|
}),
|
||||||
})
|
});
|
||||||
|
|
||||||
const AuthConfig = z.object({
|
const AuthConfig = z.object({
|
||||||
secret: z.string() // Secret for JWT
|
secret: z.string(), // Secret for JWT
|
||||||
})
|
});
|
||||||
|
|
||||||
const FrontendConfig = z.object({
|
const FrontendConfig = z.object({
|
||||||
title: z.string(), // Title for pages
|
title: z.string(), // Title for pages
|
||||||
|
@ -40,8 +40,9 @@ const FrontendConfig = z.object({
|
||||||
serve: z.string().optional(), // Carbon serve url
|
serve: z.string().optional(), // Carbon serve url
|
||||||
placement: z.string().optional(), // Carbon placement
|
placement: z.string().optional(), // Carbon placement
|
||||||
}),
|
}),
|
||||||
menu: z.array(z.union([z.string(), z.object({title: z.string(), uri: z.string()})])), // Menu for pages
|
menu: z.array(z.union([z.string(), z.object({ title: z.string(),
|
||||||
})
|
uri: z.string() })])), // Menu for pages
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Application configuration
|
* Application configuration
|
||||||
|
@ -56,7 +57,7 @@ const AppConfig = z.object({
|
||||||
frontend: FrontendConfig, // Frontend configuration
|
frontend: FrontendConfig, // Frontend configuration
|
||||||
auth: AuthConfig, // Auth configuration
|
auth: AuthConfig, // Auth configuration
|
||||||
database: z.union([LocalDatabaseConfig, MongoDatabaseConfig]), // Database configuration
|
database: z.union([LocalDatabaseConfig, MongoDatabaseConfig]), // Database configuration
|
||||||
})
|
});
|
||||||
|
|
||||||
export type AppConfig = z.infer<typeof AppConfig>;
|
export type AppConfig = z.infer<typeof AppConfig>;
|
||||||
|
|
||||||
|
@ -76,6 +77,6 @@ const paths = (args['--config'] || ['./app-config.yaml']).map((configPath) => {
|
||||||
|
|
||||||
const loadedConfig = loadConfig<AppConfig>(...paths);
|
const loadedConfig = loadConfig<AppConfig>(...paths);
|
||||||
|
|
||||||
const appConfig = AppConfig.parse(loadedConfig)
|
const appConfig = AppConfig.parse(loadedConfig);
|
||||||
|
|
||||||
export default appConfig;
|
export default appConfig;
|
||||||
|
|
|
@ -2,12 +2,26 @@ import { AliasData } from '../../models/alias.js';
|
||||||
import { FileData } from '../../models/file.js';
|
import { FileData } from '../../models/file.js';
|
||||||
import { PageData } from '../../models/page.js';
|
import { PageData } from '../../models/page.js';
|
||||||
import { PageOrderData } from '../../models/pageOrder.js';
|
import { PageOrderData } from '../../models/pageOrder.js';
|
||||||
import appConfig from "../appConfig.js";
|
import appConfig from '../appConfig.js';
|
||||||
import LocalDatabaseDriver from "./local.js";
|
import LocalDatabaseDriver from './local.js';
|
||||||
import MongoDatabaseDriver from "./mongodb.js";
|
import MongoDatabaseDriver from './mongodb.js';
|
||||||
|
import { EntityId } from './types.js';
|
||||||
|
import { ObjectId } from 'mongodb';
|
||||||
|
|
||||||
const Database = appConfig.database.driver === 'mongodb' ? MongoDatabaseDriver : LocalDatabaseDriver;
|
const Database = appConfig.database.driver === 'mongodb' ? MongoDatabaseDriver : LocalDatabaseDriver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
export function toEntityId(id: string): EntityId {
|
||||||
|
return appConfig.database.driver === 'mongodb' ? new ObjectId(id) : id;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isEqualIds(id1?: EntityId, id2?: EntityId): boolean {
|
||||||
|
return id1?.toString() === id2?.toString();
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
pages: new Database<PageData>('pages'),
|
pages: new Database<PageData>('pages'),
|
||||||
aliases: new Database<AliasData>('aliases'),
|
aliases: new Database<AliasData>('aliases'),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Datastore from "nedb";
|
import Datastore from 'nedb';
|
||||||
import {DatabaseDriver, Options, RejectFunction, ResolveFunction} from "./types.js";
|
import { DatabaseDriver, Options, RejectFunction, ResolveFunction } from './types.js';
|
||||||
import path from "path";
|
import path from 'path';
|
||||||
import appConfig from "../appConfig.js";
|
import appConfig from '../appConfig.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init function for nedb instance
|
* Init function for nedb instance
|
||||||
|
@ -11,9 +11,11 @@ import appConfig from "../appConfig.js";
|
||||||
*/
|
*/
|
||||||
function initDb(name: string): Datastore {
|
function initDb(name: string): Datastore {
|
||||||
const dbConfig = appConfig.database.driver === 'local' ? appConfig.database.local : null;
|
const dbConfig = appConfig.database.driver === 'local' ? appConfig.database.local : null;
|
||||||
|
|
||||||
if (!dbConfig) {
|
if (!dbConfig) {
|
||||||
throw new Error('Database config is not initialized');
|
throw new Error('Database config is not initialized');
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Datastore({
|
return new Datastore({
|
||||||
filename: path.resolve(`${dbConfig.path}/${name}.db`),
|
filename: path.resolve(`${dbConfig.path}/${name}.db`),
|
||||||
autoload: true,
|
autoload: true,
|
||||||
|
@ -41,9 +43,8 @@ export default class LocalDatabaseDriver<DocType> implements DatabaseDriver<DocT
|
||||||
* Insert new document into the database
|
* Insert new document into the database
|
||||||
*
|
*
|
||||||
* @see https://github.com/louischatriot/nedb#inserting-documents
|
* @see https://github.com/louischatriot/nedb#inserting-documents
|
||||||
*
|
* @param {object} doc - object to insert
|
||||||
* @param {Object} doc - object to insert
|
* @returns {Promise<object | Error>} - inserted doc or Error object
|
||||||
* @returns {Promise<Object|Error>} - inserted doc or Error object
|
|
||||||
*/
|
*/
|
||||||
public async insert(doc: DocType): Promise<DocType> {
|
public async insert(doc: DocType): Promise<DocType> {
|
||||||
return new Promise((resolve, reject) => this.db.insert(doc, (err, newDoc) => {
|
return new Promise((resolve, reject) => this.db.insert(doc, (err, newDoc) => {
|
||||||
|
@ -59,10 +60,9 @@ export default class LocalDatabaseDriver<DocType> implements DatabaseDriver<DocT
|
||||||
* Find documents that match passed query
|
* Find documents that match passed query
|
||||||
*
|
*
|
||||||
* @see https://github.com/louischatriot/nedb#finding-documents
|
* @see https://github.com/louischatriot/nedb#finding-documents
|
||||||
*
|
* @param {object} query - query object
|
||||||
* @param {Object} query - query object
|
* @param {object} projection - projection object
|
||||||
* @param {Object} projection - projection object
|
* @returns {Promise<Array<object> | Error>} - found docs or Error object
|
||||||
* @returns {Promise<Array<Object>|Error>} - found docs or Error object
|
|
||||||
*/
|
*/
|
||||||
public async find(query: Record<string, unknown>, projection?: DocType): Promise<Array<DocType>> {
|
public async find(query: Record<string, unknown>, projection?: DocType): Promise<Array<DocType>> {
|
||||||
const cbk = (resolve: ResolveFunction, reject: RejectFunction) => (err: Error | null, docs: DocType[]) => {
|
const cbk = (resolve: ResolveFunction, reject: RejectFunction) => (err: Error | null, docs: DocType[]) => {
|
||||||
|
@ -86,10 +86,9 @@ export default class LocalDatabaseDriver<DocType> implements DatabaseDriver<DocT
|
||||||
* Find one document matches passed query
|
* Find one document matches passed query
|
||||||
*
|
*
|
||||||
* @see https://github.com/louischatriot/nedb#finding-documents
|
* @see https://github.com/louischatriot/nedb#finding-documents
|
||||||
*
|
* @param {object} query - query object
|
||||||
* @param {Object} query - query object
|
* @param {object} projection - projection object
|
||||||
* @param {Object} projection - projection object
|
* @returns {Promise<object | Error>} - found doc or Error object
|
||||||
* @returns {Promise<Object|Error>} - found doc or Error object
|
|
||||||
*/
|
*/
|
||||||
public async findOne(query: Record<string, unknown>, projection?: DocType): Promise<DocType> {
|
public async findOne(query: Record<string, unknown>, projection?: DocType): Promise<DocType> {
|
||||||
const cbk = (resolve: ResolveFunction, reject: RejectFunction) => (err: Error | null, doc: DocType) => {
|
const cbk = (resolve: ResolveFunction, reject: RejectFunction) => (err: Error | null, doc: DocType) => {
|
||||||
|
@ -113,11 +112,10 @@ export default class LocalDatabaseDriver<DocType> implements DatabaseDriver<DocT
|
||||||
* Update document matches query
|
* Update document matches query
|
||||||
*
|
*
|
||||||
* @see https://github.com/louischatriot/nedb#updating-documents
|
* @see https://github.com/louischatriot/nedb#updating-documents
|
||||||
*
|
* @param {object} query - query object
|
||||||
* @param {Object} query - query object
|
* @param {object} update - fields to update
|
||||||
* @param {Object} update - fields to update
|
|
||||||
* @param {Options} options - optional params
|
* @param {Options} options - optional params
|
||||||
* @returns {Promise<number|Object|Object[]|Error>} - number of updated rows or affected docs or Error object
|
* @returns {Promise<number | object | object[] | Error>} - number of updated rows or affected docs or Error object
|
||||||
*/
|
*/
|
||||||
public async update(query: Record<string, unknown>, update: DocType, options: Options = {}): Promise<number|boolean|Array<DocType>> {
|
public async update(query: Record<string, unknown>, update: DocType, options: Options = {}): Promise<number|boolean|Array<DocType>> {
|
||||||
return new Promise((resolve, reject) => this.db.update(query, update, options, (err, result, affectedDocs) => {
|
return new Promise((resolve, reject) => this.db.update(query, update, options, (err, result, affectedDocs) => {
|
||||||
|
@ -145,8 +143,7 @@ export default class LocalDatabaseDriver<DocType> implements DatabaseDriver<DocT
|
||||||
* Remove document matches passed query
|
* Remove document matches passed query
|
||||||
*
|
*
|
||||||
* @see https://github.com/louischatriot/nedb#removing-documents
|
* @see https://github.com/louischatriot/nedb#removing-documents
|
||||||
*
|
* @param {object} query - query object
|
||||||
* @param {Object} query - query object
|
|
||||||
* @param {Options} options - optional params
|
* @param {Options} options - optional params
|
||||||
* @returns {Promise<number|Error>} - number of removed rows or Error object
|
* @returns {Promise<number|Error>} - number of removed rows or Error object
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Collection, Filter, MongoClient, OptionalUnlessRequiredId, UpdateFilter } from 'mongodb';
|
import { Collection, Filter, MongoClient, OptionalUnlessRequiredId, UpdateFilter } from 'mongodb';
|
||||||
import {DatabaseDriver, Options} from "./types.js";
|
import { DatabaseDriver, Options } from './types.js';
|
||||||
import appConfig from "../appConfig.js";
|
import appConfig from '../appConfig.js';
|
||||||
|
|
||||||
const mongodbUri = appConfig.database.driver === 'mongodb' ? appConfig.database.mongodb.uri : null;
|
const mongodbUri = appConfig.database.driver === 'mongodb' ? appConfig.database.mongodb.uri : null;
|
||||||
const mongodbClient = mongodbUri ? await MongoClient.connect(mongodbUri): null;
|
const mongodbClient = mongodbUri ? await MongoClient.connect(mongodbUri): null;
|
||||||
|
@ -15,6 +15,10 @@ export default class MongoDatabaseDriver<DocType> implements DatabaseDriver<DocT
|
||||||
private db: MongoClient;
|
private db: MongoClient;
|
||||||
private collection: Collection<DocType>;
|
private collection: Collection<DocType>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param collectionName
|
||||||
|
*/
|
||||||
constructor(collectionName: string) {
|
constructor(collectionName: string) {
|
||||||
if (!mongodbClient) {
|
if (!mongodbClient) {
|
||||||
throw new Error('MongoDB client is not initialized');
|
throw new Error('MongoDB client is not initialized');
|
||||||
|
@ -26,47 +30,46 @@ export default class MongoDatabaseDriver<DocType> implements DatabaseDriver<DocT
|
||||||
/**
|
/**
|
||||||
* Insert new document into the database
|
* Insert new document into the database
|
||||||
*
|
*
|
||||||
* @param {Object} doc - object to insert
|
* @param {object} doc - object to insert
|
||||||
* @returns {Promise<Object|Error>} - inserted doc or Error object
|
* @returns {Promise<object | Error>} - inserted doc or Error object
|
||||||
*/
|
*/
|
||||||
public async insert(doc: DocType): Promise<DocType> {
|
public async insert(doc: DocType): Promise<DocType> {
|
||||||
const result = await this.collection.insertOne(doc as OptionalUnlessRequiredId<DocType>);
|
const result = await this.collection.insertOne(doc as OptionalUnlessRequiredId<DocType>);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...doc,
|
...doc,
|
||||||
_id: result.insertedId,
|
_id: result.insertedId,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find documents that match passed query
|
* Find documents that match passed query
|
||||||
*
|
*
|
||||||
* @param {Object} query - query object
|
* @param {object} query - query object
|
||||||
* @param {Object} projection - projection object
|
* @param {object} projection - projection object
|
||||||
* @returns {Promise<Array<Object>|Error>} - found docs or Error object
|
* @returns {Promise<Array<object> | Error>} - found docs or Error object
|
||||||
*/
|
*/
|
||||||
public async find(query: Record<string, unknown>, projection?: DocType): Promise<Array<DocType>> {
|
public async find(query: Record<string, unknown>, projection?: DocType): Promise<Array<DocType>> {
|
||||||
const cursor = this.collection.find(query as Filter<DocType>)
|
const cursor = this.collection.find(query as Filter<DocType>);
|
||||||
|
|
||||||
if (projection) {
|
if (projection) {
|
||||||
cursor.project(projection);
|
cursor.project(projection);
|
||||||
}
|
}
|
||||||
|
|
||||||
const docs = await cursor.toArray();
|
const docs = await cursor.toArray();
|
||||||
|
|
||||||
return docs as unknown as Array<DocType>;
|
return docs as unknown as Array<DocType>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find one document matches passed query
|
* Find one document matches passed query
|
||||||
*
|
*
|
||||||
* @param {Object} query - query object
|
* @param {object} query - query object
|
||||||
* @param {Object} projection - projection object
|
* @param {object} projection - projection object
|
||||||
* @returns {Promise<Object|Error>} - found doc or Error object
|
* @returns {Promise<object | Error>} - found doc or Error object
|
||||||
*/
|
*/
|
||||||
public async findOne(query: Record<string, unknown>, projection?: DocType): Promise<DocType> {
|
public async findOne(query: Record<string, unknown>, projection?: DocType): Promise<DocType> {
|
||||||
const doc = await this.collection.findOne(query as Filter<DocType>, { projection });
|
const doc = await this.collection.findOne(query as Filter<DocType>, { projection });
|
||||||
if (!doc) {
|
|
||||||
throw new Error('Document not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
return doc as unknown as DocType;
|
return doc as unknown as DocType;
|
||||||
}
|
}
|
||||||
|
@ -74,39 +77,41 @@ export default class MongoDatabaseDriver<DocType> implements DatabaseDriver<DocT
|
||||||
/**
|
/**
|
||||||
* Update document matches query
|
* Update document matches query
|
||||||
*
|
*
|
||||||
* @param {Object} query - query object
|
* @param {object} query - query object
|
||||||
* @param {Object} update - fields to update
|
* @param {object} update - fields to update
|
||||||
* @param {Options} options - optional params
|
* @param {Options} options - optional params
|
||||||
* @returns {Promise<number|Object|Object[]|Error>} - number of updated rows or affected docs or Error object
|
* @returns {Promise<number | object | object[] | Error>} - number of updated rows or affected docs or Error object
|
||||||
*/
|
*/
|
||||||
public async update(query: Record<string, unknown>, update: DocType, options: Options = {}): Promise<number|boolean|Array<DocType>> {
|
public async update(query: Record<string, unknown>, update: DocType, options: Options = {}): Promise<number|boolean|Array<DocType>> {
|
||||||
const updateDocument = {
|
const updateDocument = {
|
||||||
$set: update
|
$set: update,
|
||||||
} as UpdateFilter<DocType>;
|
} as UpdateFilter<DocType>;
|
||||||
const result = await this.collection.updateMany(query as Filter<DocType>, updateDocument, options);
|
const result = await this.collection.updateMany(query as Filter<DocType>, updateDocument, options);
|
||||||
|
|
||||||
switch (true) {
|
switch (true) {
|
||||||
case options.returnUpdatedDocs:
|
case options.returnUpdatedDocs:
|
||||||
return result.modifiedCount
|
return result.modifiedCount;
|
||||||
case options.upsert:
|
case options.upsert:
|
||||||
if (result.modifiedCount) {
|
if (result.modifiedCount) {
|
||||||
return result.modifiedCount;
|
return result.modifiedCount;
|
||||||
}
|
}
|
||||||
return result as DocType[]
|
|
||||||
|
return result as DocType[];
|
||||||
default:
|
default:
|
||||||
return result as DocType[]
|
return result as DocType[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove document matches passed query
|
* Remove document matches passed query
|
||||||
*
|
*
|
||||||
* @param {Object} query - query object
|
* @param {object} query - query object
|
||||||
* @param {Options} options - optional params
|
* @param {Options} options - optional params
|
||||||
* @returns {Promise<number|Error>} - number of removed rows or Error object
|
* @returns {Promise<number|Error>} - number of removed rows or Error object
|
||||||
*/
|
*/
|
||||||
public async remove(query: Record<string, unknown>, options: Options = {}): Promise<number> {
|
public async remove(query: Record<string, unknown>, options: Options = {}): Promise<number> {
|
||||||
const result = await this.collection.deleteMany(query as Filter<DocType>);
|
const result = await this.collection.deleteMany(query as Filter<DocType>);
|
||||||
|
|
||||||
return result.deletedCount;
|
return result.deletedCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { ObjectId } from 'mongodb';
|
||||||
|
|
||||||
export interface DatabaseDriver<DocType> {
|
export interface DatabaseDriver<DocType> {
|
||||||
insert(doc: DocType): Promise<DocType>;
|
insert(doc: DocType): Promise<DocType>;
|
||||||
find(query: Record<string, unknown>, projection?: DocType): Promise<Array<DocType>>;
|
find(query: Record<string, unknown>, projection?: DocType): Promise<Array<DocType>>;
|
||||||
|
@ -6,6 +8,8 @@ export interface DatabaseDriver<DocType> {
|
||||||
remove(query: Record<string, unknown>, options: Options): Promise<number>
|
remove(query: Record<string, unknown>, options: Options): Promise<number>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type EntityId = string | ObjectId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef Options - optional params
|
* @typedef Options - optional params
|
||||||
* @param {boolean} multi - (false) allows to take action to several documents
|
* @param {boolean} multi - (false) allows to take action to several documents
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue