124 lines
4.5 KiB
TypeScript
124 lines
4.5 KiB
TypeScript
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";
|
|
import Mail from "../Mail";
|
|
import {ACCOUNT_REVIEW_NOTICE_MAIL_TEMPLATE} from "../Mails";
|
|
import UserEmail from "../auth/models/UserEmail";
|
|
|
|
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.get('/', this.getIndex, 'backend', REQUIRE_AUTH_MIDDLEWARE, REQUIRE_ADMIN_MIDDLEWARE);
|
|
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);
|
|
}
|
|
}
|
|
|
|
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: User.isApprovalMode() ? accounts : 0,
|
|
});
|
|
}
|
|
|
|
protected async postApproveAccount(req: Request, res: Response): Promise<void> {
|
|
const {account, email} = await this.accountRequest(req);
|
|
|
|
account.approved = true;
|
|
await account.save();
|
|
|
|
await new Mail(ACCOUNT_REVIEW_NOTICE_MAIL_TEMPLATE, {
|
|
approved: true,
|
|
link: config.get<string>('base_url') + Controller.route('auth'),
|
|
}).send(email!.email!);
|
|
|
|
req.flash('success', `Account successfully approved.`);
|
|
res.redirectBack(Controller.route('accounts-approval'));
|
|
}
|
|
|
|
protected async postRejectAccount(req: Request, res: Response): Promise<void> {
|
|
const {account, email} = await this.accountRequest(req);
|
|
|
|
await account.delete();
|
|
|
|
await new Mail(ACCOUNT_REVIEW_NOTICE_MAIL_TEMPLATE, {
|
|
approved: false,
|
|
}).send(email!.email!);
|
|
|
|
req.flash('success', `Account successfully deleted.`);
|
|
res.redirectBack(Controller.route('accounts-approval'));
|
|
}
|
|
|
|
protected async accountRequest(req: Request): Promise<{
|
|
account: User,
|
|
email: UserEmail,
|
|
}> {
|
|
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<string>;
|
|
|
|
/**
|
|
* The string part of the link display
|
|
*/
|
|
getDisplayString(): Promise<string>;
|
|
|
|
/**
|
|
* An optional feather icon name
|
|
*/
|
|
getDisplayIcon(): Promise<string | null>;
|
|
}
|