mirror of
https://github.com/codex-team/codex.docs.git
synced 2025-08-03 20:45:24 +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
50
src/backend/routes/aliases.ts
Normal file
50
src/backend/routes/aliases.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
import express, { Request, Response } from 'express';
|
||||
import Aliases from '../controllers/aliases';
|
||||
import Pages from '../controllers/pages';
|
||||
import Alias from '../models/alias';
|
||||
import verifyToken from './middlewares/token';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
/**
|
||||
* GET /*
|
||||
*
|
||||
* Return document with given alias
|
||||
*/
|
||||
router.get('*', verifyToken, async (req: Request, res: Response) => {
|
||||
try {
|
||||
let url = req.originalUrl.slice(1); // Cuts first '/' character
|
||||
const queryParamsIndex = url.indexOf('?');
|
||||
|
||||
if (queryParamsIndex !== -1) {
|
||||
url = url.slice(0, queryParamsIndex); // Cuts off query params
|
||||
}
|
||||
|
||||
const alias = await Aliases.get(url);
|
||||
|
||||
if (alias.id === undefined) {
|
||||
throw new Error('Alias not found');
|
||||
}
|
||||
|
||||
switch (alias.type) {
|
||||
case Alias.types.PAGE: {
|
||||
const page = await Pages.get(alias.id);
|
||||
|
||||
const pageParent = await page.getParent();
|
||||
|
||||
res.render('pages/page', {
|
||||
page,
|
||||
pageParent,
|
||||
config: req.app.locals.config,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: err,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
12
src/backend/routes/api/index.ts
Normal file
12
src/backend/routes/api/index.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import express from 'express';
|
||||
import pagesAPI from './pages';
|
||||
import transportAPI from './transport';
|
||||
import linksAPI from './links';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.use('/', pagesAPI);
|
||||
router.use('/', transportAPI);
|
||||
router.use('/', linksAPI);
|
||||
|
||||
export default router;
|
62
src/backend/routes/api/links.ts
Normal file
62
src/backend/routes/api/links.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
import express, { Request, Response } from 'express';
|
||||
import ogs from 'open-graph-scraper';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
interface ResponseData {
|
||||
success: number;
|
||||
meta?: {
|
||||
title: string | undefined;
|
||||
description: string | undefined;
|
||||
siteName: string | undefined;
|
||||
image: { url: string | undefined }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accept file url to fetch
|
||||
*/
|
||||
router.get('/fetchUrl', async (req: Request, res: Response) => {
|
||||
const response: ResponseData = {
|
||||
success: 0,
|
||||
};
|
||||
|
||||
if (!req.query.url) {
|
||||
res.status(400).json(response);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof req.query.url !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const linkData = (await ogs({ url: req.query.url })).result;
|
||||
|
||||
if (!linkData.success) {
|
||||
return;
|
||||
}
|
||||
|
||||
response.success = 1;
|
||||
response.meta = {
|
||||
title: linkData.ogTitle,
|
||||
description: linkData.ogDescription,
|
||||
siteName: linkData.ogSiteName,
|
||||
image: {
|
||||
url: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
if (linkData.ogImage !== undefined) {
|
||||
response.meta.image = { url: linkData.ogImage.toString() };
|
||||
}
|
||||
|
||||
res.status(200).json(response);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
res.status(500).json(response);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
221
src/backend/routes/api/pages.ts
Normal file
221
src/backend/routes/api/pages.ts
Normal file
|
@ -0,0 +1,221 @@
|
|||
import express, { Request, Response } from 'express';
|
||||
import multerFunc from 'multer';
|
||||
import Pages from '../../controllers/pages';
|
||||
import PagesOrder from '../../controllers/pagesOrder';
|
||||
|
||||
const router = express.Router();
|
||||
const multer = multerFunc();
|
||||
|
||||
/**
|
||||
* GET /page/:id
|
||||
*
|
||||
* Return PageData of page with given id
|
||||
*/
|
||||
|
||||
router.get('/page/:id', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const page = await Pages.get(req.params.id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
result: page.data,
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: (err as Error).message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /pages
|
||||
*
|
||||
* Return PageData for all pages
|
||||
*/
|
||||
router.get('/pages', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const pages = await Pages.getAll();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
result: pages,
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: (err as Error).message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* PUT /page
|
||||
*
|
||||
* Create new page in the database
|
||||
*/
|
||||
router.put('/page', multer.none(), async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { title, body, parent } = req.body;
|
||||
const page = await Pages.insert({
|
||||
title,
|
||||
body,
|
||||
parent,
|
||||
});
|
||||
|
||||
if (page._id === undefined) {
|
||||
throw new Error('Page not found');
|
||||
}
|
||||
|
||||
/** push to the orders array */
|
||||
await PagesOrder.push(parent, page._id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
result: page,
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: (err as Error).message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /page/:id
|
||||
*
|
||||
* Update page data in the database
|
||||
*/
|
||||
router.post('/page/:id', multer.none(), async (req: Request, res: Response) => {
|
||||
const { id } = req.params;
|
||||
|
||||
try {
|
||||
const { title, body, parent, putAbovePageId, uri } = req.body;
|
||||
const pages = await Pages.getAll();
|
||||
let page = await Pages.get(id);
|
||||
|
||||
if (page._id === undefined) {
|
||||
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 {
|
||||
if (putAbovePageId && putAbovePageId !== '0') {
|
||||
const unordered = pages.filter(_page => _page._parent === page._parent).map(_page => _page._id);
|
||||
|
||||
const unOrdered: string[] = [];
|
||||
|
||||
unordered.forEach(item => {
|
||||
if (typeof item === 'string') {
|
||||
unOrdered.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
await PagesOrder.update(unOrdered, page._id, page._parent, putAbovePageId);
|
||||
}
|
||||
}
|
||||
|
||||
page = await Pages.update(id, {
|
||||
title,
|
||||
body,
|
||||
parent,
|
||||
uri,
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
result: page,
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: (err as Error).message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* DELETE /page/:id
|
||||
*
|
||||
* Remove page from the database
|
||||
*/
|
||||
router.delete('/page/:id', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const pageId = req.params.id;
|
||||
const page = await Pages.get(pageId);
|
||||
|
||||
if (page._id === undefined) {
|
||||
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);
|
||||
|
||||
let pageToRedirect;
|
||||
|
||||
if (pageBeforeId) {
|
||||
pageToRedirect = await Pages.get(pageBeforeId);
|
||||
} else if (pageAfterId) {
|
||||
pageToRedirect = await Pages.get(pageAfterId);
|
||||
} else {
|
||||
pageToRedirect = page._parent !== '0' ? await Pages.get(page._parent) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove current page and go deeper to remove children with orders
|
||||
*
|
||||
* @param {string} startFrom - start point to delete
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const deleteRecursively = async (startFrom: string): Promise<void> => {
|
||||
let order: string[] = [];
|
||||
|
||||
try {
|
||||
const children = await PagesOrder.get(startFrom);
|
||||
|
||||
order = children.order;
|
||||
} catch (e) {
|
||||
order = [];
|
||||
}
|
||||
|
||||
order.forEach(async id => {
|
||||
await deleteRecursively(id);
|
||||
});
|
||||
|
||||
await Pages.remove(startFrom);
|
||||
try {
|
||||
await PagesOrder.remove(startFrom);
|
||||
} catch (e) {
|
||||
order = [];
|
||||
}
|
||||
};
|
||||
|
||||
await deleteRecursively(req.params.id);
|
||||
|
||||
// remove also from parent's order
|
||||
parentPageOrder.remove(req.params.id);
|
||||
await parentPageOrder.save();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
result: pageToRedirect,
|
||||
});
|
||||
} catch (err) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: (err as Error).message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
146
src/backend/routes/api/transport.ts
Normal file
146
src/backend/routes/api/transport.ts
Normal file
|
@ -0,0 +1,146 @@
|
|||
import { Request, Response, Router } from 'express';
|
||||
import multer, { StorageEngine } from 'multer';
|
||||
import mime from 'mime';
|
||||
import mkdirp from 'mkdirp';
|
||||
import config from 'config';
|
||||
import Transport from '../../controllers/transport';
|
||||
import { random16 } from '../../utils/crypto';
|
||||
|
||||
const router = Router();
|
||||
|
||||
/**
|
||||
* Multer storage for uploaded files and images
|
||||
*
|
||||
* @type {StorageEngine}
|
||||
*/
|
||||
const storage: StorageEngine = multer.diskStorage({
|
||||
destination: (req, file, cb) => {
|
||||
const dir: string = config.get('uploads') || 'public/uploads';
|
||||
|
||||
mkdirp(dir);
|
||||
cb(null, dir);
|
||||
},
|
||||
filename: async (req, file, cb) => {
|
||||
const filename = await random16();
|
||||
|
||||
cb(null, `${filename}.${mime.getExtension(file.mimetype)}`);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Multer middleware for image uploading
|
||||
*/
|
||||
const imageUploader = multer({
|
||||
storage: storage,
|
||||
fileFilter: (req, file, cb) => {
|
||||
if (!/image/.test(file.mimetype) && !/video\/mp4/.test(file.mimetype)) {
|
||||
cb(null, false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
cb(null, true);
|
||||
},
|
||||
}).fields([ {
|
||||
name: 'image',
|
||||
maxCount: 1,
|
||||
} ]);
|
||||
|
||||
/**
|
||||
* Multer middleware for file uploading
|
||||
*/
|
||||
const fileUploader = multer({
|
||||
storage: storage,
|
||||
}).fields([ {
|
||||
name: 'file',
|
||||
maxCount: 1,
|
||||
} ]);
|
||||
|
||||
/**
|
||||
* Accepts images to upload
|
||||
*/
|
||||
router.post('/transport/image', imageUploader, async (req: Request, res: Response) => {
|
||||
const response = {
|
||||
success: 0,
|
||||
message: '',
|
||||
};
|
||||
|
||||
if (req.files === undefined) {
|
||||
response.message = 'No files found';
|
||||
res.status(400).json(response);
|
||||
|
||||
return;
|
||||
}
|
||||
if (!('image' in req.files)) {
|
||||
res.status(400).json(response);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Object.assign(
|
||||
response,
|
||||
await Transport.save(req.files.image[0], req.body.map ? JSON.parse(req.body.map) : undefined)
|
||||
);
|
||||
|
||||
response.success = 1;
|
||||
res.status(200).json(response);
|
||||
} catch (e) {
|
||||
res.status(500).json(response);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Accepts files to upload
|
||||
*/
|
||||
router.post('/transport/file', fileUploader, async (req: Request, res: Response) => {
|
||||
const response = { success: 0 };
|
||||
|
||||
if (req.files === undefined) {
|
||||
res.status(400).json(response);
|
||||
|
||||
return;
|
||||
}
|
||||
if (!('file' in req.files)) {
|
||||
res.status(400).json(response);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Object.assign(
|
||||
response,
|
||||
await Transport.save(req.files.file[0], req.body.map ? JSON.parse(req.body.map) : undefined)
|
||||
);
|
||||
|
||||
response.success = 1;
|
||||
res.status(200).json(response);
|
||||
} catch (e) {
|
||||
res.status(500).json(response);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Accept file url to fetch
|
||||
*/
|
||||
router.post('/transport/fetch', multer().none(), async (req: Request, res: Response) => {
|
||||
const response = { success: 0 };
|
||||
|
||||
if (!req.body.url) {
|
||||
res.status(400).json(response);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Object.assign(response, await Transport.fetch(req.body.url, req.body.map ? JSON.parse(req.body.map) : undefined));
|
||||
|
||||
response.success = 1;
|
||||
res.status(200).json(response);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
res.status(500).json(response);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
78
src/backend/routes/auth.ts
Normal file
78
src/backend/routes/auth.ts
Normal file
|
@ -0,0 +1,78 @@
|
|||
import express, { Request, Response } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import config from 'config';
|
||||
import bcrypt from 'bcrypt';
|
||||
import csrf from 'csurf';
|
||||
import * as dotenv from 'dotenv';
|
||||
import Users from '../controllers/users';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const router = express.Router();
|
||||
const csrfProtection = csrf({ cookie: true });
|
||||
const parseForm = express.urlencoded({ extended: false });
|
||||
|
||||
/**
|
||||
* Authorization page
|
||||
*/
|
||||
router.get('/auth', csrfProtection, function (req: Request, res: Response) {
|
||||
res.render('auth', {
|
||||
title: 'Login page',
|
||||
csrfToken: req.csrfToken(),
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Process given password
|
||||
*/
|
||||
router.post('/auth', parseForm, csrfProtection, async (req: Request, res: Response) => {
|
||||
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 (err) {
|
||||
res.render('auth', {
|
||||
title: 'Login page',
|
||||
header: 'Password not set',
|
||||
csrfToken: req.csrfToken(),
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
16
src/backend/routes/home.ts
Normal file
16
src/backend/routes/home.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import express, { Request, Response } from 'express';
|
||||
import verifyToken from './middlewares/token';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
/* GET home page. */
|
||||
router.get('/', verifyToken, async (req: Request, res: Response) => {
|
||||
const config = req.app.locals.config;
|
||||
|
||||
if (config.startPage) {
|
||||
return res.redirect(config.startPage);
|
||||
}
|
||||
res.render('pages/index', { isAuthorized: res.locals.isAuthorized });
|
||||
});
|
||||
|
||||
export default router;
|
17
src/backend/routes/index.ts
Normal file
17
src/backend/routes/index.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import express from 'express';
|
||||
import home from './home';
|
||||
import pages from './pages';
|
||||
import auth from './auth';
|
||||
import aliases from './aliases';
|
||||
import api from './api';
|
||||
import pagesMiddleware from './middlewares/pages';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
router.use('/', pagesMiddleware, home);
|
||||
router.use('/', pagesMiddleware, pages);
|
||||
router.use('/', pagesMiddleware, auth);
|
||||
router.use('/api', api);
|
||||
router.use('/', aliases);
|
||||
|
||||
export default router;
|
16
src/backend/routes/middlewares/locals.ts
Normal file
16
src/backend/routes/middlewares/locals.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { NextFunction, Request, Response } from 'express';
|
||||
|
||||
/**
|
||||
* Middleware for checking locals.isAuthorized property, which allows to edit/create pages
|
||||
*
|
||||
* @param req - request object
|
||||
* @param res - response object
|
||||
* @param next - next function
|
||||
*/
|
||||
export default function allowEdit(req: Request, res: Response, next: NextFunction): void {
|
||||
if (res.locals.isAuthorized) {
|
||||
next();
|
||||
} else {
|
||||
res.redirect('/auth');
|
||||
}
|
||||
}
|
79
src/backend/routes/middlewares/pages.ts
Normal file
79
src/backend/routes/middlewares/pages.ts
Normal file
|
@ -0,0 +1,79 @@
|
|||
import { NextFunction, Request, Response } from 'express';
|
||||
import Pages from '../../controllers/pages';
|
||||
import PagesOrder from '../../controllers/pagesOrder';
|
||||
import Page from '../../models/page';
|
||||
import asyncMiddleware from '../../utils/asyncMiddleware';
|
||||
import PageOrder from '../../models/pageOrder';
|
||||
|
||||
/**
|
||||
* Process one-level pages list to parent-children list
|
||||
*
|
||||
* @param {string} parentPageId - parent page id
|
||||
* @param {Page[]} pages - list of all available pages
|
||||
* @param {PagesOrder[]} pagesOrder - list of pages order
|
||||
* @param {number} level - max level recursion
|
||||
* @param {number} currentLevel - current level of element
|
||||
*
|
||||
* @returns {Page[]}
|
||||
*/
|
||||
function createMenuTree(parentPageId: string, pages: Page[], pagesOrder: PageOrder[], level = 1, currentLevel = 1): Page[] {
|
||||
const childrenOrder = pagesOrder.find(order => order.data.page === parentPageId);
|
||||
|
||||
/**
|
||||
* branch is a page children in tree
|
||||
* if we got some children order on parents tree, then we push found pages in order sequence
|
||||
* otherwise just find all pages includes parent tree
|
||||
*/
|
||||
let ordered: any[] = [];
|
||||
|
||||
if (childrenOrder) {
|
||||
ordered = childrenOrder.order.map((pageId: string) => {
|
||||
return pages.find(page => page._id === pageId);
|
||||
});
|
||||
}
|
||||
|
||||
const unordered = pages.filter(page => page._parent === parentPageId);
|
||||
const branch = Array.from(new Set([...ordered, ...unordered]));
|
||||
|
||||
/**
|
||||
* stop recursion when we got the passed max level
|
||||
*/
|
||||
if (currentLevel === level + 1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Each parents children can have subbranches
|
||||
*/
|
||||
return branch.filter(page => page && page._id).map(page => {
|
||||
return Object.assign({
|
||||
children: createMenuTree(page._id, pages, pagesOrder, level, currentLevel + 1),
|
||||
}, page.data);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware for all /page/... routes
|
||||
*
|
||||
* @param {Request} req
|
||||
* @param {Response} res
|
||||
* @param {NextFunction} next
|
||||
*/
|
||||
export default asyncMiddleware(async (req: Request, res: Response, next: NextFunction) => {
|
||||
/**
|
||||
* Pages without parent
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
const parentIdOfRootPages = '0';
|
||||
|
||||
try {
|
||||
const pages = await Pages.getAll();
|
||||
const pagesOrder = await PagesOrder.getAll();
|
||||
|
||||
res.locals.menu = createMenuTree(parentIdOfRootPages, pages, pagesOrder, 2);
|
||||
} catch (error) {
|
||||
console.log('Can not load menu:', error);
|
||||
}
|
||||
next();
|
||||
});
|
38
src/backend/routes/middlewares/token.ts
Normal file
38
src/backend/routes/middlewares/token.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import * as dotenv from 'dotenv';
|
||||
import config from 'config';
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import Users from '../../controllers/users';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
/**
|
||||
* Middleware for checking jwt token
|
||||
*
|
||||
* @param req - request object
|
||||
* @param res - response object
|
||||
* @param next - next function
|
||||
*/
|
||||
export default async function verifyToken(req: Request, res: Response, next: NextFunction): Promise<void> {
|
||||
const token = req.cookies.authToken;
|
||||
|
||||
try {
|
||||
const userDoc = await Users.get();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
75
src/backend/routes/pages.ts
Normal file
75
src/backend/routes/pages.ts
Normal file
|
@ -0,0 +1,75 @@
|
|||
import express, { NextFunction, Request, Response } from 'express';
|
||||
import Pages from '../controllers/pages';
|
||||
import PagesOrder from '../controllers/pagesOrder';
|
||||
import verifyToken from './middlewares/token';
|
||||
import allowEdit from './middlewares/locals';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
/**
|
||||
* Create new page form
|
||||
*/
|
||||
router.get('/page/new', verifyToken, allowEdit, async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const pagesAvailable = await Pages.getAll();
|
||||
|
||||
res.render('pages/form', {
|
||||
pagesAvailable,
|
||||
page: null,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(404);
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Edit page form
|
||||
*/
|
||||
router.get('/page/edit/:id', verifyToken, allowEdit, async (req: Request, res: Response, next: NextFunction) => {
|
||||
const pageId = req.params.id;
|
||||
|
||||
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', {
|
||||
page,
|
||||
parentsChildrenOrdered,
|
||||
pagesAvailable,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(404);
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* View page
|
||||
*/
|
||||
router.get('/page/:id', verifyToken, async (req: Request, res: Response, next: NextFunction) => {
|
||||
const pageId = req.params.id;
|
||||
|
||||
try {
|
||||
const page = await Pages.get(pageId);
|
||||
|
||||
const pageParent = await page.parent;
|
||||
|
||||
res.render('pages/page', {
|
||||
page,
|
||||
pageParent,
|
||||
config: req.app.locals.config,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(404);
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
Loading…
Add table
Add a link
Reference in a new issue