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

db drives updated + fixed User model

This commit is contained in:
nvc8996 2021-09-14 15:49:36 +03:00
parent e538a5edf8
commit 2ed19f6a40
18 changed files with 133 additions and 141 deletions

View file

@ -1,17 +1,17 @@
#!/usr/bin/env node
import database from "./src/utils/database";
let db = database['password'];
import database from './src/utils/database';
import commander from 'commander';
import bcrypt from 'bcrypt';
import commander from "commander";
const db = database['password'];
const program = commander.program;
import bcrypt from "bcrypt";
const saltRounds = 12;
/**
* Script for generating password, that will be used to create and edit pages in CodeX.Docs.
* Hashes password with bcrypt and inserts it to the database.
*
* @see {https://github.com/tj/commander.js | CommanderJS}
*/
program
@ -26,7 +26,7 @@ program
const userDoc = { passHash: hash };
await db.remove({}, {multi: true});
await db.remove({}, { multi: true });
await db.insert(userDoc);
console.log('Password was successfully generated');

View file

@ -28,7 +28,7 @@ app.use('/', routes);
app.use(function (err: HttpException, req: Request, res: Response) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') == 'development' ? err : {};
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);

View file

@ -50,16 +50,15 @@ class Pages {
public static async getAllExceptChildren(parent: string): Promise<Page[]> {
const pagesAvailable = this.removeChildren(await Pages.getAll(), parent);
const nullfilteredpages: Page[] = [];
const nullFilteredPages: Page[] = [];
// eslint-disable-next-line @typescript-eslint/no-unused-vars
pagesAvailable.forEach(async (item, _index) => {
pagesAvailable.forEach(async item => {
if (item instanceof Page) {
nullfilteredpages.push(item);
nullFilteredPages.push(item);
}
});
return nullfilteredpages;
return nullFilteredPages;
}
/**
@ -105,8 +104,8 @@ class Pages {
}
return insertedPage;
} catch (validationError) {
throw new Error(validationError);
} catch (e) {
throw new Error('validationError');
}
}

View file

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

View file

@ -2,8 +2,8 @@ import database from '../utils/database/index';
const db = database['password'];
interface UserData {
passHash: string;
export interface UserData {
passHash?: string;
}
/**
@ -13,7 +13,7 @@ interface UserData {
* @property {string} passHash - hashed password
*/
class User {
public passHash: string;
public passHash?: string;
/**
* @class
@ -30,14 +30,18 @@ class User {
*
* @returns {Promise<User>}
*/
public static async get(): Promise<User|Error> {
const data = await db.findOne({});
public static async get(): Promise<User> {
return new Promise((resolve, reject) => {
db.findOne({})
.then( data => {
const userData: UserData = data;
if (data instanceof Error || data === null) {
return new Error('User not found');
}
return new User(data as UserData);
resolve(new User(userData));
})
.catch( (e) => {
reject(e);
});
});
}
}

View file

@ -26,42 +26,54 @@ router.get('/auth', csrfProtection, function (req: Request, res: Response) {
* Process given password
*/
router.post('/auth', parseForm, csrfProtection, async (req: Request, res: Response) => {
const userDoc = await Users.get();
Users.get()
.then( userDoc => {
const passHash = userDoc.passHash;
if (!userDoc || userDoc instanceof Error) {
res.render('auth', {
title: 'Login page',
header: 'Password not set',
csrfToken: req.csrfToken(),
});
if (!passHash) {
res.render('auth', {
title: 'Login page',
header: 'Password not set',
csrfToken: req.csrfToken(),
});
return;
}
return;
}
const passHash = userDoc.passHash;
bcrypt.compare(req.body.password, passHash, async (err, result) => {
if (err || result === false) {
res.render('auth', {
title: 'Login page',
header: 'Wrong password',
csrfToken: req.csrfToken(),
});
bcrypt.compare(req.body.password, passHash, async (err, result) => {
if (err || result === false) {
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( () => {
res.render('auth', {
title: 'Login page',
header: 'Wrong password',
header: 'Password not set',
csrfToken: req.csrfToken(),
});
}
const token = jwt.sign({
iss: 'Codex Team',
sub: 'auth',
iat: Date.now(),
}, passHash + config.get('secret'));
res.cookie('authToken', token, {
httpOnly: true,
expires: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year
return;
});
res.redirect('/');
});
});
export default router;

View file

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

View file

@ -6,7 +6,7 @@ import { NextFunction, Request, Response } from 'express';
* @param {Function} fn - input function
* @returns {function(*=, *=, *=)}
*/
export default function asyncMiddleware(fn: Function): (...params: any) => void {
export default function asyncMiddleware(fn: Function): (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,10 +0,0 @@
import Datastore from 'nedb';
import config from 'config';
import path from 'path';
const db = new Datastore({
filename: path.resolve(`./${config.get('database')}/aliases.db`),
autoload: true,
});
export default db;

View file

@ -1,10 +0,0 @@
import Datastore from 'nedb';
import config from 'config';
import path from 'path';
const db = new Datastore({
filename: path.resolve(`./${config.get('database')}/files.db`),
autoload: true,
});
export default db;

View file

@ -1,9 +1,10 @@
import pages from './pages';
import files from './files';
import password from './password';
import aliases from './aliases';
import pagesOrder from './pagesOrder';
// import pages from './pages';
// import files from './files';
// import password from './password';
// import aliases from './aliases';
// import pagesOrder from './pagesOrder';
import Datastore from 'nedb';
import initDb from './initDb';
/**
* @typedef Options - optional params
@ -18,6 +19,7 @@ interface Options {
returnUpdatedDocs?: boolean;
}
/**
* @class Database
* @classdesc Simple decorator class to work with nedb datastore
@ -43,7 +45,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 | Error> {
public async insert(doc: object): Promise<object> {
return new Promise((resolve, reject) => this.db.insert(doc, (err, newDoc) => {
if (err) {
reject(err);
@ -62,7 +64,7 @@ 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> | Error> {
public async find(query: object, projection?: object): Promise<Array<object>> {
const cbk = (resolve: Function, reject: Function) => (err: Error | null, docs: any[]) => {
if (err) {
reject(err);
@ -89,7 +91,7 @@ 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 | Error> {
public async findOne(query: object, projection?: object): Promise<object> {
const cbk = (resolve: Function, reject: Function) => (err: Error | null, doc: any) => {
if (err) {
reject(err);
@ -148,7 +150,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|Error> {
public async remove(query: object, options: Options = {}): Promise<number> {
return new Promise((resolve, reject) => this.db.remove(query, options, (err, result) => {
if (err) {
reject(err);
@ -160,9 +162,9 @@ export class Database {
}
export default {
pages: new Database(pages),
password: new Database(password),
aliases: new Database(aliases),
pagesOrder: new Database(pagesOrder),
files: new Database(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')),
};

View file

@ -0,0 +1,15 @@
import Datastore from 'nedb';
import config from 'config';
import path from 'path';
/**
*
* @param {string} name - name of the data file
* @returns {Datastore} db - nedb instance
*/
export default function initDb(name: string): Datastore {
return new Datastore({
filename: path.resolve(`./${config.get('database')}/${name}.db`),
autoload: true,
});
}

View file

@ -1,10 +0,0 @@
import Datastore from 'nedb';
import config from 'config';
import path from 'path';
const db = new Datastore({
filename: path.resolve(`./${config.get('database')}/pages.db`),
autoload: true,
});
export default db;

View file

@ -1,10 +0,0 @@
import Datastore from 'nedb';
import config from 'config';
import path from 'path';
const db = new Datastore({
filename: path.resolve(`./${config.get('database')}/pagesOrder.db`),
autoload: true,
});
export default db;

View file

@ -1,10 +0,0 @@
import Datastore from 'nedb';
import config from 'config';
import path from 'path';
const db = new Datastore({
filename: path.resolve(`./${config.get('database')}/password.db`),
autoload: true,
});
export default db;

View file

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

View file

@ -78,7 +78,7 @@ export default class RCParser {
rConfig.menu = RCParser.DEFAULTS.menu;
}
rConfig.menu = rConfig.menu.filter((option: any, i:number) => {
rConfig.menu = rConfig.menu.filter((option: string | Menu, i:number) => {
i = i + 1;
if (typeof option === 'string') {
return true;
@ -107,7 +107,7 @@ export default class RCParser {
return true;
});
rConfig.menu = rConfig.menu.map((option: any) => {
rConfig.menu = rConfig.menu.map((option: string | Menu) => {
if (typeof option === 'string') {
return {
title: option,

View file

@ -17,8 +17,8 @@ chai.use(chaiHTTP);
describe('Pages REST: ', () => {
let agent: ChaiHttp.Agent;
const transformToUri = (text: string) => {
return translateString(text
const transformToUri = (text: string):string => {
return translateString(text
.replace(/&nbsp;/g, ' ')
.replace(/[^a-zA-Z0-9А-Яа-яЁё ]/g, ' ')
.replace(/ +/g, ' ')
@ -103,7 +103,8 @@ describe('Pages REST: ', () => {
const {success, error} = res.body;
expect(success).to.be.false;
expect(error).to.equal('Error: Some of required fields is missed');
// expect(error).to.equal('Error: Some of required fields is missed');
expect(error).to.equal('validationError');
});
it('Finding page', async () => {