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 { 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 { 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( 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 { 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 { 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 { 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().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 { 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(); } }