import config from "config"; import Controller from "../Controller"; import User from "../auth/models/User"; import {Request, Response} from "express"; import {BadRequestError, NotFoundHttpError} from "../HttpError"; import Mail from "../mail/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"; import NunjucksComponent from "../components/NunjucksComponent"; import ModelFactory from "../db/ModelFactory"; import UserNameComponent from "../auth/models/UserNameComponent"; export default class BackendController extends Controller { private static readonly menu: BackendMenuElement[] = []; public static registerMenuElement(element: BackendMenuElement): void { this.menu.push(element); } public constructor() { super(); if (User.isApprovalMode()) { BackendController.registerMenuElement({ getLink: async () => Controller.route('accounts-approval'), getDisplayString: async () => { const pendingUsersCount = (await User.select() .where('approved', false) .get()).length; return `Accounts approval (${pendingUsersCount})`; }, getDisplayIcon: async () => 'user-check', }); } } public getRoutesPrefix(): string { return '/backend'; } public routes(): void { this.useMiddleware(RequireAuthMiddleware, RequireAdminMiddleware); this.get('/', this.getIndex, 'backend'); if (User.isApprovalMode()) { this.get('/accounts-approval', this.getAccountApproval, 'accounts-approval'); this.post('/accounts-approval/approve', this.postApproveAccount, 'approve-account'); this.post('/accounts-approval/reject', this.postRejectAccount, 'reject-account'); } } protected async getIndex(req: Request, res: Response): Promise { res.render('backend/index', { menu: await Promise.all(BackendController.menu.map(async m => ({ link: await m.getLink(), display_string: await m.getDisplayString(), display_icon: await m.getDisplayIcon(), }))), }); } protected async getAccountApproval(req: Request, res: Response): Promise { const accounts = await User.select() .where('approved', 0) .with('mainEmail') .get(); res.render('backend/accounts_approval', { accounts: accounts, has_user_name_component: ModelFactory.get(User).hasComponent(UserNameComponent), }); } protected async postApproveAccount(req: Request, res: Response): Promise { const {account, email} = await this.accountRequest(req); account.as(UserApprovedComponent).approved = true; await account.save(); if (email && email.email) { await new Mail(this.getApp().as(NunjucksComponent).getEnvironment(), ACCOUNT_REVIEW_NOTICE_MAIL_TEMPLATE, { approved: true, link: config.get('public_url') + Controller.route('auth'), }).send(email.email); } req.flash('success', `Account successfully approved.`); res.redirect(Controller.route('accounts-approval')); } protected async postRejectAccount(req: Request, res: Response): Promise { const {account, email} = await this.accountRequest(req); await account.delete(); if (email && email.email) { await new Mail(this.getApp().as(NunjucksComponent).getEnvironment(), ACCOUNT_REVIEW_NOTICE_MAIL_TEMPLATE, { approved: false, }).send(email.email); } req.flash('success', `Account successfully deleted.`); res.redirect(Controller.route('accounts-approval')); } protected async accountRequest(req: Request): Promise<{ account: User, email: UserEmail | null, }> { if (!req.body.user_id) throw new BadRequestError('Missing user_id field', 'Check your form', req.url); const account = await User.select().where('id', req.body.user_id).with('mainEmail').first(); if (!account) throw new NotFoundHttpError('User', req.url); const email = await account.mainEmail.get(); return { account: account, email: email, }; } } export interface BackendMenuElement { /** * Returns the link of this menu element (usually using {@code Controller.route}) */ getLink(): Promise; /** * The string part of the link display */ getDisplayString(): Promise; /** * An optional feather icon name */ getDisplayIcon(): Promise; }