swaf/src/helpers/BackendController.ts

139 lines
4.9 KiB
TypeScript

import {Request, Response} from "express";
import {RequireAdminMiddleware, RequireAuthMiddleware} from "../auth/AuthComponent.js";
import User from "../auth/models/User.js";
import UserApprovedComponent from "../auth/models/UserApprovedComponent.js";
import UserEmail from "../auth/models/UserEmail.js";
import UserNameComponent from "../auth/models/UserNameComponent.js";
import {route} from "../common/Routing.js";
import MailComponent from "../components/MailComponent.js";
import Controller from "../Controller.js";
import ModelFactory from "../db/ModelFactory.js";
import {BadRequestError, NotFoundHttpError} from "../HttpError.js";
import Mail from "../mail/Mail.js";
import {ACCOUNT_REVIEW_NOTICE_MAIL_TEMPLATE} from "../Mails.js";
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 () => 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<void> {
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<void> {
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<void> {
const {account, email} = await this.accountRequest(req);
account.as(UserApprovedComponent).approved = true;
await account.save();
if (email && email.email) {
await this.getApp().as(MailComponent).sendMail(new Mail(ACCOUNT_REVIEW_NOTICE_MAIL_TEMPLATE, {
approved: true,
link: route('auth', {}, {}, true),
}), email.email);
}
req.flash('success', `Account successfully approved.`);
res.redirect(route('accounts-approval'));
}
protected async postRejectAccount(req: Request, res: Response): Promise<void> {
const {account, email} = await this.accountRequest(req);
await account.delete();
if (email && email.email) {
await this.getApp().as(MailComponent).sendMail(new Mail(ACCOUNT_REVIEW_NOTICE_MAIL_TEMPLATE, {
approved: false,
}), email.email);
}
req.flash('success', `Account successfully deleted.`);
res.redirect(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 route})
*/
getLink(): Promise<string>;
/**
* The string part of the link display
*/
getDisplayString(): Promise<string>;
/**
* An optional feather icon name
*/
getDisplayIcon(): Promise<string | null>;
}