Improve middleware definition and cleanup code
This commit is contained in:
parent
0d6f7c0d90
commit
b736f5f6cb
@ -16,14 +16,15 @@ import SecurityError from "./SecurityError";
|
||||
import * as path from "path";
|
||||
import CacheProvider from "./CacheProvider";
|
||||
import RedisComponent from "./components/RedisComponent";
|
||||
import Extendable from "./Extendable";
|
||||
import TemplateError = lib.TemplateError;
|
||||
|
||||
export default abstract class Application {
|
||||
export default abstract class Application implements Extendable<ApplicationComponent> {
|
||||
private readonly version: string;
|
||||
private readonly ignoreCommandLine: boolean;
|
||||
private readonly controllers: Controller[] = [];
|
||||
private readonly webSocketListeners: { [p: string]: WebSocketListener<any> } = {};
|
||||
private readonly components: ApplicationComponent<any>[] = [];
|
||||
private readonly webSocketListeners: { [p: string]: WebSocketListener<Application> } = {};
|
||||
private readonly components: ApplicationComponent[] = [];
|
||||
private cacheProvider?: CacheProvider;
|
||||
|
||||
private ready: boolean = false;
|
||||
@ -37,8 +38,9 @@ export default abstract class Application {
|
||||
|
||||
protected abstract async init(): Promise<void>;
|
||||
|
||||
protected use(thing: Controller | WebSocketListener<this> | ApplicationComponent<any>) {
|
||||
protected use(thing: Controller | WebSocketListener<this> | ApplicationComponent) {
|
||||
if (thing instanceof Controller) {
|
||||
thing.setApp(this);
|
||||
this.controllers.push(thing);
|
||||
} else if (thing instanceof WebSocketListener) {
|
||||
const path = thing.path();
|
||||
@ -151,15 +153,24 @@ export default abstract class Application {
|
||||
|
||||
// Start components
|
||||
for (const component of this.components) {
|
||||
await component.start(app);
|
||||
await component.start?.(app);
|
||||
}
|
||||
|
||||
// Components routes
|
||||
for (const component of this.components) {
|
||||
if (component.init) {
|
||||
component.setCurrentRouter(initRouter);
|
||||
await component.init(initRouter);
|
||||
}
|
||||
|
||||
if (component.handle) {
|
||||
component.setCurrentRouter(handleRouter);
|
||||
await component.handle(handleRouter);
|
||||
}
|
||||
|
||||
component.setCurrentRouter(null);
|
||||
}
|
||||
|
||||
// Routes
|
||||
this.routes(initRouter, handleRouter);
|
||||
|
||||
@ -203,7 +214,7 @@ export default abstract class Application {
|
||||
|
||||
// Check security fields
|
||||
for (const component of this.components) {
|
||||
await component.checkSecuritySettings();
|
||||
await component.checkSecuritySettings?.();
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,7 +222,7 @@ export default abstract class Application {
|
||||
Logger.info('Stopping application...');
|
||||
|
||||
for (const component of this.components) {
|
||||
await component.stop();
|
||||
await component.stop?.();
|
||||
}
|
||||
|
||||
Logger.info(`${this.constructor.name} v${this.version} - bye`);
|
||||
@ -219,7 +230,7 @@ export default abstract class Application {
|
||||
|
||||
private routes(initRouter: Router, handleRouter: Router) {
|
||||
for (const controller of this.controllers) {
|
||||
if (controller.hasGlobalHandlers()) {
|
||||
if (controller.hasGlobalMiddlewares()) {
|
||||
controller.setupGlobalHandlers(handleRouter);
|
||||
|
||||
Logger.info(`Registered global middlewares for controller ${controller.constructor.name}`);
|
||||
@ -247,7 +258,7 @@ export default abstract class Application {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public getWebSocketListeners(): { [p: string]: WebSocketListener<any> } {
|
||||
public getWebSocketListeners(): { [p: string]: WebSocketListener<Application> } {
|
||||
return this.webSocketListeners;
|
||||
}
|
||||
|
||||
@ -255,7 +266,14 @@ export default abstract class Application {
|
||||
return this.cacheProvider;
|
||||
}
|
||||
|
||||
public getComponent<T extends ApplicationComponent<any>>(type: Type<T>): T | undefined {
|
||||
return <T>this.components.find(component => component.constructor === type);
|
||||
public as<C extends ApplicationComponent>(type: Type<C>): C {
|
||||
const component = this.components.find(component => component.constructor === type);
|
||||
if (!component) throw new Error(`This app doesn't have a ${type.name} component.`);
|
||||
return component as C;
|
||||
}
|
||||
|
||||
public asOptional<C extends ApplicationComponent>(type: Type<C>): C | null {
|
||||
const component = this.components.find(component => component.constructor === type);
|
||||
return component ? component as C : null;
|
||||
}
|
||||
}
|
@ -1,42 +1,24 @@
|
||||
import {Express, Router} from "express";
|
||||
import Logger from "./Logger";
|
||||
import {sleep} from "./Utils";
|
||||
import {sleep, Type} from "./Utils";
|
||||
import Application from "./Application";
|
||||
import config from "config";
|
||||
import SecurityError from "./SecurityError";
|
||||
import Middleware from "./Middleware";
|
||||
|
||||
export default abstract class ApplicationComponent<T> {
|
||||
private val?: T;
|
||||
protected app?: Application;
|
||||
export default abstract class ApplicationComponent {
|
||||
private currentRouter?: Router;
|
||||
private app?: Application;
|
||||
|
||||
public async checkSecuritySettings(): Promise<void> {
|
||||
}
|
||||
public async checkSecuritySettings?(): Promise<void>;
|
||||
|
||||
public async start(app: Express): Promise<void> {
|
||||
}
|
||||
public async start?(expressApp: Express): Promise<void>;
|
||||
|
||||
public async init(router: Router): Promise<void> {
|
||||
}
|
||||
public async init?(router: Router): Promise<void>;
|
||||
|
||||
public async handle(router: Router): Promise<void> {
|
||||
}
|
||||
public async handle?(router: Router): Promise<void>;
|
||||
|
||||
public async stop(): Promise<void> {
|
||||
|
||||
}
|
||||
|
||||
protected export(val: T) {
|
||||
this.val = val;
|
||||
}
|
||||
|
||||
public import(): T {
|
||||
if (!this.val) throw 'Cannot import if nothing was exported.';
|
||||
return this.val;
|
||||
}
|
||||
|
||||
public setApp(app: Application) {
|
||||
this.app = app;
|
||||
}
|
||||
public async stop?(): Promise<void>;
|
||||
|
||||
protected async prepare(name: string, prepare: () => Promise<void>): Promise<void> {
|
||||
let err;
|
||||
@ -71,4 +53,34 @@ export default abstract class ApplicationComponent<T> {
|
||||
throw new SecurityError(`${field} field not configured.`);
|
||||
}
|
||||
}
|
||||
|
||||
protected use<M extends Middleware>(middleware: Type<M>): void {
|
||||
if (!this.currentRouter) throw new Error('Cannot call this method outside init() and handle().');
|
||||
|
||||
const instance = new middleware(this.getApp());
|
||||
this.currentRouter.use(async (req, res, next) => {
|
||||
try {
|
||||
await instance.getRequestHandler()(req, res, next);
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected getCurrentRouter(): Router | null {
|
||||
return this.currentRouter || null;
|
||||
}
|
||||
|
||||
public setCurrentRouter(router: Router | null): void {
|
||||
this.currentRouter = router || undefined;
|
||||
}
|
||||
|
||||
protected getApp(): Application {
|
||||
if (!this.app) throw new Error('app field not initialized.');
|
||||
return this.app;
|
||||
}
|
||||
|
||||
public setApp(app: Application) {
|
||||
this.app = app;
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,13 @@ import express, {IRouter, RequestHandler, Router} from "express";
|
||||
import {PathParams} from "express-serve-static-core";
|
||||
import config from "config";
|
||||
import Logger from "./Logger";
|
||||
import Validator, {FileError, ValidationBag} from "./db/Validator";
|
||||
import Validator, {ValidationBag} from "./db/Validator";
|
||||
import FileUploadMiddleware from "./FileUploadMiddleware";
|
||||
import * as querystring from "querystring";
|
||||
import {ParsedUrlQueryInput} from "querystring";
|
||||
import Middleware from "./Middleware";
|
||||
import {Type} from "./Utils";
|
||||
import Application from "./Application";
|
||||
|
||||
export default abstract class Controller {
|
||||
private static readonly routes: { [p: string]: string } = {};
|
||||
@ -39,18 +42,19 @@ export default abstract class Controller {
|
||||
|
||||
private readonly router: Router = express.Router();
|
||||
private readonly fileUploadFormRouter: Router = express.Router();
|
||||
private app?: Application;
|
||||
|
||||
public getGlobalHandlers(): RequestHandler[] {
|
||||
public getGlobalMiddlewares(): Middleware[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
public hasGlobalHandlers(): boolean {
|
||||
return this.getGlobalHandlers().length > 0;
|
||||
public hasGlobalMiddlewares(): boolean {
|
||||
return this.getGlobalMiddlewares().length > 0;
|
||||
}
|
||||
|
||||
public setupGlobalHandlers(router: Router): void {
|
||||
for (const globalHandler of this.getGlobalHandlers()) {
|
||||
router.use(this.wrap(globalHandler));
|
||||
for (const middleware of this.getGlobalMiddlewares()) {
|
||||
router.use(this.wrap(middleware.getRequestHandler()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,19 +79,19 @@ export default abstract class Controller {
|
||||
this.router.use(handler);
|
||||
}
|
||||
|
||||
protected get(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: (RequestHandler | FileUploadMiddleware)[]) {
|
||||
protected get(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: (Type<Middleware>)[]) {
|
||||
this.handle('get', path, handler, routeName, ...middlewares);
|
||||
}
|
||||
|
||||
protected post(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: (RequestHandler | FileUploadMiddleware)[]) {
|
||||
protected post(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: (Type<Middleware>)[]) {
|
||||
this.handle('post', path, handler, routeName, ...middlewares);
|
||||
}
|
||||
|
||||
protected put(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: (RequestHandler | FileUploadMiddleware)[]) {
|
||||
protected put(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: (Type<Middleware>)[]) {
|
||||
this.handle('put', path, handler, routeName, ...middlewares);
|
||||
}
|
||||
|
||||
protected delete(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: (RequestHandler | FileUploadMiddleware)[]) {
|
||||
protected delete(path: PathParams, handler: RequestHandler, routeName?: string, ...middlewares: (Type<Middleware>)[]) {
|
||||
this.handle('delete', path, handler, routeName, ...middlewares);
|
||||
}
|
||||
|
||||
@ -96,14 +100,15 @@ export default abstract class Controller {
|
||||
path: PathParams,
|
||||
handler: RequestHandler,
|
||||
routeName?: string,
|
||||
...middlewares: (RequestHandler | FileUploadMiddleware)[]
|
||||
...middlewares: (Type<Middleware>)[]
|
||||
): void {
|
||||
this.registerRoutes(path, handler, routeName);
|
||||
for (const middleware of middlewares) {
|
||||
if (middleware instanceof FileUploadMiddleware) {
|
||||
this.fileUploadFormRouter[action](path, this.wrap(FILE_UPLOAD_MIDDLEWARE(middleware)));
|
||||
const instance = new middleware(this.getApp());
|
||||
if (instance instanceof FileUploadMiddleware) {
|
||||
this.fileUploadFormRouter[action](path, this.wrap(instance.getRequestHandler()));
|
||||
} else {
|
||||
this.router[action](path, this.wrap(middleware));
|
||||
this.router[action](path, this.wrap(instance.getRequestHandler()));
|
||||
}
|
||||
}
|
||||
this.router[action](path, this.wrap(handler));
|
||||
@ -164,33 +169,15 @@ export default abstract class Controller {
|
||||
|
||||
if (bag.hasMessages()) throw bag;
|
||||
}
|
||||
|
||||
protected getApp(): Application {
|
||||
if (!this.app) throw new Error('Application not initialized.');
|
||||
return this.app;
|
||||
}
|
||||
|
||||
public setApp(app: Application) {
|
||||
this.app = app;
|
||||
}
|
||||
}
|
||||
|
||||
export type RouteParams = { [p: string]: string } | string[] | string | number;
|
||||
|
||||
const FILE_UPLOAD_MIDDLEWARE: (fileUploadMiddleware: FileUploadMiddleware) => RequestHandler = (fileUploadMiddleware: FileUploadMiddleware) => {
|
||||
return async (req, res, next) => {
|
||||
const form = fileUploadMiddleware.formFactory();
|
||||
try {
|
||||
await new Promise<any>((resolve, reject) => {
|
||||
form.parse(req, (err, fields, files) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
req.body = fields;
|
||||
req.files = files;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
const bag = new ValidationBag();
|
||||
const fileError = new FileError(e);
|
||||
fileError.thingName = fileUploadMiddleware.defaultField;
|
||||
bag.addMessage(fileError);
|
||||
next(bag);
|
||||
return;
|
||||
}
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
7
src/Extendable.ts
Normal file
7
src/Extendable.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import {Type} from "./Utils";
|
||||
|
||||
export default interface Extendable<ComponentClass> {
|
||||
as<C extends ComponentClass>(type: Type<C>): C;
|
||||
|
||||
asOptional<C extends ComponentClass>(type: Type<C>): C | null;
|
||||
}
|
@ -1,11 +1,35 @@
|
||||
import {IncomingForm} from "formidable";
|
||||
import Middleware from "./Middleware";
|
||||
import {NextFunction, Request, Response} from "express";
|
||||
import {FileError, ValidationBag} from "./db/Validator";
|
||||
|
||||
export default class FileUploadMiddleware {
|
||||
public readonly formFactory: () => IncomingForm;
|
||||
public readonly defaultField: string;
|
||||
export default abstract class FileUploadMiddleware extends Middleware {
|
||||
protected abstract makeForm(): IncomingForm;
|
||||
|
||||
public constructor(formFactory: () => IncomingForm, defaultField: string) {
|
||||
this.formFactory = formFactory;
|
||||
this.defaultField = defaultField;
|
||||
protected abstract getDefaultField(): string;
|
||||
|
||||
public async handle(req: Request, res: Response, next: NextFunction): Promise<void> {
|
||||
const form = this.makeForm();
|
||||
try {
|
||||
await new Promise<any>((resolve, reject) => {
|
||||
form.parse(req, (err, fields, files) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
req.body = fields;
|
||||
req.files = files;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
const bag = new ValidationBag();
|
||||
const fileError = new FileError(e);
|
||||
fileError.thingName = this.getDefaultField();
|
||||
bag.addMessage(fileError);
|
||||
next(bag);
|
||||
return;
|
||||
}
|
||||
next();
|
||||
}
|
||||
}
|
28
src/Middleware.ts
Normal file
28
src/Middleware.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import {RequestHandler} from "express";
|
||||
import {NextFunction, Request, Response} from "express-serve-static-core";
|
||||
import Application from "./Application";
|
||||
|
||||
export default abstract class Middleware {
|
||||
public constructor(
|
||||
protected readonly app: Application,
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
protected abstract async handle(req: Request, res: Response, next: NextFunction): Promise<void>;
|
||||
|
||||
public getRequestHandler(): RequestHandler {
|
||||
return async (req, res, next): Promise<void> => {
|
||||
try {
|
||||
if (req.middlewares.find(m => m.constructor === this.constructor)) {
|
||||
next();
|
||||
} else {
|
||||
req.middlewares.push(this);
|
||||
return await this.handle(req, res, next);
|
||||
}
|
||||
} catch (e) {
|
||||
next(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -1,32 +1,58 @@
|
||||
import ApplicationComponent from "../ApplicationComponent";
|
||||
import {NextFunction, Request, Response, Router} from "express";
|
||||
import {NextFunction, Request, Response} from "express";
|
||||
import AuthGuard from "./AuthGuard";
|
||||
import Controller from "../Controller";
|
||||
import {ForbiddenHttpError} from "../HttpError";
|
||||
import Middleware from "../Middleware";
|
||||
import User from "./models/User";
|
||||
import AuthProof from "./AuthProof";
|
||||
|
||||
export default class AuthComponent<T extends AuthGuard<any>> extends ApplicationComponent<void> {
|
||||
private readonly authGuard: T;
|
||||
export default class AuthComponent extends ApplicationComponent {
|
||||
private readonly authGuard: AuthGuard<AuthProof<User>>;
|
||||
|
||||
public constructor(authGuard: T) {
|
||||
public constructor(authGuard: AuthGuard<AuthProof<User>>) {
|
||||
super();
|
||||
this.authGuard = authGuard;
|
||||
}
|
||||
|
||||
public async init(router: Router): Promise<void> {
|
||||
router.use(async (req, res, next) => {
|
||||
req.authGuard = this.authGuard;
|
||||
req.models.user = res.locals.user = await (await req.authGuard.getProof(req))?.getResource();
|
||||
next();
|
||||
});
|
||||
public async init(): Promise<void> {
|
||||
this.use(AuthMiddleware);
|
||||
}
|
||||
|
||||
public getAuthGuard(): T {
|
||||
public getAuthGuard(): AuthGuard<AuthProof<User>> {
|
||||
return this.authGuard;
|
||||
}
|
||||
}
|
||||
|
||||
export const REQUIRE_REQUEST_AUTH_MIDDLEWARE = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
||||
let proof = await req.authGuard.isAuthenticatedViaRequest(req);
|
||||
export class AuthMiddleware extends Middleware {
|
||||
private authGuard?: AuthGuard<AuthProof<User>>;
|
||||
private user: User | null = null;
|
||||
|
||||
protected async handle(req: Request, res: Response, next: NextFunction): Promise<void> {
|
||||
this.authGuard = this.app.as(AuthComponent).getAuthGuard();
|
||||
|
||||
const proof = await this.authGuard.isAuthenticated(req.session!);
|
||||
if (proof) {
|
||||
this.user = await proof.getResource();
|
||||
res.locals.user = this.user;
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
public getUser(): User | null {
|
||||
return this.user;
|
||||
}
|
||||
|
||||
public getAuthGuard(): AuthGuard<AuthProof<User>> {
|
||||
if (!this.authGuard) throw new Error('AuthGuard was not initialized.');
|
||||
return this.authGuard;
|
||||
}
|
||||
}
|
||||
|
||||
export class RequireRequestAuthMiddleware extends Middleware {
|
||||
protected async handle(req: Request, res: Response, next: NextFunction): Promise<void> {
|
||||
const proof = await req.as(AuthMiddleware).getAuthGuard().isAuthenticatedViaRequest(req);
|
||||
if (!proof) {
|
||||
req.flash('error', `You must be logged in to access ${req.url}.`);
|
||||
res.redirect(Controller.route('auth', undefined, {
|
||||
@ -36,19 +62,21 @@ export const REQUIRE_REQUEST_AUTH_MIDDLEWARE = async (req: Request, res: Respons
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const REQUIRE_AUTH_MIDDLEWARE = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
||||
export class RequireAuthMiddleware extends Middleware {
|
||||
protected async handle(req: Request, res: Response, next: NextFunction): Promise<void> {
|
||||
const authGuard = req.as(AuthMiddleware).getAuthGuard();
|
||||
// Via request
|
||||
let proof = await req.authGuard.isAuthenticatedViaRequest(req);
|
||||
if (proof) {
|
||||
|
||||
if (await authGuard.isAuthenticatedViaRequest(req)) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
// Via session
|
||||
proof = await req.authGuard.isAuthenticated(req.session!);
|
||||
if (!proof) {
|
||||
if (!await authGuard.isAuthenticated(req.session!)) {
|
||||
req.flash('error', `You must be logged in to access ${req.url}.`);
|
||||
res.redirect(Controller.route('auth', undefined, {
|
||||
redirect_uri: req.url,
|
||||
@ -57,19 +85,27 @@ export const REQUIRE_AUTH_MIDDLEWARE = async (req: Request, res: Response, next:
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
export const REQUIRE_GUEST_MIDDLEWARE = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
||||
if (await req.authGuard.isAuthenticated(req.session!)) {
|
||||
}
|
||||
}
|
||||
|
||||
export class RequireGuestMiddleware extends Middleware {
|
||||
protected async handle(req: Request, res: Response, next: NextFunction): Promise<void> {
|
||||
if (await req.as(AuthMiddleware).getAuthGuard().isAuthenticated(req.session!)) {
|
||||
res.redirectBack();
|
||||
return;
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
export const REQUIRE_ADMIN_MIDDLEWARE = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
||||
if (!req.models.user || !req.models.user.is_admin) {
|
||||
}
|
||||
}
|
||||
|
||||
export class RequireAdminMiddleware extends Middleware {
|
||||
protected async handle(req: Request, res: Response, next: NextFunction): Promise<void> {
|
||||
const user = req.as(AuthMiddleware).getUser();
|
||||
if (!user || !user.is_admin) {
|
||||
throw new ForbiddenHttpError('secret tool', req.url);
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import Controller from "../Controller";
|
||||
import {NextFunction, Request, Response} from "express";
|
||||
import {REQUIRE_AUTH_MIDDLEWARE, REQUIRE_GUEST_MIDDLEWARE} from "./AuthComponent";
|
||||
import {AuthMiddleware, RequireAuthMiddleware, RequireGuestMiddleware} from "./AuthComponent";
|
||||
|
||||
export default abstract class AuthController extends Controller {
|
||||
public getRoutesPrefix(): string {
|
||||
@ -8,10 +8,10 @@ export default abstract class AuthController extends Controller {
|
||||
}
|
||||
|
||||
public routes() {
|
||||
this.get('/', this.getAuth, 'auth', REQUIRE_GUEST_MIDDLEWARE);
|
||||
this.post('/', this.postAuth, 'auth', REQUIRE_GUEST_MIDDLEWARE);
|
||||
this.get('/', this.getAuth, 'auth', RequireGuestMiddleware);
|
||||
this.post('/', this.postAuth, 'auth', RequireGuestMiddleware);
|
||||
this.get('/check', this.getCheckAuth, 'check_auth');
|
||||
this.post('/logout', this.postLogout, 'logout', REQUIRE_AUTH_MIDDLEWARE);
|
||||
this.post('/logout', this.postLogout, 'logout', RequireAuthMiddleware);
|
||||
}
|
||||
|
||||
protected async getAuth(req: Request, res: Response, next: NextFunction): Promise<void> {
|
||||
@ -26,7 +26,7 @@ export default abstract class AuthController extends Controller {
|
||||
protected abstract async getCheckAuth(req: Request, res: Response, next: NextFunction): Promise<void>;
|
||||
|
||||
protected async postLogout(req: Request, res: Response, next: NextFunction): Promise<void> {
|
||||
const proof = await req.authGuard.getProof(req);
|
||||
const proof = await req.as(AuthMiddleware).getAuthGuard().getProof(req);
|
||||
await proof?.revoke();
|
||||
req.flash('success', 'Successfully logged out.');
|
||||
res.redirect(req.query.redirect_uri?.toString() || '/');
|
||||
|
@ -53,7 +53,7 @@ export default abstract class AuthGuard<P extends AuthProof<User>> {
|
||||
proof: P,
|
||||
onLogin?: (user: User) => Promise<void>,
|
||||
onRegister?: (connection: Connection, user: User) => Promise<RegisterCallback[]>
|
||||
): Promise<void> {
|
||||
): Promise<User> {
|
||||
if (!await proof.isValid()) throw new InvalidAuthProofError();
|
||||
if (!await proof.isAuthorized()) throw new UnauthorizedAuthProofError();
|
||||
|
||||
@ -95,6 +95,8 @@ export default abstract class AuthGuard<P extends AuthProof<User>> {
|
||||
// Login
|
||||
session.is_authenticated = true;
|
||||
if (onLogin) await onLogin(user);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,17 +9,19 @@ import {AuthError, PendingApprovalAuthError, RegisterCallback} from "../AuthGuar
|
||||
import geoip from "geoip-lite";
|
||||
import AuthController from "../AuthController";
|
||||
import RedirectBackComponent from "../../components/RedirectBackComponent";
|
||||
import {AuthMiddleware} from "../AuthComponent";
|
||||
import User from "../models/User";
|
||||
|
||||
|
||||
export default abstract class MagicLinkAuthController extends AuthController {
|
||||
public static async checkAndAuth(req: Request, res: Response, magicLink: MagicLink): Promise<void> {
|
||||
public static async checkAndAuth(req: Request, res: Response, magicLink: MagicLink): Promise<User | null> {
|
||||
if (magicLink.getSessionID() !== req.sessionID!) throw new BadOwnerMagicLink();
|
||||
if (!await magicLink.isAuthorized()) throw new UnauthorizedMagicLink();
|
||||
if (!await magicLink.isValid()) throw new InvalidMagicLink();
|
||||
|
||||
// Auth
|
||||
try {
|
||||
await req.authGuard.authenticateOrRegister(req.session!, magicLink, undefined, async (connection, user) => {
|
||||
return await req.as(AuthMiddleware).getAuthGuard().authenticateOrRegister(req.session!, magicLink, undefined, async (connection, user) => {
|
||||
const callbacks: RegisterCallback[] = [];
|
||||
|
||||
const userEmail = UserEmail.create({
|
||||
@ -46,7 +48,7 @@ export default abstract class MagicLinkAuthController extends AuthController {
|
||||
res.redirect('/');
|
||||
}
|
||||
});
|
||||
return;
|
||||
return null;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
@ -139,10 +141,11 @@ export default abstract class MagicLinkAuthController extends AuthController {
|
||||
return;
|
||||
}
|
||||
|
||||
await MagicLinkAuthController.checkAndAuth(req, res, magicLink);
|
||||
const user = await MagicLinkAuthController.checkAndAuth(req, res, magicLink);
|
||||
|
||||
if (user) {
|
||||
// Auth success
|
||||
const username = req.models.user?.name;
|
||||
const username = user.name;
|
||||
res.format({
|
||||
json: () => {
|
||||
res.json({'status': 'success', 'message': `Welcome, ${username}!`});
|
||||
@ -152,7 +155,7 @@ export default abstract class MagicLinkAuthController extends AuthController {
|
||||
res.redirect('/');
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ import ApplicationComponent from "../ApplicationComponent";
|
||||
import {ForbiddenHttpError} from "../HttpError";
|
||||
import Logger from "../Logger";
|
||||
|
||||
export default class AutoUpdateComponent extends ApplicationComponent<void> {
|
||||
export default class AutoUpdateComponent extends ApplicationComponent {
|
||||
public async checkSecuritySettings(): Promise<void> {
|
||||
this.checkSecurityConfigField('gitlab_webhook_token');
|
||||
}
|
||||
@ -39,7 +39,7 @@ export default class AutoUpdateComponent extends ApplicationComponent<void> {
|
||||
await this.runCommand(`yarn dist`);
|
||||
|
||||
// Stop app
|
||||
await this.app!.stop();
|
||||
await this.getApp().stop();
|
||||
|
||||
Logger.info('Success!');
|
||||
} catch (e) {
|
||||
|
@ -2,8 +2,9 @@ import ApplicationComponent from "../ApplicationComponent";
|
||||
import {Request, Router} from "express";
|
||||
import crypto from "crypto";
|
||||
import {BadRequestError} from "../HttpError";
|
||||
import {AuthMiddleware} from "../auth/AuthComponent";
|
||||
|
||||
export default class CsrfProtectionComponent extends ApplicationComponent<void> {
|
||||
export default class CsrfProtectionComponent extends ApplicationComponent {
|
||||
private static readonly excluders: ((req: Request) => boolean)[] = [];
|
||||
|
||||
public static getCSRFToken(session: Express.Session): string {
|
||||
@ -33,18 +34,17 @@ export default class CsrfProtectionComponent extends ApplicationComponent<void>
|
||||
|
||||
if (!['GET', 'HEAD', 'OPTIONS'].find(s => s === req.method)) {
|
||||
try {
|
||||
if (!(await req.authGuard.isAuthenticatedViaRequest(req))) {
|
||||
if (!await req.as(AuthMiddleware).getAuthGuard().isAuthenticatedViaRequest(req)) {
|
||||
if (req.session.csrf === undefined) {
|
||||
throw new InvalidCsrfTokenError(req.baseUrl, `You weren't assigned any CSRF token.`);
|
||||
return next(new InvalidCsrfTokenError(req.baseUrl, `You weren't assigned any CSRF token.`));
|
||||
} else if (req.body.csrf === undefined) {
|
||||
throw new InvalidCsrfTokenError(req.baseUrl, `You didn't provide any CSRF token.`);
|
||||
return next(new InvalidCsrfTokenError(req.baseUrl, `You didn't provide any CSRF token.`));
|
||||
} else if (req.session.csrf !== req.body.csrf) {
|
||||
throw new InvalidCsrfTokenError(req.baseUrl, `Tokens don't match.`);
|
||||
return next(new InvalidCsrfTokenError(req.baseUrl, `Tokens don't match.`));
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
next(e);
|
||||
return;
|
||||
return next(e);
|
||||
}
|
||||
}
|
||||
next();
|
||||
|
@ -3,8 +3,10 @@ import express, {Express, Router} from "express";
|
||||
import Logger from "../Logger";
|
||||
import {Server} from "http";
|
||||
import compression from "compression";
|
||||
import Middleware from "../Middleware";
|
||||
import {Type} from "../Utils";
|
||||
|
||||
export default class ExpressAppComponent extends ApplicationComponent<void> {
|
||||
export default class ExpressAppComponent extends ApplicationComponent {
|
||||
private readonly addr: string;
|
||||
private readonly port: number;
|
||||
private server?: Server;
|
||||
@ -39,8 +41,12 @@ export default class ExpressAppComponent extends ApplicationComponent<void> {
|
||||
router.use(compression());
|
||||
|
||||
router.use((req, res, next) => {
|
||||
req.models = {};
|
||||
req.modelCollections = {};
|
||||
req.middlewares = [];
|
||||
req.as = <M extends Middleware>(type: Type<M>) => {
|
||||
const middleware = req.middlewares.find(m => m.constructor === type);
|
||||
if (!middleware) throw new Error('Middleware ' + type.name + ' not present in this request.');
|
||||
return middleware as M;
|
||||
};
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import ApplicationComponent from "../ApplicationComponent";
|
||||
import {Router} from "express";
|
||||
|
||||
export default class FormHelperComponent extends ApplicationComponent<void> {
|
||||
export default class FormHelperComponent extends ApplicationComponent {
|
||||
public async init(router: Router): Promise<void> {
|
||||
router.use((req, res, next) => {
|
||||
if (!req.session) {
|
||||
|
@ -3,7 +3,7 @@ import onFinished from "on-finished";
|
||||
import Logger from "../Logger";
|
||||
import {Request, Response, Router} from "express";
|
||||
|
||||
export default class LogRequestsComponent extends ApplicationComponent<void> {
|
||||
export default class LogRequestsComponent extends ApplicationComponent {
|
||||
private static fullRequests: boolean = false;
|
||||
|
||||
public static logFullHttpRequests() {
|
||||
|
@ -4,8 +4,7 @@ import Mail from "../Mail";
|
||||
import config from "config";
|
||||
import SecurityError from "../SecurityError";
|
||||
|
||||
export default class MailComponent extends ApplicationComponent<void> {
|
||||
|
||||
export default class MailComponent extends ApplicationComponent {
|
||||
|
||||
public async checkSecuritySettings(): Promise<void> {
|
||||
if (!config.get<boolean>('mail.secure')) {
|
||||
|
@ -4,7 +4,7 @@ import {ServiceUnavailableHttpError} from "../HttpError";
|
||||
import Application from "../Application";
|
||||
import config from "config";
|
||||
|
||||
export default class MaintenanceComponent extends ApplicationComponent<void> {
|
||||
export default class MaintenanceComponent extends ApplicationComponent {
|
||||
private readonly application: Application;
|
||||
private readonly canServe: () => boolean;
|
||||
|
||||
|
@ -2,7 +2,7 @@ import ApplicationComponent from "../ApplicationComponent";
|
||||
import {Express} from "express";
|
||||
import MysqlConnectionManager from "../db/MysqlConnectionManager";
|
||||
|
||||
export default class MysqlComponent extends ApplicationComponent<void> {
|
||||
export default class MysqlComponent extends ApplicationComponent {
|
||||
public async start(app: Express): Promise<void> {
|
||||
await this.prepare('Mysql connection', () => MysqlConnectionManager.prepare());
|
||||
}
|
||||
@ -12,7 +12,7 @@ export default class MysqlComponent extends ApplicationComponent<void> {
|
||||
}
|
||||
|
||||
public canServe(): boolean {
|
||||
return MysqlConnectionManager.pool !== undefined;
|
||||
return MysqlConnectionManager.isReady();
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import nunjucks, {Environment} from "nunjucks";
|
||||
import config from "config";
|
||||
import {Express, Router} from "express";
|
||||
import {Express, NextFunction, Request, Response, Router} from "express";
|
||||
import ApplicationComponent from "../ApplicationComponent";
|
||||
import Controller from "../Controller";
|
||||
import {ServerError} from "../HttpError";
|
||||
@ -8,8 +8,11 @@ import * as querystring from "querystring";
|
||||
import {ParsedUrlQueryInput} from "querystring";
|
||||
import * as util from "util";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
import Logger from "../Logger";
|
||||
import Middleware from "../Middleware";
|
||||
|
||||
export default class NunjucksComponent extends ApplicationComponent<void> {
|
||||
export default class NunjucksComponent extends ApplicationComponent {
|
||||
private readonly viewsPath: string[];
|
||||
private env?: Environment;
|
||||
|
||||
@ -20,13 +23,14 @@ export default class NunjucksComponent extends ApplicationComponent<void> {
|
||||
|
||||
public async start(app: Express): Promise<void> {
|
||||
let coreVersion = 'unknown';
|
||||
const file = fs.existsSync(path.join(__dirname, '../../package.json')) ?
|
||||
path.join(__dirname, '../../package.json') :
|
||||
path.join(__dirname, '../package.json');
|
||||
|
||||
try {
|
||||
coreVersion = require('../../package.json').version;
|
||||
coreVersion = JSON.parse(fs.readFileSync(file).toString()).version;
|
||||
} catch (e) {
|
||||
try {
|
||||
coreVersion = require('../package.json').version;
|
||||
} catch (e) {
|
||||
}
|
||||
Logger.warn('Couldn\'t determine coreVersion.', e);
|
||||
}
|
||||
|
||||
this.env = new nunjucks.Environment([
|
||||
@ -43,7 +47,7 @@ export default class NunjucksComponent extends ApplicationComponent<void> {
|
||||
if (path === null) throw new ServerError(`Route ${route} not found.`);
|
||||
return path;
|
||||
})
|
||||
.addGlobal('app_version', this.app!.getVersion())
|
||||
.addGlobal('app_version', this.getApp().getVersion())
|
||||
.addGlobal('core_version', coreVersion)
|
||||
.addGlobal('querystring', querystring)
|
||||
.addGlobal('app', config.get('app'))
|
||||
@ -59,18 +63,29 @@ export default class NunjucksComponent extends ApplicationComponent<void> {
|
||||
}
|
||||
|
||||
public async init(router: Router): Promise<void> {
|
||||
router.use((req, res, next) => {
|
||||
req.env = this.env!;
|
||||
res.locals.url = req.url;
|
||||
res.locals.params = req.params;
|
||||
res.locals.query = req.query;
|
||||
res.locals.body = req.body;
|
||||
|
||||
next();
|
||||
});
|
||||
this.use(NunjucksMiddleware);
|
||||
}
|
||||
|
||||
public getEnv(): Environment | undefined {
|
||||
return this.env;
|
||||
}
|
||||
}
|
||||
|
||||
export class NunjucksMiddleware extends Middleware {
|
||||
private env?: Environment;
|
||||
|
||||
protected async handle(req: Request, res: Response, next: NextFunction): Promise<void> {
|
||||
this.env = this.app.as(NunjucksComponent).getEnv();
|
||||
res.locals.url = req.url;
|
||||
res.locals.params = req.params;
|
||||
res.locals.query = req.query;
|
||||
res.locals.body = req.body;
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
public getEnvironment(): Environment {
|
||||
if (!this.env) throw new Error('Environment not initialized.');
|
||||
return this.env;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import {ServerError} from "../HttpError";
|
||||
import onFinished from "on-finished";
|
||||
import Logger from "../Logger";
|
||||
|
||||
export default class RedirectBackComponent extends ApplicationComponent<void> {
|
||||
export default class RedirectBackComponent extends ApplicationComponent {
|
||||
public static getPreviousURL(req: Request, defaultUrl?: string): string | undefined {
|
||||
return req.session?.previousUrl || defaultUrl;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import CacheProvider from "../CacheProvider";
|
||||
|
||||
const RedisStore = connect_redis(session);
|
||||
|
||||
export default class RedisComponent extends ApplicationComponent<void> implements CacheProvider {
|
||||
export default class RedisComponent extends ApplicationComponent implements CacheProvider {
|
||||
private redisClient?: RedisClient;
|
||||
private store?: Store;
|
||||
|
||||
|
@ -3,7 +3,7 @@ import express, {Router} from "express";
|
||||
import {PathParams} from "express-serve-static-core";
|
||||
import * as path from "path";
|
||||
|
||||
export default class ServeStaticDirectoryComponent extends ApplicationComponent<void> {
|
||||
export default class ServeStaticDirectoryComponent extends ApplicationComponent {
|
||||
private readonly root: string;
|
||||
private readonly path?: PathParams;
|
||||
|
||||
|
@ -6,7 +6,7 @@ import flash from "connect-flash";
|
||||
import {Router} from "express";
|
||||
import SecurityError from "../SecurityError";
|
||||
|
||||
export default class SessionComponent extends ApplicationComponent<void> {
|
||||
export default class SessionComponent extends ApplicationComponent {
|
||||
private readonly storeComponent: RedisComponent;
|
||||
|
||||
public constructor(storeComponent: RedisComponent) {
|
||||
|
@ -11,7 +11,7 @@ import RedisComponent from "./RedisComponent";
|
||||
import WebSocketListener from "../WebSocketListener";
|
||||
import NunjucksComponent from "./NunjucksComponent";
|
||||
|
||||
export default class WebSocketServerComponent extends ApplicationComponent<void> {
|
||||
export default class WebSocketServerComponent extends ApplicationComponent {
|
||||
private wss?: WebSocket.Server;
|
||||
|
||||
constructor(
|
||||
|
@ -7,8 +7,9 @@ import ModelFactory from "./ModelFactory";
|
||||
import ModelRelation from "./ModelRelation";
|
||||
import ModelQuery, {ModelQueryResult, SelectFields} from "./ModelQuery";
|
||||
import {Request} from "express";
|
||||
import Extendable from "../Extendable";
|
||||
|
||||
export default abstract class Model {
|
||||
export default abstract class Model implements Extendable<ModelComponent<Model>> {
|
||||
public static get table(): string {
|
||||
return this.name
|
||||
.replace(/(?:^|\.?)([A-Z])/g, (x, y) => '_' + y.toLowerCase())
|
||||
@ -44,14 +45,14 @@ export default abstract class Model {
|
||||
return ModelFactory.get(this).paginate(request, perPage, query);
|
||||
}
|
||||
|
||||
protected readonly _factory: ModelFactory<any>;
|
||||
private readonly _components: ModelComponent<any>[] = [];
|
||||
private readonly _validators: { [key: string]: Validator<any> } = {};
|
||||
protected readonly _factory: ModelFactory<Model>;
|
||||
private readonly _components: ModelComponent<this>[] = [];
|
||||
private readonly _validators: { [key: string]: Validator<any> | undefined } = {};
|
||||
private _exists: boolean;
|
||||
|
||||
[key: string]: any;
|
||||
|
||||
public constructor(factory: ModelFactory<any>, isNew: boolean) {
|
||||
public constructor(factory: ModelFactory<Model>, isNew: boolean) {
|
||||
if (!factory || !(factory instanceof ModelFactory)) throw new Error('Cannot instantiate model directly.');
|
||||
this._factory = factory;
|
||||
this.init();
|
||||
@ -71,15 +72,26 @@ export default abstract class Model {
|
||||
this._components.push(modelComponent);
|
||||
}
|
||||
|
||||
public as<T extends ModelComponent<any>>(type: Type<T>): T {
|
||||
public as<C extends ModelComponent<Model>>(type: Type<C>): C {
|
||||
for (const component of this._components) {
|
||||
if (component instanceof type) {
|
||||
return <any>this;
|
||||
return this as unknown as C;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Component ${type.name} was not initialized for this ${this.constructor.name}.`);
|
||||
}
|
||||
|
||||
public asOptional<C extends ModelComponent<Model>>(type: Type<C>): C | null {
|
||||
for (const component of this._components) {
|
||||
if (component instanceof type) {
|
||||
return this as unknown as C;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public updateWithData(data: any) {
|
||||
for (const property of this._properties) {
|
||||
if (data[property] !== undefined) {
|
||||
@ -92,19 +104,16 @@ export default abstract class Model {
|
||||
* Override this to automatically fill obvious missing data i.e. from relation or default value that are fetched
|
||||
* asynchronously.
|
||||
*/
|
||||
protected async autoFill(): Promise<void> {
|
||||
}
|
||||
protected async autoFill?(): Promise<void>;
|
||||
|
||||
protected async beforeSave(connection: Connection): Promise<void> {
|
||||
}
|
||||
protected async beforeSave?(connection: Connection): Promise<void>;
|
||||
|
||||
protected async afterSave(): Promise<void> {
|
||||
}
|
||||
protected async afterSave?(): Promise<void>;
|
||||
|
||||
public async save(connection?: Connection, postHook?: (callback: () => Promise<void>) => void): Promise<void> {
|
||||
if (connection && !postHook) throw new Error('If connection is provided, postHook must be provided too.');
|
||||
|
||||
await this.autoFill();
|
||||
await this.autoFill?.();
|
||||
await this.validate(false, connection);
|
||||
|
||||
const needs_full_update = connection ?
|
||||
@ -120,7 +129,7 @@ export default abstract class Model {
|
||||
this.updateWithData(result.results[0]);
|
||||
}
|
||||
|
||||
await this.afterSave();
|
||||
await this.afterSave?.();
|
||||
};
|
||||
|
||||
if (connection) {
|
||||
@ -132,7 +141,7 @@ export default abstract class Model {
|
||||
|
||||
private async saveTransaction(connection: Connection): Promise<boolean> {
|
||||
// Before save
|
||||
await this.beforeSave(connection);
|
||||
await this.beforeSave?.(connection);
|
||||
if (!this.exists() && this.hasOwnProperty('created_at')) {
|
||||
this.created_at = new Date();
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ export default abstract class ModelComponent<T extends Model> {
|
||||
}
|
||||
|
||||
public applyToModel(): void {
|
||||
this.init();
|
||||
this.init?.();
|
||||
|
||||
for (const property of this._properties) {
|
||||
if (!property.startsWith('_')) {
|
||||
@ -32,7 +32,7 @@ export default abstract class ModelComponent<T extends Model> {
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract init(): void;
|
||||
protected init?(): void;
|
||||
|
||||
protected setValidation<V>(propertyName: keyof this): Validator<V> {
|
||||
const validator = new Validator<V>();
|
||||
|
@ -21,6 +21,10 @@ export default class MysqlConnectionManager {
|
||||
private static migrationsRegistered: boolean = false;
|
||||
private static readonly migrations: Migration[] = [];
|
||||
|
||||
public static isReady(): boolean {
|
||||
return this.databaseReady && this.currentPool !== undefined;
|
||||
}
|
||||
|
||||
public static registerMigrations(migrations: Type<Migration>[]) {
|
||||
if (!this.migrationsRegistered) {
|
||||
this.migrationsRegistered = true;
|
||||
|
@ -1,6 +1,5 @@
|
||||
import config from "config";
|
||||
import Controller from "../Controller";
|
||||
import {REQUIRE_ADMIN_MIDDLEWARE, REQUIRE_AUTH_MIDDLEWARE} from "../auth/AuthComponent";
|
||||
import User from "../auth/models/User";
|
||||
import {Request, Response} from "express";
|
||||
import {BadRequestError, NotFoundHttpError} from "../HttpError";
|
||||
@ -8,6 +7,7 @@ import Mail from "../Mail";
|
||||
import {ACCOUNT_REVIEW_NOTICE_MAIL_TEMPLATE} from "../Mails";
|
||||
import UserEmail from "../auth/models/UserEmail";
|
||||
import UserApprovedComponent from "../auth/models/UserApprovedComponent";
|
||||
import {RequireAdminMiddleware, RequireAuthMiddleware} from "../auth/AuthComponent";
|
||||
|
||||
export default class BackendController extends Controller {
|
||||
private static readonly menu: BackendMenuElement[] = [];
|
||||
@ -37,11 +37,11 @@ export default class BackendController extends Controller {
|
||||
}
|
||||
|
||||
public routes(): void {
|
||||
this.get('/', this.getIndex, 'backend', REQUIRE_AUTH_MIDDLEWARE, REQUIRE_ADMIN_MIDDLEWARE);
|
||||
this.get('/', this.getIndex, 'backend', RequireAuthMiddleware, RequireAdminMiddleware);
|
||||
if (User.isApprovalMode()) {
|
||||
this.get('/accounts-approval', this.getAccountApproval, 'accounts-approval', REQUIRE_AUTH_MIDDLEWARE, REQUIRE_ADMIN_MIDDLEWARE);
|
||||
this.post('/accounts-approval/approve', this.postApproveAccount, 'approve-account', REQUIRE_AUTH_MIDDLEWARE, REQUIRE_ADMIN_MIDDLEWARE);
|
||||
this.post('/accounts-approval/reject', this.postRejectAccount, 'reject-account', REQUIRE_AUTH_MIDDLEWARE, REQUIRE_ADMIN_MIDDLEWARE);
|
||||
this.get('/accounts-approval', this.getAccountApproval, 'accounts-approval', RequireAuthMiddleware, RequireAdminMiddleware);
|
||||
this.post('/accounts-approval/approve', this.postApproveAccount, 'approve-account', RequireAuthMiddleware, RequireAdminMiddleware);
|
||||
this.post('/accounts-approval/reject', this.postRejectAccount, 'reject-account', RequireAuthMiddleware, RequireAdminMiddleware);
|
||||
}
|
||||
}
|
||||
|
||||
|
19
src/types/Express.d.ts
vendored
19
src/types/Express.d.ts
vendored
@ -1,21 +1,18 @@
|
||||
import {Environment} from "nunjucks";
|
||||
import Model from "../db/Model";
|
||||
import AuthGuard from "../auth/AuthGuard";
|
||||
import {Files} from "formidable";
|
||||
import User from "../auth/models/User";
|
||||
import {Type} from "../Utils";
|
||||
import Middleware from "../Middleware";
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
export interface Request {
|
||||
env: Environment;
|
||||
models: {
|
||||
user?: User | null,
|
||||
[p: string]: Model | null | undefined,
|
||||
};
|
||||
modelCollections: { [p: string]: Model[] | null };
|
||||
authGuard: AuthGuard<any>;
|
||||
files: Files;
|
||||
|
||||
|
||||
middlewares: Middleware[];
|
||||
|
||||
as<M extends Middleware>(type: Type<M>): M;
|
||||
|
||||
|
||||
flash(): { [key: string]: string[] };
|
||||
|
||||
flash(message: string): any;
|
||||
|
Loading…
Reference in New Issue
Block a user