mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-07-21 22:29:40 +02:00
Typescript rewrite (#147)
* Updated highlight.js * Update .codexdocsrc.sample remove undefined page for a fresh new install * backend rewritten in TS * test -> TS, .added dockerignore, bug fixed * Removed compiled js files, eslint codex/ts added * fixed jsdocs warning, leaving editor confirmation * use path.resolve for DB paths * db drives updated + fixed User model * redundant cleared + style fixed * explicit type fixing * fixing testing code * added body block type * compiled JS files -> dist, fixed compiling errors * fixed compiling error, re-organized ts source code * updated Dockerfile * fixed link to parent page * up nodejs version * fix package name * fix deps Co-authored-by: nvc8996 <nvc.8996@gmail.com> Co-authored-by: Taly <vitalik7tv@yandex.ru>
This commit is contained in:
parent
059cfb96f9
commit
34514761f5
99 changed files with 3817 additions and 2249 deletions
208
src/backend/controllers/pages.ts
Normal file
208
src/backend/controllers/pages.ts
Normal file
|
@ -0,0 +1,208 @@
|
|||
import Page, { PageData } from '../models/page';
|
||||
import Alias from '../models/alias';
|
||||
|
||||
type PageDataFields = keyof PageData;
|
||||
|
||||
/**
|
||||
* @class Pages
|
||||
* @classdesc Pages controller
|
||||
*/
|
||||
class Pages {
|
||||
/**
|
||||
* Fields required for page model creation
|
||||
*
|
||||
* @returns {['title', 'body']}
|
||||
*/
|
||||
public static get REQUIRED_FIELDS(): Array<PageDataFields> {
|
||||
return [ 'body' ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return page model with passed id
|
||||
*
|
||||
* @param {string} id - page id
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
public static async get(id: string): Promise<Page> {
|
||||
const page = await Page.get(id);
|
||||
|
||||
if (!page._id) {
|
||||
throw new Error('Page with given id does not exist');
|
||||
}
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all pages
|
||||
*
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
public static async getAll(): Promise<Page[]> {
|
||||
return Page.getAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all pages without children of passed page
|
||||
*
|
||||
* @param {string} parent - id of current page
|
||||
* @returns {Promise<Page[]>}
|
||||
*/
|
||||
public static async getAllExceptChildren(parent: string): Promise<Page[]> {
|
||||
const pagesAvailable = this.removeChildren(await Pages.getAll(), parent);
|
||||
|
||||
const nullFilteredPages: Page[] = [];
|
||||
|
||||
pagesAvailable.forEach(async item => {
|
||||
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>}
|
||||
*/
|
||||
public 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 - info about page
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
public static async insert(data: PageData): Promise<Page> {
|
||||
try {
|
||||
Pages.validate(data);
|
||||
|
||||
const page = new Page(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 (e) {
|
||||
throw new Error('validationError');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update page with given id in the database
|
||||
*
|
||||
* @param {string} id - page id
|
||||
* @param {PageData} data - info about page
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
public static async update(id: string, data: PageData): Promise<Page> {
|
||||
const page = await Page.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();
|
||||
}
|
||||
|
||||
if (previousUri) {
|
||||
Alias.markAsDeprecated(previousUri);
|
||||
}
|
||||
}
|
||||
|
||||
return updatedPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove page with given id from the database
|
||||
*
|
||||
* @param {string} id - page id
|
||||
* @returns {Promise<Page>}
|
||||
*/
|
||||
public static async remove(id: string): Promise<Page> {
|
||||
const page = await Page.get(id);
|
||||
|
||||
if (!page._id) {
|
||||
throw new Error('Page with given id does not exist');
|
||||
}
|
||||
|
||||
if (page.uri) {
|
||||
const alias = await Alias.get(page.uri);
|
||||
|
||||
await alias.destroy();
|
||||
}
|
||||
|
||||
return page.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check PageData object for required fields
|
||||
*
|
||||
* @param {PageData} data - info about page
|
||||
* @throws {Error} - validation error
|
||||
*/
|
||||
private static validate(data: PageData): void {
|
||||
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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Pages;
|
Loading…
Add table
Add a link
Reference in a new issue