from flask import render_template, request, redirect, url_for, flash from flask_login import login_required, current_user from flask_wtf import FlaskForm from wtforms import ( StringField, validators, SelectMultipleField, BooleanField, IntegerField, ) from app import parallel_limiter from app.config import ( EMAIL_DOMAIN, ALIAS_DOMAINS, MAX_NB_DIRECTORY, BOUNCE_PREFIX_FOR_REPLY_PHASE, ) from app.dashboard.base import dashboard_bp from app.db import Session from app.errors import DirectoryInTrashError from app.models import Directory, Mailbox, DirectoryMailbox class NewDirForm(FlaskForm): name = StringField( "name", validators=[validators.DataRequired(), validators.Length(min=3)] ) class ToggleDirForm(FlaskForm): directory_id = IntegerField(validators=[validators.DataRequired()]) directory_enabled = BooleanField(validators=[]) class UpdateDirForm(FlaskForm): directory_id = IntegerField(validators=[validators.DataRequired()]) mailbox_ids = SelectMultipleField( validators=[validators.DataRequired()], validate_choice=False, choices=[] ) class DeleteDirForm(FlaskForm): directory_id = IntegerField(validators=[validators.DataRequired()]) @dashboard_bp.route("/directory", methods=["GET", "POST"]) @login_required @parallel_limiter.lock(only_when=lambda: request.method == "POST") def directory(): dirs = ( Directory.filter_by(user_id=current_user.id) .order_by(Directory.created_at.desc()) .all() ) mailboxes = current_user.mailboxes() new_dir_form = NewDirForm() toggle_dir_form = ToggleDirForm() update_dir_form = UpdateDirForm() update_dir_form.mailbox_ids.choices = [ (str(mailbox.id), str(mailbox.id)) for mailbox in mailboxes ] delete_dir_form = DeleteDirForm() if request.method == "POST": if request.form.get("form-name") == "delete": if not delete_dir_form.validate(): flash(f"Invalid request", "warning") return redirect(url_for("dashboard.directory")) dir_obj = Directory.get(delete_dir_form.directory_id.data) if not dir_obj: flash("Unknown error. Refresh the page", "warning") return redirect(url_for("dashboard.directory")) elif dir_obj.user_id != current_user.id: flash("You cannot delete this directory", "warning") return redirect(url_for("dashboard.directory")) name = dir_obj.name Directory.delete(dir_obj.id) Session.commit() flash(f"Directory {name} has been deleted", "success") return redirect(url_for("dashboard.directory")) if request.form.get("form-name") == "toggle-directory": if not toggle_dir_form.validate(): flash(f"Invalid request", "warning") return redirect(url_for("dashboard.directory")) dir_id = toggle_dir_form.directory_id.data dir_obj = Directory.get(dir_id) if not dir_obj or dir_obj.user_id != current_user.id: flash("Unknown error. Refresh the page", "warning") return redirect(url_for("dashboard.directory")) if toggle_dir_form.directory_enabled.data: dir_obj.disabled = False flash(f"On-the-fly is enabled for {dir_obj.name}", "success") else: dir_obj.disabled = True flash(f"On-the-fly is disabled for {dir_obj.name}", "warning") Session.commit() return redirect(url_for("dashboard.directory")) elif request.form.get("form-name") == "update": if not update_dir_form.validate(): flash(f"Invalid request", "warning") return redirect(url_for("dashboard.directory")) dir_id = update_dir_form.directory_id.data dir_obj = Directory.get(dir_id) if not dir_obj or dir_obj.user_id != current_user.id: flash("Unknown error. Refresh the page", "warning") return redirect(url_for("dashboard.directory")) mailbox_ids = update_dir_form.mailbox_ids.data # check if mailbox is not tempered with mailboxes = [] for mailbox_id in mailbox_ids: mailbox = Mailbox.get(mailbox_id) if ( not mailbox or mailbox.user_id != current_user.id or not mailbox.verified ): flash("Something went wrong, please retry", "warning") return redirect(url_for("dashboard.directory")) mailboxes.append(mailbox) if not mailboxes: flash("You must select at least 1 mailbox", "warning") return redirect(url_for("dashboard.directory")) # first remove all existing directory-mailboxes links DirectoryMailbox.filter_by(directory_id=dir_obj.id).delete() Session.flush() for mailbox in mailboxes: DirectoryMailbox.create(directory_id=dir_obj.id, mailbox_id=mailbox.id) Session.commit() flash(f"Directory {dir_obj.name} has been updated", "success") return redirect(url_for("dashboard.directory")) elif request.form.get("form-name") == "create": if not current_user.is_premium(): flash("Only premium plan can add directory", "warning") return redirect(url_for("dashboard.directory")) if current_user.directory_quota <= 0: flash( f"You cannot have more than {MAX_NB_DIRECTORY} directories", "warning", ) return redirect(url_for("dashboard.directory")) if new_dir_form.validate(): new_dir_name = new_dir_form.name.data.lower() if Directory.get_by(name=new_dir_name): flash(f"{new_dir_name} already used", "warning") elif new_dir_name in ( "reply", "ra", "bounces", "bounce", "transactional", BOUNCE_PREFIX_FOR_REPLY_PHASE, ): flash( "this directory name is reserved, please choose another name", "warning", ) else: try: new_dir = Directory.create( name=new_dir_name, user_id=current_user.id ) except DirectoryInTrashError: flash( f"{new_dir_name} has been used before and cannot be reused", "error", ) else: Session.commit() mailbox_ids = request.form.getlist("mailbox_ids") if mailbox_ids: # check if mailbox is not tempered with mailboxes = [] for mailbox_id in mailbox_ids: mailbox = Mailbox.get(mailbox_id) if ( not mailbox or mailbox.user_id != current_user.id or not mailbox.verified ): flash( "Something went wrong, please retry", "warning" ) return redirect(url_for("dashboard.directory")) mailboxes.append(mailbox) for mailbox in mailboxes: DirectoryMailbox.create( directory_id=new_dir.id, mailbox_id=mailbox.id ) Session.commit() flash(f"Directory {new_dir.name} is created", "success") return redirect(url_for("dashboard.directory")) return render_template( "dashboard/directory.html", dirs=dirs, toggle_dir_form=toggle_dir_form, update_dir_form=update_dir_form, delete_dir_form=delete_dir_form, new_dir_form=new_dir_form, mailboxes=mailboxes, EMAIL_DOMAIN=EMAIL_DOMAIN, ALIAS_DOMAINS=ALIAS_DOMAINS, )