From 6e5af27f8fde3f201076889bae0381d570dbcdfd Mon Sep 17 00:00:00 2001 From: nvc8996 Date: Thu, 16 Sep 2021 17:13:40 +0300 Subject: [PATCH] explicit type fixing --- src/controllers/pages.ts | 27 +++++++----- src/controllers/pagesOrder.ts | 17 ++++---- src/controllers/transport.ts | 13 +++++- src/controllers/users.ts | 19 +++----- src/exceptions/httpException.ts | 1 + src/models/alias.ts | 2 +- src/models/file.ts | 8 ++-- src/models/page.ts | 18 +++++--- src/models/pageOrder.ts | 2 +- src/models/user.ts | 12 +---- src/routes/api/pages.ts | 8 ++++ src/routes/auth.ts | 77 ++++++++++++++++----------------- src/routes/middlewares/token.ts | 34 +++++++-------- src/routes/pages.ts | 5 +++ src/utils/asyncMiddleware.ts | 6 ++- src/utils/database/index.ts | 39 +++++++++++------ src/utils/objects.ts | 6 +-- test/models/page.ts | 23 +++++----- 18 files changed, 177 insertions(+), 140 deletions(-) diff --git a/src/controllers/pages.ts b/src/controllers/pages.ts index 35358bd..6a4bd07 100644 --- a/src/controllers/pages.ts +++ b/src/controllers/pages.ts @@ -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 { + public static get REQUIRED_FIELDS(): Array { return [ 'body' ]; } @@ -23,7 +24,7 @@ class Pages { * @returns {Promise} */ public static async get(id: string): Promise { - 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} */ public static async getAll(): Promise { - 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} */ public static async update(id: string, data: PageData): Promise { - 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} */ public static async remove(id: string): Promise { - 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(); } diff --git a/src/controllers/pagesOrder.ts b/src/controllers/pagesOrder.ts index a08a455..f084af3 100644 --- a/src/controllers/pagesOrder.ts +++ b/src/controllers/pagesOrder.ts @@ -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} */ public static async get(parentId: string): Promise { - 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} */ public static async getAll(): Promise { - 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 { - 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 { - 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 { - 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 { - 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} */ public static async remove(parentId: string): Promise { - 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'); diff --git a/src/controllers/transport.ts b/src/controllers/transport.ts index 0b659fa..b5b64fa 100644 --- a/src/controllers/transport.ts +++ b/src/controllers/transport.ts @@ -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(); diff --git a/src/controllers/users.ts b/src/controllers/users.ts index e6dc400..549cad5 100644 --- a/src/controllers/users.ts +++ b/src/controllers/users.ts @@ -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} + * @returns {Promise} */ - public static get(): Promise { - return new Promise((resolve, reject) => { - Model.get() - .then( userDoc => { - resolve(userDoc); - }) - .catch( (e) => { - reject(e); - }); - }); + public static async get(): Promise { + const userData: User = await User.get(); + + return userData; } } diff --git a/src/exceptions/httpException.ts b/src/exceptions/httpException.ts index 46ff115..f13e7a2 100644 --- a/src/exceptions/httpException.ts +++ b/src/exceptions/httpException.ts @@ -1,5 +1,6 @@ /** * HttpException class for middleware + * * @property {number} status - exception status code * @property {string} message - detail about the exception */ diff --git a/src/models/alias.ts b/src/models/alias.ts index 3a36232..58d8142 100644 --- a/src/models/alias.ts +++ b/src/models/alias.ts @@ -13,7 +13,7 @@ const aliasesDb = database['aliases']; * @property {string} id - entity id * */ -interface AliasData { +export interface AliasData { _id?: string; hash?: string; type?: string; diff --git a/src/models/file.ts b/src/models/file.ts index 50f4622..b604906 100644 --- a/src/models/file.ts +++ b/src/models/file.ts @@ -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} */ public static async get(_id: string): Promise { - 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} */ - public static async getAll(query: object = {}): Promise { + public static async getAll(query: FileData = {}): Promise { const docs = await filesDb.find(query); return Promise.all(docs.map(doc => new File(doc))); diff --git a/src/models/page.ts b/src/models/page.ts index 4cfe193..8b77c1d 100644 --- a/src/models/page.ts +++ b/src/models/page.ts @@ -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} */ - public static async getAll(query: object = {}): Promise { + public static async getAll(query: PageData = {}): Promise { const docs = await pagesDb.find(query); return Promise.all(docs.map(doc => new Page(doc))); @@ -156,7 +156,9 @@ class Page { * @returns {Promise} */ public async save(): Promise { - 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); } } diff --git a/src/models/pageOrder.ts b/src/models/pageOrder.ts index ceacce5..c137358 100644 --- a/src/models/pageOrder.ts +++ b/src/models/pageOrder.ts @@ -69,7 +69,7 @@ class PageOrder { * @param {object} query - input query * @returns {Promise} */ - public static async getAll(query: object = {}): Promise { + public static async getAll(query: Record = {}): Promise { const docs = await db.find(query); return Promise.all(docs.map(doc => new PageOrder(doc))); diff --git a/src/models/user.ts b/src/models/user.ts index 75d0609..0d382bb 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -31,17 +31,9 @@ class User { * @returns {Promise} */ public static async get(): Promise { - 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); } } diff --git a/src/routes/api/pages.ts b/src/routes/api/pages.ts index e6b3995..ba6b5fe 100644 --- a/src/routes/api/pages.ts +++ b/src/routes/api/pages.ts @@ -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); diff --git a/src/routes/auth.ts b/src/routes/auth.ts index 89c90b9..0f0065e 100644 --- a/src/routes/auth.ts +++ b/src/routes/auth.ts @@ -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; diff --git a/src/routes/middlewares/token.ts b/src/routes/middlewares/token.ts index f49af26..2289be1 100644 --- a/src/routes/middlewares/token.ts +++ b/src/routes/middlewares/token.ts @@ -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 { 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(); + } } diff --git a/src/routes/pages.ts b/src/routes/pages.ts index b9ba5c7..82c8107 100644 --- a/src/routes/pages.ts +++ b/src/routes/pages.ts @@ -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', { diff --git a/src/utils/asyncMiddleware.ts b/src/utils/asyncMiddleware.ts index 682e4f3..65fdda4 100644 --- a/src/utils/asyncMiddleware.ts +++ b/src/utils/asyncMiddleware.ts @@ -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); diff --git a/src/utils/database/index.ts b/src/utils/database/index.ts index 414415d..518244e 100644 --- a/src/utils/database/index.ts +++ b/src/utils/database/index.ts @@ -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 { private db: Datastore; /** * @class @@ -39,7 +52,7 @@ export class Database { * @param {Object} doc - object to insert * @returns {Promise} - inserted doc or Error object */ - public async insert(doc: object): Promise { + public async insert(doc: DocType): Promise { 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|Error>} - found docs or Error object */ - public async find(query: object, projection?: object): Promise> { - const cbk = (resolve: Function, reject: Function) => (err: Error | null, docs: any[]) => { + public async find(query: DocType, projection?: DocType): Promise> { + 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} - found doc or Error object */ - public async findOne(query: object, projection?: object): Promise { - const cbk = (resolve: Function, reject: Function) => (err: Error | null, doc: any) => { + public async findOne(query: DocType, projection?: DocType): Promise { + 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 of updated rows or affected docs or Error object */ - public async update(query: object, update: object, options: Options = {}): Promise { + public async update(query: DocType, update: DocType, options: Options = {}): Promise { 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 of removed rows or Error object */ - public async remove(query: object, options: Options = {}): Promise { + public async remove(query: DocType, options: Options = {}): Promise { 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(initDb('pages')), + password: new Database(initDb('password')), + aliases: new Database(initDb('aliases')), + pagesOrder: new Database(initDb('pagesOrder')), + files: new Database(initDb('files')), }; diff --git a/src/utils/objects.ts b/src/utils/objects.ts index 28a86cb..7eb5d06 100644 --- a/src/utils/objects.ts +++ b/src/utils/objects.ts @@ -7,10 +7,10 @@ */ /** - * @param target - target to merge into - * @param {...any} sources - sources to merge from + * @param {Record} target - target to merge into + * @param {...any[]} sources - sources to merge from */ -function deepMerge(target: any, ...sources: any[]): Record { +function deepMerge(target: Record, ...sources: any[]): Record { const isObject = (item: unknown): boolean => !!item && typeof item === 'object' && !Array.isArray(item); if (!sources.length) { diff --git a/test/models/page.ts b/test/models/page.ts index 71cf48d..6d3772f 100644 --- a/test/models/page.ts +++ b/test/models/page.ts @@ -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(/ /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); } }); });