add custom_domain view

This commit is contained in:
Son NK 2019-12-02 00:13:39 +00:00
parent 5d9420a763
commit a827b27215
7 changed files with 234 additions and 1 deletions

View File

@ -7,4 +7,5 @@ from .views import (
alias_log,
unsubscribe,
api_key,
custom_domain,
)

View File

@ -0,0 +1,127 @@
{% extends 'default.html' %}
{% block title %}
Custom Domains
{% endblock %}
{% block head %}
{% endblock %}
{% block default_content %}
<div class="row">
<div class="col-md-8 offset-md-2">
<h1 class="h3"> Custom Domains </h1>
{% for custom_domain in custom_domains %}
<div class="card" style="max-width: 50rem">
<div class="card-body">
<h5 class="card-title">
{{ custom_domain.domain }}
{% if custom_domain.verified %}
<i class="fe fe-check" style="color: green"></i>
{% endif %}
</h5>
{% if not custom_domain.verified %}
<hr>
Please follow the following steps to set up your domain: <br>
<div>
<span class="badge badge-primary badge-pill mr-2">1</span>
Add the following MX DNS record to your domain
</div>
<div class="ml-6 mt-3">
{% for priority, email_server in EMAIL_SERVERS_WITH_PRIORITY %}
<div class="ml-3 text-info">
Domain: <em>{{ custom_domain.domain }}</em> <br>
Priority: 10 <br>
Target: <em>{{ email_server }}</em> <br>
</div>
{% endfor %}
Or if you edit your DNS record in text format, use the following code: <br>
<pre class="ml-3">
{% for priority, email_server in EMAIL_SERVERS_WITH_PRIORITY %}
{{ custom_domain.domain }} IN MX {{ priority }} {{ email_server }}
{% endfor %}
</pre>
</div>
<div>
<span class="badge badge-primary badge-pill mr-2">2</span>
Verify 👇🏽
</div>
<div class="ml-6 mt-3">
<form method="post">
<input type="hidden" name="form-name" value="check-domain">
<input type="hidden" name="custom-domain-id" value="{{ custom_domain.id }}">
<button type="submit" class="btn btn-primary">Verify</button>
</form>
{% if custom_domain.id in errors %}
<div class="text-danger">
{{ errors.get(custom_domain.id) }}
</div>
{% endif %}
As the change could take up to 24 hours, do not hesitate to come back to this page and verify again.
</div>
{% endif %}
</div>
<div class="card-footer">
<div class="row">
<div class="col">
<form method="post">
<input type="hidden" name="form-name" value="delete">
<input type="hidden" name="custom-domain-id" value="{{ custom_domain.id }}">
<span class="card-link btn btn-link float-right delete-custom-domain">
Delete
</span>
</form>
</div>
</div>
</div>
</div>
{% endfor %}
<hr>
<form method="post">
{{ new_custom_domain_form.csrf_token }}
<input type="hidden" name="form-name" value="create">
<label class="form-label">Domain</label>
<small>Please use full path domain, for ex <em>my-subdomain.my-domain.com</em></small>
{{ new_custom_domain_form.domain(class="form-control", placeholder="my-domain.com") }}
{{ render_field_errors(new_custom_domain_form.domain) }}
<button class="btn btn-lg btn-success mt-2">Create</button>
</form>
</div>
</div>
{% endblock %}
{% block script %}
<script>
$(".delete-custom-domain").on("click", function (e) {
notie.confirm({
text: "All aliases associated with this domain will be also deleted, " +
" please confirm",
cancelCallback: () => {
// nothing to do
},
submitCallback: () => {
$(this).closest("form").submit();
}
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,88 @@
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
from app.config import EMAIL_SERVERS_WITH_PRIORITY, EMAIL_SERVERS
from app.dashboard.base import dashboard_bp
from app.dns_utils import get_mx_domains
from app.extensions import db
from app.models import CustomDomain
# todo: add more validation
class NewCustomDomainForm(FlaskForm):
domain = StringField("domain", validators=[validators.DataRequired()])
@dashboard_bp.route("/custom_domain", methods=["GET", "POST"])
@login_required
def custom_domain():
custom_domains = CustomDomain.query.filter_by(user_id=current_user.id).all()
new_custom_domain_form = NewCustomDomainForm()
errors = {}
if request.method == "POST":
if request.form.get("form-name") == "delete":
custom_domain_id = request.form.get("custom-domain-id")
custom_domain = CustomDomain.get(custom_domain_id)
if not custom_domain:
flash("Unknown error. Refresh the page", "warning")
return redirect(url_for("dashboard.custom_domain"))
elif custom_domain.user_id != current_user.id:
flash("You cannot delete this domain", "warning")
return redirect(url_for("dashboard.custom_domain"))
name = custom_domain.domain
CustomDomain.delete(custom_domain_id)
db.session.commit()
flash(f"Domain {name} has been deleted successfully", "success")
return redirect(url_for("dashboard.custom_domain"))
elif request.form.get("form-name") == "create":
if new_custom_domain_form.validate():
new_custom_domain = CustomDomain.create(
domain=new_custom_domain_form.domain.data, user_id=current_user.id
)
db.session.commit()
flash(
f"New domain {new_custom_domain.domain} has been created successfully",
"success",
)
return redirect(url_for("dashboard.custom_domain"))
elif request.form.get("form-name") == "check-domain":
custom_domain_id = request.form.get("custom-domain-id")
custom_domain = CustomDomain.get(custom_domain_id)
if not custom_domain:
flash("Unknown error. Refresh the page", "warning")
return redirect(url_for("dashboard.custom_domain"))
elif custom_domain.user_id != current_user.id:
flash("You cannot delete this domain", "warning")
return redirect(url_for("dashboard.custom_domain"))
else:
mx_domains = get_mx_domains(custom_domain.domain)
if mx_domains != EMAIL_SERVERS:
errors[
custom_domain.id
] = f"Your DNS is not correctly set. The MX record we obtain is {mx_domains}"
else:
flash(
"Your domain is verified. Now it can be used to create custom alias",
"success",
)
custom_domain.verified = True
db.session.commit()
return render_template(
"dashboard/custom_domain.html",
custom_domains=custom_domains,
new_custom_domain_form=new_custom_domain_form,
EMAIL_SERVERS_WITH_PRIORITY=EMAIL_SERVERS_WITH_PRIORITY,
errors=errors,
)

View File

@ -40,7 +40,7 @@
<hr>
<div class="form-group">
<div class="row">
<label class="form-label col">Authorized URIs</label>
<label class="form-label col">Authorized Redirect URIs</label>
<p class="col text-right">
<small class="text-muted">
<em>redirect_uri</em> must be <b>HTTPS</b> for security reason.

13
app/dns_utils.py Normal file
View File

@ -0,0 +1,13 @@
import dns.resolver
def get_mx_domains(hostname) -> [str]:
answers = dns.resolver.query(hostname, "MX")
ret = []
for a in answers:
record = a.to_text() # for ex '20 alt2.aspmx.l.google.com.'
r = record.split(" ")[1] # alt2.aspmx.l.google.com.
ret.append(r)
return ret

View File

@ -209,6 +209,9 @@ class User(db.Model, ModelMixin, UserMixin):
sub = Subscription.get_by(user_id=self.id)
return sub
def verified_custom_domains(self):
return CustomDomain.query.filter_by(user_id=self.id, verified=True).all()
def __repr__(self):
return f"<User {self.id} {self.name} {self.email}>"

View File

@ -99,6 +99,7 @@ def fake_data():
password="password",
activated=True,
is_admin=True,
can_use_custom_domain=True
)
db.session.commit()