1
0
Fork 0
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:
Nikita Melnikov 2022-03-05 22:57:23 +04:00 committed by GitHub
parent 059cfb96f9
commit 34514761f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
99 changed files with 3817 additions and 2249 deletions

View 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;

View 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;

View 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;

View 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;