From 8a531f6c86abdd3cf531bdf1d3b55ca3cd09c8fe Mon Sep 17 00:00:00 2001 From: Son NK Date: Mon, 10 Feb 2020 23:17:05 +0700 Subject: [PATCH] User can add/delete/verify mailbox --- app/dashboard/__init__.py | 1 + .../templates/dashboard/mailbox.html | 111 +++++++++++++++ app/dashboard/views/mailbox.py | 129 ++++++++++++++++++ .../emails/transactional/verify-mailbox.html | 9 ++ .../emails/transactional/verify-mailbox.txt | 10 ++ templates/menu.html | 8 ++ 6 files changed, 268 insertions(+) create mode 100644 app/dashboard/templates/dashboard/mailbox.html create mode 100644 app/dashboard/views/mailbox.py create mode 100644 templates/emails/transactional/verify-mailbox.html create mode 100644 templates/emails/transactional/verify-mailbox.txt diff --git a/app/dashboard/__init__.py b/app/dashboard/__init__.py index 4cc25e80..1a684cc4 100644 --- a/app/dashboard/__init__.py +++ b/app/dashboard/__init__.py @@ -14,4 +14,5 @@ from .views import ( domain_detail, lifetime_licence, directory, + mailbox, ) diff --git a/app/dashboard/templates/dashboard/mailbox.html b/app/dashboard/templates/dashboard/mailbox.html new file mode 100644 index 00000000..bfa07221 --- /dev/null +++ b/app/dashboard/templates/dashboard/mailbox.html @@ -0,0 +1,111 @@ +{% extends 'default.html' %} +{% set active_page = "mailbox" %} + +{% block title %} + Mailboxes +{% endblock %} + +{% block default_content %} +
+
+

Mailboxes

+ + {% if not current_user.is_premium() %} + + {% endif %} + + + + {% for mailbox in mailboxes %} +
+
+
+ {{ mailbox.email }} + {% if mailbox.verified %} + + {% else %} + + 🚫 + + {% endif %} + +
+
+ Created {{ mailbox.created_at | dt }}
+ {{ mailbox.nb_alias() }} aliases. +
+
+ + + +
+ {% endfor %} + + {% if mailboxs|length > 0 %} +
+ {% endif %} + +
+ {{ new_mailbox_form.csrf_token }} + + +
Email
+
+ A verification email will be sent to this email to make sure you have access to this email. +
+ + {{ new_mailbox_form.email(class="form-control", placeholder="email@example.com", + autofocus="1") }} + {{ render_field_errors(new_mailbox_form.email) }} + +
+ +
+ +
+{% endblock %} + +{% block script %} + +{% endblock %} \ No newline at end of file diff --git a/app/dashboard/views/mailbox.py b/app/dashboard/views/mailbox.py new file mode 100644 index 00000000..8b95e42e --- /dev/null +++ b/app/dashboard/views/mailbox.py @@ -0,0 +1,129 @@ +from flask import render_template, request, redirect, url_for, flash +from flask_login import login_required, current_user +from flask_wtf import FlaskForm +from itsdangerous import Signer, BadSignature +from wtforms import validators +from wtforms.fields.html5 import EmailField + +from app.config import EMAIL_DOMAIN, ALIAS_DOMAINS, FLASK_SECRET, URL +from app.dashboard.base import dashboard_bp +from app.email_utils import ( + send_email, + render, + can_be_used_as_personal_email, + email_already_used, +) +from app.extensions import db +from app.log import LOG +from app.models import Mailbox + + +class NewMailboxForm(FlaskForm): + email = EmailField( + "email", validators=[validators.DataRequired(), validators.Email()] + ) + + +@dashboard_bp.route("/mailbox", methods=["GET", "POST"]) +@login_required +def mailbox_route(): + mailboxes = Mailbox.query.filter_by(user_id=current_user.id).all() + + new_mailbox_form = NewMailboxForm() + + if request.method == "POST": + if request.form.get("form-name") == "delete": + mailbox_id = request.form.get("mailbox-id") + mailbox = Mailbox.get(mailbox_id) + + if not mailbox or mailbox.user_id != current_user.id: + flash("Unknown error. Refresh the page", "warning") + return redirect(url_for("dashboard.mailbox_route")) + + email = mailbox.email + Mailbox.delete(mailbox_id) + db.session.commit() + flash(f"Mailbox {email} has been deleted", "success") + + return redirect(url_for("dashboard.mailbox_route")) + + elif request.form.get("form-name") == "create": + if not current_user.is_premium(): + flash("Only premium plan can add additional mailbox", "warning") + return redirect(url_for("dashboard.mailbox_route")) + + if new_mailbox_form.validate(): + mailbox_email = new_mailbox_form.email.data.lower() + + if email_already_used(mailbox_email): + flash(f"{mailbox_email} already used", "error") + elif not can_be_used_as_personal_email(mailbox_email): + flash( + f"You cannot use {mailbox_email}.", "error", + ) + else: + new_mailbox = Mailbox.create( + email=mailbox_email, user_id=current_user.id + ) + db.session.commit() + + s = Signer(FLASK_SECRET) + mailbox_id_signed = s.sign(str(new_mailbox.id)).decode() + verification_url = ( + URL + + "/dashboard/mailbox_verify" + + f"?mailbox_id={mailbox_id_signed}" + ) + send_email( + current_user.email, + f"Please confirm your email {mailbox_email}", + render( + "transactional/verify-mailbox.txt", + user=current_user, + link=verification_url, + mailbox_email=mailbox_email, + ), + render( + "transactional/verify-mailbox.html", + user=current_user, + link=verification_url, + mailbox_email=mailbox_email, + ), + ) + + flash( + f"You are going to receive an email to confirm {mailbox_email}.", + "success", + ) + + return redirect(url_for("dashboard.mailbox_route")) + + return render_template( + "dashboard/mailbox.html", + mailboxes=mailboxes, + new_mailbox_form=new_mailbox_form, + EMAIL_DOMAIN=EMAIL_DOMAIN, + ALIAS_DOMAINS=ALIAS_DOMAINS, + ) + + +@dashboard_bp.route("/mailbox_verify") +def mailbox_verify(): + s = Signer(FLASK_SECRET) + mailbox_id = request.args.get("mailbox_id") + + try: + r_id = int(s.unsign(mailbox_id)) + except BadSignature: + flash("Invalid link. Please delete and re-add your mailbox", "error") + else: + mailbox = Mailbox.get(r_id) + mailbox.verified = True + db.session.commit() + + LOG.d("Mailbox %s is verified", mailbox) + flash( + f"The {mailbox.email} is now verified, you can start creating alias with it", + "success", + ) + return redirect(url_for("dashboard.mailbox_route")) diff --git a/templates/emails/transactional/verify-mailbox.html b/templates/emails/transactional/verify-mailbox.html new file mode 100644 index 00000000..e774b1d4 --- /dev/null +++ b/templates/emails/transactional/verify-mailbox.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} + +{% block content %} + {{ render_text("Hi " + user.name) }} + {{ render_text("You have added "+ mailbox_email +" as an additional mailbox.") }} + {{ render_text("To confirm, please click on the button below or use this link:
" + link) }} + {{ render_button("Confirm email", link) }} +{% endblock %} + diff --git a/templates/emails/transactional/verify-mailbox.txt b/templates/emails/transactional/verify-mailbox.txt new file mode 100644 index 00000000..6a804029 --- /dev/null +++ b/templates/emails/transactional/verify-mailbox.txt @@ -0,0 +1,10 @@ +Hi {{user.name}} + +You have added {{mailbox_email}} as an additional mailbox. + +To confirm, please click on this link: + +{{link}} + +Regards, +Son - SimpleLogin founder. diff --git a/templates/menu.html b/templates/menu.html index 03de7d78..7eefa451 100644 --- a/templates/menu.html +++ b/templates/menu.html @@ -39,6 +39,14 @@ + +