190 lines
8.0 KiB
TypeScript
190 lines
8.0 KiB
TypeScript
import Controller from "wms-core/Controller";
|
|
import {RequireAuthMiddleware} from "wms-core/auth/AuthComponent";
|
|
import {Request, Response} from "express";
|
|
import {ADD_RECOVERY_EMAIL_MAIL_TEMPLATE} from "../Mails";
|
|
import Validator, {EMAIL_REGEX, InvalidFormatValidationError, ValidationBag} from "wms-core/db/Validator";
|
|
import MagicLinkController from "./MagicLinkController";
|
|
import {MagicLinkActionType} from "./MagicLinkActionType";
|
|
import UserEmail from "wms-core/auth/models/UserEmail";
|
|
import {BadRequestError, ForbiddenHttpError, NotFoundHttpError, ServerError} from "wms-core/HttpError";
|
|
import MailDomain from "../models/MailDomain";
|
|
import UserMailIdentityComponent from "../models/UserMailIdentityComponent";
|
|
import MailIdentity from "../models/MailIdentity";
|
|
import UserNameComponent from "../models/UserNameComponent";
|
|
import {WhereOperator, WhereTest} from "wms-core/db/ModelQuery";
|
|
|
|
export default class AccountController extends Controller {
|
|
public getRoutesPrefix(): string {
|
|
return '/account';
|
|
}
|
|
|
|
public routes(): void {
|
|
this.get('/', this.getAccount, 'account', RequireAuthMiddleware);
|
|
this.post('/add-recovery-email', this.addRecoveryEmail, 'add-recovery-email', RequireAuthMiddleware);
|
|
this.post('/set-main-email', this.postSetMainRecoveryEmail, 'set-main-recovery-email', RequireAuthMiddleware);
|
|
this.post('/remove-email', this.postRemoveRecoveryEmail, 'remove-recovery-email', RequireAuthMiddleware);
|
|
|
|
this.post('/create-mail-identity', this.postCreateMailIdentity, 'create-mail-identity', RequireAuthMiddleware);
|
|
this.post('/delete-mail-identity', this.postDeleteMailIdentity, 'delete-mail-identity', RequireAuthMiddleware);
|
|
}
|
|
|
|
protected async getAccount(req: Request, res: Response): Promise<void> {
|
|
const user = req.as(RequireAuthMiddleware).getUser();
|
|
const userMailIdentity = user.as(UserMailIdentityComponent);
|
|
|
|
res.render('account', {
|
|
emails: await user.emails.get(),
|
|
mailboxIdentity: await (await userMailIdentity.mainMailIdentity.get())?.toEmail(),
|
|
identities: await Promise.all((await userMailIdentity.mailIdentities.get()).map(async identity => ({
|
|
id: identity.id,
|
|
email: await identity.toEmail(),
|
|
}))),
|
|
domains: (await MailDomain.select()
|
|
.where('user_id', user.id)
|
|
.where('user_id', null, WhereTest.EQ, WhereOperator.OR)
|
|
.sortBy('user_id', 'DESC')
|
|
.get())
|
|
.map(d => ({
|
|
value: d.id,
|
|
display: d.name,
|
|
})),
|
|
});
|
|
}
|
|
|
|
protected async addRecoveryEmail(req: Request, res: Response): Promise<void> {
|
|
await this.validate({
|
|
email: new Validator().defined().regexp(EMAIL_REGEX),
|
|
}, req.body);
|
|
|
|
const email = req.body.email;
|
|
|
|
// Existing email
|
|
if (await UserEmail.select().where('email', email).first()) {
|
|
const bag = new ValidationBag();
|
|
const error = new InvalidFormatValidationError('You already have this email.');
|
|
error.thingName = 'email';
|
|
bag.addMessage(error);
|
|
throw bag;
|
|
}
|
|
|
|
if (!req.sessionID) throw new ServerError('Session not initialized.');
|
|
|
|
await MagicLinkController.sendMagicLink(
|
|
this.getApp(),
|
|
req.sessionID,
|
|
MagicLinkActionType.ADD_RECOVERY_EMAIL,
|
|
Controller.route('account'),
|
|
email,
|
|
ADD_RECOVERY_EMAIL_MAIL_TEMPLATE,
|
|
{},
|
|
);
|
|
|
|
res.redirect(Controller.route('magic_link_lobby', undefined, {
|
|
redirect_uri: Controller.route('account'),
|
|
}));
|
|
}
|
|
|
|
protected async postSetMainRecoveryEmail(req: Request, res: Response): Promise<void> {
|
|
if (!req.body.id)
|
|
throw new BadRequestError('Missing id field', 'Check form parameters.', req.url);
|
|
|
|
const user = req.as(RequireAuthMiddleware).getUser();
|
|
|
|
const userEmail = await UserEmail.getById(req.body.id);
|
|
if (!userEmail)
|
|
throw new NotFoundHttpError('email', req.url);
|
|
if (userEmail.user_id !== user.id)
|
|
throw new ForbiddenHttpError('email', req.url);
|
|
if (userEmail.id === user.main_email_id)
|
|
throw new BadRequestError('This address is already your main address', 'Try refreshing the account page.', req.url);
|
|
|
|
user.main_email_id = userEmail.id;
|
|
await user.save();
|
|
|
|
req.flash('success', 'This email was successfully set as your main address.');
|
|
res.redirectBack();
|
|
}
|
|
|
|
protected async postRemoveRecoveryEmail(req: Request, res: Response): Promise<void> {
|
|
if (!req.body.id)
|
|
throw new BadRequestError('Missing id field', 'Check form parameters.', req.url);
|
|
|
|
const user = req.as(RequireAuthMiddleware).getUser();
|
|
|
|
const userEmail = await UserEmail.getById(req.body.id);
|
|
if (!userEmail)
|
|
throw new NotFoundHttpError('email', req.url);
|
|
if (userEmail.user_id !== user.id)
|
|
throw new ForbiddenHttpError('email', req.url);
|
|
if (userEmail.id === user.main_email_id)
|
|
throw new BadRequestError('Cannot remove main email address', 'Try refreshing the account page.', req.url);
|
|
|
|
await userEmail.delete();
|
|
|
|
req.flash('success', 'This email was successfully removed from your account.');
|
|
res.redirectBack();
|
|
}
|
|
|
|
protected async postCreateMailIdentity(req: Request, res: Response): Promise<void> {
|
|
const domain = await MailDomain.getById(req.body.mail_domain_id);
|
|
if (!domain) throw new NotFoundHttpError('domain', req.url);
|
|
|
|
const user = req.as(RequireAuthMiddleware).getUser();
|
|
const mailIdentityComponent = user.as(UserMailIdentityComponent);
|
|
|
|
const identity = MailIdentity.create({
|
|
user_id: user.id,
|
|
name: req.body.name,
|
|
mail_domain_id: req.body.mail_domain_id,
|
|
});
|
|
|
|
// Check whether this identity can be created by this user
|
|
if (domain.isPublic()) {
|
|
await this.validate({
|
|
name: new Validator<string>().defined().equals(user.as(UserNameComponent).name),
|
|
}, req.body);
|
|
const actualPublicAddressesCount = await mailIdentityComponent.getPublicAddressesCount();
|
|
const maxPublicAddressesCount = mailIdentityComponent.getMaxPublicAddressesCount();
|
|
if (actualPublicAddressesCount >= maxPublicAddressesCount) {
|
|
req.flash('error', 'You have reached maximum public email addresses.');
|
|
res.redirectBack();
|
|
return;
|
|
}
|
|
} else {
|
|
if (!domain.canCreateAddresses(user)) {
|
|
throw new ForbiddenHttpError('domain', req.url);
|
|
}
|
|
}
|
|
|
|
// Save identity
|
|
await identity.save();
|
|
|
|
// Set main mail identity if not already set
|
|
if (!mailIdentityComponent.main_mail_identity_id) {
|
|
mailIdentityComponent.main_mail_identity_id = identity.id;
|
|
await user.save();
|
|
req.flash('info', 'Congratulations! You just created your mailbox.');
|
|
}
|
|
|
|
req.flash('success', 'Mail identity ' + await identity.toEmail() + ' successfully created.');
|
|
res.redirectBack();
|
|
}
|
|
|
|
protected async postDeleteMailIdentity(req: Request, res: Response): Promise<void> {
|
|
const user = req.as(RequireAuthMiddleware).getUser();
|
|
|
|
const identity = await MailIdentity.getById(req.body.id);
|
|
if (!identity) throw new NotFoundHttpError('Mail identity', req.url);
|
|
if (identity.user_id !== user.id) throw new ForbiddenHttpError('mail identity', req.url);
|
|
if (user.as(UserMailIdentityComponent).main_mail_identity_id === identity.id) {
|
|
req.flash('error', 'Cannot delete your mailbox identity.');
|
|
res.redirectBack();
|
|
return;
|
|
}
|
|
|
|
await identity.delete();
|
|
req.flash('success', 'Identity ' + await identity.toEmail() + ' successfully deleted.');
|
|
res.redirectBack();
|
|
}
|
|
}
|