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

explicit type fixing

This commit is contained in:
nvc8996 2021-09-16 17:13:40 +03:00
parent 00d6edbc54
commit 6e5af27f8f
18 changed files with 177 additions and 140 deletions

View file

@ -1,7 +1,8 @@
import Model, { PageData } from '../models/page';
import Page from '../models/page';
import Page, { PageData } from '../models/page';
import Alias from '../models/alias';
type PageDataFields = keyof PageData;
/**
* @class Pages
* @classdesc Pages controller
@ -12,7 +13,7 @@ class Pages {
*
* @returns {['title', 'body']}
*/
public static get REQUIRED_FIELDS(): Array<string> {
public static get REQUIRED_FIELDS(): Array<PageDataFields> {
return [ 'body' ];
}
@ -23,7 +24,7 @@ class Pages {
* @returns {Promise<Page>}
*/
public static async get(id: string): Promise<Page> {
const page = await Model.get(id);
const page = await Page.get(id);
if (!page._id) {
throw new Error('Page with given id does not exist');
@ -38,7 +39,7 @@ class Pages {
* @returns {Promise<Page[]>}
*/
public static async getAll(): Promise<Page[]> {
return Model.getAll();
return Page.getAll();
}
/**
@ -90,7 +91,7 @@ class Pages {
try {
Pages.validate(data);
const page = new Model(data);
const page = new Page(data);
const insertedPage = await page.save();
@ -117,7 +118,7 @@ class Pages {
* @returns {Promise<Page>}
*/
public static async update(id: string, data: PageData): Promise<Page> {
const page = await Model.get(id);
const page = await Page.get(id);
const previousUri = page.uri;
if (!page._id) {
@ -141,7 +142,9 @@ class Pages {
alias.save();
}
Alias.markAsDeprecated(previousUri);
if (previousUri) {
Alias.markAsDeprecated(previousUri);
}
}
return updatedPage;
@ -154,15 +157,17 @@ class Pages {
* @returns {Promise<Page>}
*/
public static async remove(id: string): Promise<Page> {
const page = await Model.get(id);
const page = await Page.get(id);
if (!page._id) {
throw new Error('Page with given id does not exist');
}
const alias = await Alias.get(page.uri);
if (page.uri) {
const alias = await Alias.get(page.uri);
await alias.destroy();
await alias.destroy();
}
return page.destroy();
}

View file

@ -1,4 +1,3 @@
import Model from '../models/pageOrder';
import PageOrder from '../models/pageOrder';
import Page from '../models/page';
@ -16,7 +15,7 @@ class PagesOrder {
* @returns {Promise<PageOrder>}
*/
public static async get(parentId: string): Promise<PageOrder> {
const order = await Model.get(parentId);
const order = await PageOrder.get(parentId);
if (!order._id) {
throw new Error('Page with given id does not contain order');
@ -31,7 +30,7 @@ class PagesOrder {
* @returns {Promise<PageOrder[]>}
*/
public static async getAll(): Promise<PageOrder[]> {
return Model.getAll();
return PageOrder.getAll();
}
/**
@ -41,7 +40,7 @@ class PagesOrder {
* @param {string} childId - new page pushed to the order
*/
public static async push(parentId: string, childId: string): Promise<void> {
const order = await Model.get(parentId);
const order = await PageOrder.get(parentId);
order.push(childId);
await order.save();
@ -55,12 +54,12 @@ class PagesOrder {
* @param {string} targetPageId - page's id which is changing the parent page
*/
public static async move(oldParentId: string, newParentId: string, targetPageId: string): Promise<void> {
const oldParentOrder = await Model.get(oldParentId);
const oldParentOrder = await PageOrder.get(oldParentId);
oldParentOrder.remove(targetPageId);
await oldParentOrder.save();
const newParentOrder = await Model.get(newParentId);
const newParentOrder = await PageOrder.get(newParentId);
newParentOrder.push(targetPageId);
await newParentOrder.save();
@ -76,7 +75,7 @@ class PagesOrder {
* @returns {Page[]}
*/
public static async getOrderedChildren(pages: Page[], currentPageId: string, parentPageId: string, ignoreSelf = false): Promise<Page[]> {
const children = await Model.get(parentPageId);
const children = await PageOrder.get(parentPageId);
const unordered = pages.filter(page => page._parent === parentPageId).map(page => page._id);
// Create unique array with ordered and unordered pages id
@ -102,7 +101,7 @@ class PagesOrder {
* @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> {
const pageOrder = await Model.get(parentPageId);
const pageOrder = await PageOrder.get(parentPageId);
// Create unique array with ordered and unordered pages id
pageOrder.order = Array.from(new Set([...pageOrder.order, ...unordered]));
@ -115,7 +114,7 @@ class PagesOrder {
* @returns {Promise<void>}
*/
public static async remove(parentId: string): Promise<void> {
const order = await Model.get(parentId);
const order = await PageOrder.get(parentId);
if (!order._id) {
throw new Error('Page with given id does not contain order');

View file

@ -72,12 +72,23 @@ class Transport {
fs.writeFileSync(`${config.get('uploads')}/${filename}.${ext}`, buffer);
const fetchedContentType: string | null = fetchedFile.headers.get('content-type');
let fetchedMimeType: string|undefined;
if (fetchedContentType === null) {
fetchedMimeType = undefined;
}else{
fetchedMimeType = fetchedContentType;
}
const mimeType = type ? type.mime : fetchedMimeType;
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'),
mimetype: mimeType,
});
await file.save();

View file

@ -1,5 +1,4 @@
import Model from '../models/user';
import UserData from '../models/user';
import User from '../models/user';
/**
* @class Users
@ -9,18 +8,12 @@ class Users {
/**
* Find and return user model.
*
* @returns {Promise<UserData>}
* @returns {Promise<User>}
*/
public static get(): Promise<UserData> {
return new Promise((resolve, reject) => {
Model.get()
.then( userDoc => {
resolve(userDoc);
})
.catch( (e) => {
reject(e);
});
});
public static async get(): Promise<User> {
const userData: User = await User.get();
return userData;
}
}

View file

@ -1,5 +1,6 @@
/**
* HttpException class for middleware
*
* @property {number} status - exception status code
* @property {string} message - detail about the exception
*/

View file

@ -13,7 +13,7 @@ const aliasesDb = database['aliases'];
* @property {string} id - entity id
*
*/
interface AliasData {
export interface AliasData {
_id?: string;
hash?: string;
type?: string;

View file

@ -17,9 +17,9 @@ export interface FileData {
name?: string;
filename?: string;
path?: string;
mimetype?: string | null;
mimetype?: string;
size?: number;
[key: string]: any;
[key: string]: string | number | undefined;
}
/**
@ -64,7 +64,7 @@ class File {
* @returns {Promise<File>}
*/
public static async get(_id: string): Promise<File> {
const data = await filesDb.findOne({ _id });
const data: FileData = await filesDb.findOne({ _id });
return new File(data);
}
@ -87,7 +87,7 @@ class File {
* @param {object} query - input query
* @returns {Promise<File[]>}
*/
public static async getAll(query: object = {}): Promise<File[]> {
public static async getAll(query: FileData = {}): Promise<File[]> {
const docs = await filesDb.find(query);
return Promise.all(docs.map(doc => new File(doc)));

View file

@ -31,10 +31,10 @@ export interface PageData {
*/
class Page {
public _id?: string;
public body: any;
public title: any;
public uri: any;
public _parent: any;
public body?: any;
public title?: string;
public uri?: string;
public _parent?: string;
/**
* @class
@ -83,7 +83,7 @@ class Page {
* @param {object} query - input query
* @returns {Promise<Page[]>}
*/
public static async getAll(query: object = {}): Promise<Page[]> {
public static async getAll(query: PageData = {}): Promise<Page[]> {
const docs = await pagesDb.find(query);
return Promise.all(docs.map(doc => new Page(doc)));
@ -156,7 +156,9 @@ class Page {
* @returns {Promise<Page>}
*/
public async save(): Promise<Page> {
this.uri = await this.composeUri(this.uri);
if (this.uri !== undefined) {
this.uri = await this.composeUri(this.uri);
}
if (!this._id) {
const insertedRow = await pagesDb.insert(this.data) as { _id: string };
@ -233,6 +235,10 @@ class Page {
* @returns {string}
*/
private transformTitleToUri(): string {
if (this.title === undefined) {
return '';
}
return urlify(this.title);
}
}

View file

@ -69,7 +69,7 @@ class PageOrder {
* @param {object} query - input query
* @returns {Promise<PageOrder[]>}
*/
public static async getAll(query: object = {}): Promise<PageOrder[]> {
public static async getAll(query: Record<string, unknown> = {}): Promise<PageOrder[]> {
const docs = await db.find(query);
return Promise.all(docs.map(doc => new PageOrder(doc)));

View file

@ -31,17 +31,9 @@ class User {
* @returns {Promise<User>}
*/
public static async get(): Promise<User> {
return new Promise((resolve, reject) => {
db.findOne({})
.then( data => {
const userData: UserData = data;
const userData: UserData = await db.findOne({});
resolve(new User(userData));
})
.catch( (e) => {
reject(e);
});
});
return new User(userData);
}
}

View file

@ -99,6 +99,10 @@ router.post('/page/:id', multer.none(), async (req: Request, res: Response) => {
throw new Error('Page not found');
}
if (!page._parent) {
throw new Error('Parent not found');
}
if (page._parent !== parent) {
await PagesOrder.move(page._parent, parent, id);
} else {
@ -149,6 +153,10 @@ router.delete('/page/:id', async (req: Request, res: Response) => {
throw new Error('Page not found');
}
if (!page._parent) {
throw new Error('Parent not found');
}
const parentPageOrder = await PagesOrder.get(page._parent);
const pageBeforeId = parentPageOrder.getPageBefore(page._id);
const pageAfterId = parentPageOrder.getPageAfter(page._id);

View file

@ -26,46 +26,11 @@ router.get('/auth', csrfProtection, function (req: Request, res: Response) {
* Process given password
*/
router.post('/auth', parseForm, csrfProtection, async (req: Request, res: Response) => {
Users.get()
.then( userDoc => {
const passHash = userDoc.passHash;
try {
const userDoc = await Users.get();
const passHash = userDoc.passHash;
if (!passHash) {
res.render('auth', {
title: 'Login page',
header: 'Password not set',
csrfToken: req.csrfToken(),
});
return;
}
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(),
});
return;
}
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('/');
});
})
.catch( () => {
if (!passHash) {
res.render('auth', {
title: 'Login page',
header: 'Password not set',
@ -73,7 +38,41 @@ router.post('/auth', parseForm, csrfProtection, async (req: Request, res: Respon
});
return;
}
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(),
});
return;
}
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('/');
});
} catch (err) {
res.render('auth', {
title: 'Login page',
header: 'Password not set',
csrfToken: req.csrfToken(),
});
return;
}
});
export default router;

View file

@ -13,26 +13,26 @@ dotenv.config();
* @param res - response object
* @param next - next function
*/
export default function verifyToken(req: Request, res: Response, next: NextFunction): void {
export default async function verifyToken(req: Request, res: Response, next: NextFunction): Promise<void> {
const token = req.cookies.authToken;
Users.get()
.then( userDoc => {
if (!userDoc.passHash) {
res.locals.isAuthorized = false;
next();
try {
const userDoc = await Users.get();
return;
}
const decodedToken = jwt.verify(token, userDoc.passHash + config.get('secret'));
res.locals.isAuthorized = !!decodedToken;
next();
})
.catch( () => {
if (!userDoc.passHash) {
res.locals.isAuthorized = false;
next();
});
return;
}
const decodedToken = jwt.verify(token, userDoc.passHash + config.get('secret'));
res.locals.isAuthorized = !!decodedToken;
next();
} catch (err) {
res.locals.isAuthorized = false;
next();
}
}

View file

@ -32,6 +32,11 @@ router.get('/page/edit/:id', verifyToken, allowEdit, async (req: Request, res: R
try {
const page = await Pages.get(pageId);
const pagesAvailable = await Pages.getAllExceptChildren(pageId);
if (!page._parent) {
throw new Error('Parent not found');
}
const parentsChildrenOrdered = await PagesOrder.getOrderedChildren(pagesAvailable, pageId, page._parent, true);
res.render('pages/form', {

View file

@ -1,12 +1,16 @@
import { NextFunction, Request, Response } from 'express';
interface InputFunction {
(req: Request, res: Response, next: NextFunction): void;
}
/**
* Helper for making async middlewares for express router
*
* @param {Function} fn - input function
* @returns {function(*=, *=, *=)}
*/
export default function asyncMiddleware(fn: Function): (req: Request, res: Response, next: NextFunction) => void {
export default function asyncMiddleware(fn: InputFunction): (req: Request, res: Response, next: NextFunction) => void {
return (req: Request, res: Response, next: NextFunction) => {
Promise.resolve(fn(req, res, next))
.catch(next);

View file

@ -1,4 +1,9 @@
import Datastore from 'nedb';
import { AliasData } from '../../models/alias';
import { FileData } from '../../models/file';
import { PageData } from '../../models/page';
import { PageOrderData } from '../../models/pageOrder';
import { UserData } from '../../models/user';
import initDb from './initDb';
/**
@ -14,13 +19,21 @@ interface Options {
returnUpdatedDocs?: boolean;
}
interface ResolveFunction {
(value: any): void;
}
interface RejectFunction {
(reason?: unknown): void;
}
/**
* @class Database
* @classdesc Simple decorator class to work with nedb datastore
*
* @property {Datastore} db - nedb Datastore object
*/
export class Database {
export class Database<DocType> {
private db: Datastore;
/**
* @class
@ -39,7 +52,7 @@ export class Database {
* @param {Object} doc - object to insert
* @returns {Promise<Object|Error>} - inserted doc or Error object
*/
public async insert(doc: object): Promise<object> {
public async insert(doc: DocType): Promise<DocType> {
return new Promise((resolve, reject) => this.db.insert(doc, (err, newDoc) => {
if (err) {
reject(err);
@ -58,8 +71,8 @@ export class Database {
* @param {Object} projection - projection object
* @returns {Promise<Array<Object>|Error>} - found docs or Error object
*/
public async find(query: object, projection?: object): Promise<Array<object>> {
const cbk = (resolve: Function, reject: Function) => (err: Error | null, docs: any[]) => {
public async find(query: DocType, projection?: DocType): Promise<Array<DocType>> {
const cbk = (resolve: ResolveFunction, reject: RejectFunction) => (err: Error | null, docs: DocType[]) => {
if (err) {
reject(err);
}
@ -85,8 +98,8 @@ export class Database {
* @param {Object} projection - projection object
* @returns {Promise<Object|Error>} - found doc or Error object
*/
public async findOne(query: object, projection?: object): Promise<object> {
const cbk = (resolve: Function, reject: Function) => (err: Error | null, doc: any) => {
public async findOne(query: DocType, projection?: DocType): Promise<DocType> {
const cbk = (resolve: ResolveFunction, reject: RejectFunction) => (err: Error | null, doc: DocType) => {
if (err) {
reject(err);
}
@ -113,7 +126,7 @@ export class Database {
* @param {Options} options - optional params
* @returns {Promise<number|Object|Object[]|Error>} - number of updated rows or affected docs or Error object
*/
public async update(query: object, update: object, options: Options = {}): Promise<any> {
public async update(query: DocType, update: DocType, options: Options = {}): Promise<number|boolean> {
return new Promise((resolve, reject) => this.db.update(query, update, options, (err, result, affectedDocs) => {
if (err) {
reject(err);
@ -144,7 +157,7 @@ export class Database {
* @param {Options} options - optional params
* @returns {Promise<number|Error>} - number of removed rows or Error object
*/
public async remove(query: object, options: Options = {}): Promise<number> {
public async remove(query: DocType, options: Options = {}): Promise<number> {
return new Promise((resolve, reject) => this.db.remove(query, options, (err, result) => {
if (err) {
reject(err);
@ -156,9 +169,9 @@ export class Database {
}
export default {
pages: new Database(initDb('pages')),
password: new Database(initDb('password')),
aliases: new Database(initDb('aliases')),
pagesOrder: new Database(initDb('pagesOrder')),
files: new Database(initDb('files')),
pages: new Database<PageData>(initDb('pages')),
password: new Database<UserData>(initDb('password')),
aliases: new Database<AliasData>(initDb('aliases')),
pagesOrder: new Database<PageOrderData>(initDb('pagesOrder')),
files: new Database<FileData>(initDb('files')),
};

View file

@ -7,10 +7,10 @@
*/
/**
* @param target - target to merge into
* @param {...any} sources - sources to merge from
* @param {Record<string, any>} target - target to merge into
* @param {...any[]} sources - sources to merge from
*/
function deepMerge(target: any, ...sources: any[]): Record<string, unknown> {
function deepMerge(target: Record<string, any>, ...sources: any[]): Record<string, unknown> {
const isObject = (item: unknown): boolean => !!item && typeof item === 'object' && !Array.isArray(item);
if (!sources.length) {

View file

@ -9,8 +9,7 @@ import database from '../../src/utils/database';
const pages = database['pages'];
describe('Page model', () => {
const transformToUri = (text: string) => {
const transformToUri = (text: string): string => {
return translateString(text
.replace(/&nbsp;/g, ' ')
.replace(/[^a-zA-Z0-9А-Яа-яЁё ]/g, ' ')
@ -132,7 +131,7 @@ describe('Page model', () => {
expect(savedPage.body).to.equal(initialData.body);
expect(page._id).not.be.undefined;
const insertedPage = await pages.findOne({_id: page._id}) as Page;
const insertedPage = await pages.findOne({_id: page._id});
expect(insertedPage._id).to.equal(page._id);
expect(insertedPage.title).to.equal(page.title);
@ -158,7 +157,7 @@ describe('Page model', () => {
expect(page._id).to.equal(insertedPage._id);
const updatedPage = await pages.findOne({_id: page._id}) as Page;
const updatedPage = await pages.findOne({_id: page._id});
expect(updatedPage._id).to.equal(savedPage._id);
expect(updatedPage.title).to.equal(updateData.body.blocks[0].data.text);
@ -365,8 +364,7 @@ describe('Page model', () => {
});
it('test deletion', async () => {
const pages = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const pageIndexes = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
const orders = {
'0' : ['1', '2', '3'],
'1' : ['4', '5'],
@ -374,11 +372,14 @@ describe('Page model', () => {
'3' : ['9'],
} as { [key: string]: string[] };
function deleteRecursively(startFrom: string) {
function deleteRecursively(startFrom: string): void {
const order: string[] = orders[startFrom];
if (!order) {
const found = pages.indexOf(startFrom);
pages.splice(found, 1);
const found = pageIndexes.indexOf(startFrom);
pageIndexes.splice(found, 1);
return;
}
@ -386,8 +387,8 @@ describe('Page model', () => {
deleteRecursively(id);
});
const found = pages.indexOf(startFrom);
pages.splice(found, 1);
const found = pageIndexes.indexOf(startFrom);
pageIndexes.splice(found, 1);
}
});
});