emilepharand/Babilonia

View on GitHub
server/routes.ts

Summary

Maintainability
A
1 hr
Test Coverage
import AsyncLock from 'async-lock';
import type {
    Express, NextFunction, Request, Response,
} from 'express';
import {Router} from 'express';
import * as Controller from './controller';

const lock = new AsyncLock();
const lockKey = 'lock';

type Method = 'get' | 'post' | 'put' | 'delete';

export default class Routes {
    public router: Router;

    private readonly app: Express;

    constructor(app: Express) {
        // eslint-disable-next-line new-cap
        this.router = Router();
        this.app = app;
    }

    public init(): void {
        this.app.use('/', this.router);
        if (process.env.TEST_MODE) {
            // Signals that the server is running in tests
            this.router.get('/', (_, res) => {
                res.status(200).end();
            });
        }
        // Languages
        this.route('get', '/languages', Controller.getLanguages);
        this.route('post', '/languages', Controller.addLanguage);
        this.route('put', '/languages', Controller.editLanguages);
        this.route('delete', '/languages', Controller.getLanguages);
        this.route('get', '/languages/:id', Controller.getLanguageById);
        this.route('delete', '/languages/:id', Controller.deleteLanguage);
        // Ideas
        this.route('get', '/ideas/:id', Controller.getIdeaById);
        this.route('post', '/ideas', Controller.addIdea);
        this.route('put', '/ideas/:id', Controller.editIdea);
        this.route('delete', '/ideas/:id', Controller.deleteIdea);
        // Expressions
        this.route('get', '/expressions', Controller.getExpressions);
        // Practice
        this.route('delete', '/everything', Controller.deleteAllData);
        this.route('get', '/practice-ideas/next', Controller.getNextPracticeIdea);
        // Stats
        this.route('get', '/stats', Controller.getStats);
        // Settings
        this.route('put', '/settings', Controller.setSettings);
        this.route('get', '/settings', Controller.getSettings);
        // Everything
        this.route('delete', '/everything', Controller.deleteAllData);
        // Database
        this.route('put', '/database/path', Controller.changeDatabase);
        this.route('get', '/database/path', Controller.getDatabasePath);
        this.route('put', '/database/migrate', Controller.migrateDatabase);
    }

    private wrapAsync(fn: (req: Request, res: Response, next: NextFunction) => Promise<void>): (req: Request, res: Response, next: NextFunction) => void {
        return async (req, res, next) => {
            await lock.acquire(lockKey, async () => {
                await fn(req, res, next).catch(next);
            });
        };
    }

    private route(method: Method, route: string, handler: (req: Request, res: Response, next: NextFunction) => Promise<void>): void {
        this.router[method](route, this.wrapAsync(handler));
    }
}