mirror of
https://github.com/simple-login/app.git
synced 2024-09-27 20:31:30 +02:00
add custom_domain view
This commit is contained in:
parent
5d9420a763
commit
a827b27215
@ -7,4 +7,5 @@ from .views import (
|
||||
alias_log,
|
||||
unsubscribe,
|
||||
api_key,
|
||||
custom_domain,
|
||||
)
|
||||
|
127
app/dashboard/templates/dashboard/custom_domain.html
Normal file
127
app/dashboard/templates/dashboard/custom_domain.html
Normal 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 %}
|
88
app/dashboard/views/custom_domain.py
Normal file
88
app/dashboard/views/custom_domain.py
Normal 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,
|
||||
)
|
@ -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
13
app/dns_utils.py
Normal 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
|
@ -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}>"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user