create auto create page, remove custom domain auto_create_regex part
This commit is contained in:
parent
f36f8b94e2
commit
56c72d5fba
|
@ -0,0 +1,140 @@
|
|||
{% extends 'dashboard/domain_detail/base.html' %}
|
||||
|
||||
{% set domain_detail_page = "auto_create" %}
|
||||
|
||||
{% block title %}
|
||||
{{ custom_domain.domain }} Auto Create Rules
|
||||
{% endblock %}
|
||||
|
||||
{% block domain_detail_content %}
|
||||
<h1 class="h2 mb-1"> {{ custom_domain.domain }} auto create alias rules </h1>
|
||||
<div>
|
||||
<span class="badge badge-info">Advanced</span>
|
||||
<span class="badge badge-warning">Beta</span>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{% if custom_domain.catch_all %}
|
||||
<div class="alert alert-warning mt-3">
|
||||
Rules are ineffective when catch-all is enabled.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="{% if custom_domain.catch_all %} disabled-content {% endif %}">
|
||||
<div class="mt-3 mb-2">
|
||||
For a greater control than a simple catch-all, you can define a set of <b>rules</b> to auto create aliases. <br>
|
||||
A rule is based on a regular expression (<b>regex</b>): if an alias matches the expression, it'll be automatically
|
||||
created.
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
Only the local part of the alias (i.e. <b>@{{ custom_domain.domain }}</b> is ignored) during the
|
||||
regex test.
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
When there are several rules, rules will be evaluated by their order.
|
||||
</div>
|
||||
|
||||
{% if custom_domain.auto_create_rules | length > 0 %}
|
||||
<div class="mt-2">
|
||||
{% for auto_create_rule in custom_domain.auto_create_rules %}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
Order: <b>{{ auto_create_rule.order }}</b> <br>
|
||||
<input readonly value="{{ auto_create_rule.regex }}" class="form-control">
|
||||
New alias will belong to
|
||||
{% for mailbox in auto_create_rule.mailboxes %}
|
||||
<b>{{ mailbox.email }}</b>
|
||||
{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<form method="post" class="mt-2">
|
||||
<input type="hidden" name="form-name" value="delete-auto-create-rule">
|
||||
<input type="hidden" name="rule-id" value="{{ auto_create_rule.id }}">
|
||||
<button class="btn btn-outline-danger btn-sm float-right">Delete</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-2">
|
||||
<hr>
|
||||
<h3>New rule </h3>
|
||||
|
||||
<form method="post" data-parsley-validate>
|
||||
<input type="hidden" name="form-name" value="create-auto-create-rule">
|
||||
{{ new_auto_create_rule_form.csrf_token }}
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label>Regex</label>
|
||||
|
||||
{{ new_auto_create_rule_form.regex(class="form-control",
|
||||
placeholder="prefix\..*",
|
||||
data_parsley_pattern="[0-9a-z-_.(\\\)(\*)(\|)]{1,}",
|
||||
data_parsley_trigger="change",
|
||||
data_parsley_error_message="Only lowercase letter, number, dot (.), dash (-), underscore (_), backslash (\), star (*) are currently supported.") }}
|
||||
{{ render_field_errors(new_auto_create_rule_form.regex) }}
|
||||
|
||||
<div class="small-text">
|
||||
For example, if you want aliases that starts with <b>prefix.</b> to be automatically created, you can set
|
||||
the
|
||||
regex to <em data-toggle="tooltip"
|
||||
title="Click to copy"
|
||||
class="clipboard"
|
||||
data-clipboard-text="prefix\..*">prefix\..*</em>
|
||||
<br>
|
||||
|
||||
If you want aliases that ends with <b>.suffix</b> to be automatically created, you can use the regex
|
||||
<em data-toggle="tooltip"
|
||||
title="Click to copy"
|
||||
class="clipboard"
|
||||
data-clipboard-text=".*\.suffix">.*\.suffix</em>
|
||||
<br>
|
||||
To test out regex, we recommend using regex tester tool like
|
||||
<a href="https://regex101.com" target="_blank">https://regex101.com↗</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Order</label>
|
||||
{{ new_auto_create_rule_form.order(class="form-control", placeholder="10", min=1, value=1, type="number") }}
|
||||
{{ render_field_errors(new_auto_create_rule_form.order) }}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="flex-grow-1 mr-2">
|
||||
<select data-width="100%" required
|
||||
class="mailbox-select" multiple name="mailbox_ids">
|
||||
{% for mailbox in mailboxes %}
|
||||
<option value="{{ mailbox.id }}" {% if mailbox.id == current_user.default_mailbox_id %}
|
||||
selected {% endif %}>
|
||||
{{ mailbox.email }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary mt-2">Create</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script>
|
||||
$('.mailbox-select').multipleSelect();
|
||||
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
@ -20,6 +20,12 @@
|
|||
class="list-group-item list-group-item-action {{ 'active' if domain_detail_page == 'trash' }}">
|
||||
<span class="icon mr-3"><i class="fe fe-trash"></i></span>Deleted Alias
|
||||
</a>
|
||||
|
||||
<a href="{{ url_for('dashboard.domain_detail_auto_create', custom_domain_id=custom_domain.id) }}"
|
||||
class="list-group-item list-group-item-action {{ 'active' if domain_detail_page == 'auto_create' }}">
|
||||
<span class="icon mr-3"><i class="fe fe-layers"></i></span>Auto Create
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -41,68 +41,19 @@
|
|||
Simply use <b>anything@{{ custom_domain.domain }}</b>
|
||||
next time you need an alias: it'll be <b>automatically</b>
|
||||
created the first time it receives an email.
|
||||
To have more fine-grained control, you can also use the
|
||||
<a data-toggle="collapse" href="#regex-section">regular expression <i class="fe fe-chevrons-down"></i></a>.
|
||||
To have more fine-grained control, you can also define
|
||||
<a href="{{ url_for('dashboard.domain_detail_auto_create', custom_domain_id=custom_domain.id) }}">auto create
|
||||
rules
|
||||
<i class="fe fe-chevrons-right"></i></a>.
|
||||
</div>
|
||||
|
||||
<div class="{% if custom_domain.auto_create_regex is none %} collapse {% endif %}
|
||||
{% if custom_domain.catch_all %} disabled-content {% endif %}
|
||||
border border-info p-2"
|
||||
id="regex-section">
|
||||
<span class="badge badge-info">Advanced</span>
|
||||
<span class="badge badge-warning">Beta</span> <br>
|
||||
You can also set a regular expression (regex): if an alias matches the expression, it'll be automatically created.
|
||||
<br>
|
||||
Please note that only the local part of the alias (i.e. <b>@{{ custom_domain.domain }}</b> is ignored) during the
|
||||
regex
|
||||
test.
|
||||
|
||||
<form method="post" class="form-inline" data-parsley-validate>
|
||||
<input type="hidden" name="form-name" value="set-auto_create_regex">
|
||||
<div class="form-group">
|
||||
<input class="form-control mr-2"
|
||||
value="{{ custom_domain.auto_create_regex or "" }}"
|
||||
name="auto_create_regex"
|
||||
required
|
||||
data-parsley-pattern="[0-9a-z-_.(\\)(\*)(\|)]{1,}"
|
||||
data-parsley-trigger="change"
|
||||
data-parsley-error-message="Only lowercase letter, number, dot (.), dash (-), underscore (_), backslash (\), star (*) are currently supported."
|
||||
placeholder="prefix\..*">
|
||||
</div>
|
||||
<button class="btn btn-outline-primary" name="action" value="save">Save</button>
|
||||
{% if custom_domain.auto_create_regex %}
|
||||
<button class="btn btn-outline-danger float-right ml-2" name="action" value="remove">Remove</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
|
||||
For example, if you want aliases that starts with <b>prefix.</b> to be automatically created, you can set the
|
||||
regex to <em data-toggle="tooltip"
|
||||
title="Click to copy"
|
||||
class="clipboard"
|
||||
data-clipboard-text="prefix\..*">prefix\..*</em>
|
||||
<br>
|
||||
|
||||
If you want aliases that ends with <b>.suffix</b> to be automatically created, you can use the regex
|
||||
<em data-toggle="tooltip"
|
||||
title="Click to copy"
|
||||
class="clipboard"
|
||||
data-clipboard-text=".*\.suffix">.*\.suffix</em>
|
||||
<br>
|
||||
|
||||
To test out regex, we recommend using regex tester tool like <a href="https://regex101.com" target="_blank">https://regex101.com↗</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
The new alias will belong to
|
||||
{% for mailbox in custom_domain.mailboxes %}
|
||||
<b>{{ mailbox.email }}</b>
|
||||
{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="{% if not custom_domain.auto_create_alias_enabled %} disabled-content {% endif %}">
|
||||
<div>Auto-created aliases are automatically owned by these mailboxes</div>
|
||||
<div class="{% if not custom_domain.catch_all %} disabled-content {% endif %}">
|
||||
<div>Auto-created aliases are automatically owned by the following mailboxes
|
||||
<i class="fe fe-corner-right-down"></i></a>.
|
||||
</div>
|
||||
{% set domain_mailboxes=custom_domain.mailboxes %}
|
||||
<form method="post" class="mt-2">
|
||||
<input type="hidden" name="form-name" value="update">
|
||||
|
|
|
@ -2,6 +2,8 @@ from threading import Thread
|
|||
|
||||
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, IntegerField
|
||||
|
||||
from app.config import EMAIL_SERVERS_WITH_PRIORITY, EMAIL_DOMAIN
|
||||
from app.dashboard.base import dashboard_bp
|
||||
|
@ -14,7 +16,15 @@ from app.dns_utils import (
|
|||
from app.email_utils import send_email
|
||||
from app.extensions import db
|
||||
from app.log import LOG
|
||||
from app.models import CustomDomain, Alias, DomainDeletedAlias, Mailbox, DomainMailbox
|
||||
from app.models import (
|
||||
CustomDomain,
|
||||
Alias,
|
||||
DomainDeletedAlias,
|
||||
Mailbox,
|
||||
DomainMailbox,
|
||||
AutoCreateRule,
|
||||
AutoCreateRuleMailbox,
|
||||
)
|
||||
from app.utils import random_string
|
||||
|
||||
|
||||
|
@ -261,25 +271,6 @@ def domain_detail(custom_domain_id):
|
|||
return redirect(
|
||||
url_for("dashboard.domain_detail", custom_domain_id=custom_domain.id)
|
||||
)
|
||||
elif request.form.get("form-name") == "set-auto_create_regex":
|
||||
if request.form.get("action") == "save":
|
||||
auto_create_regex = request.form.get("auto_create_regex")
|
||||
if auto_create_regex:
|
||||
custom_domain.auto_create_regex = auto_create_regex
|
||||
db.session.commit()
|
||||
flash("The auto create regex has been updated", "success")
|
||||
else:
|
||||
flash("The auto create regex cannot be empty", "error")
|
||||
else:
|
||||
custom_domain.auto_create_regex = None
|
||||
db.session.commit()
|
||||
flash(
|
||||
f"The auto create regex has been has been removed",
|
||||
"info",
|
||||
)
|
||||
return redirect(
|
||||
url_for("dashboard.domain_detail", custom_domain_id=custom_domain.id)
|
||||
)
|
||||
|
||||
elif request.form.get("form-name") == "delete":
|
||||
name = custom_domain.domain
|
||||
|
@ -378,3 +369,120 @@ def domain_detail_trash(custom_domain_id):
|
|||
domain_deleted_aliases=domain_deleted_aliases,
|
||||
custom_domain=custom_domain,
|
||||
)
|
||||
|
||||
|
||||
class AutoCreateRuleForm(FlaskForm):
|
||||
regex = StringField(
|
||||
"regex", validators=[validators.DataRequired(), validators.Length(max=128)]
|
||||
)
|
||||
|
||||
order = IntegerField(
|
||||
"order",
|
||||
validators=[validators.DataRequired(), validators.NumberRange(min=0, max=100)],
|
||||
)
|
||||
|
||||
|
||||
@dashboard_bp.route(
|
||||
"/domains/<int:custom_domain_id>/auto-create", methods=["GET", "POST"]
|
||||
)
|
||||
@login_required
|
||||
def domain_detail_auto_create(custom_domain_id):
|
||||
custom_domain: CustomDomain = CustomDomain.get(custom_domain_id)
|
||||
mailboxes = current_user.mailboxes()
|
||||
new_auto_create_rule_form = AutoCreateRuleForm()
|
||||
|
||||
if not custom_domain or custom_domain.user_id != current_user.id:
|
||||
flash("You cannot see this page", "warning")
|
||||
return redirect(url_for("dashboard.index"))
|
||||
|
||||
if request.method == "POST":
|
||||
if request.form.get("form-name") == "create-auto-create-rule":
|
||||
if new_auto_create_rule_form.validate():
|
||||
# make sure order isn't used before
|
||||
for auto_create_rule in custom_domain.auto_create_rules:
|
||||
auto_create_rule: AutoCreateRule
|
||||
if auto_create_rule.order == int(
|
||||
new_auto_create_rule_form.order.data
|
||||
):
|
||||
flash(
|
||||
"Another rule with the same order already exists", "error"
|
||||
)
|
||||
break
|
||||
else:
|
||||
mailbox_ids = request.form.getlist("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.domain_detail_auto_create",
|
||||
custom_domain_id=custom_domain.id,
|
||||
)
|
||||
)
|
||||
mailboxes.append(mailbox)
|
||||
|
||||
if not mailboxes:
|
||||
flash("You must select at least 1 mailbox", "warning")
|
||||
return redirect(
|
||||
url_for(
|
||||
"dashboard.domain_detail_auto_create",
|
||||
custom_domain_id=custom_domain.id,
|
||||
)
|
||||
)
|
||||
|
||||
rule = AutoCreateRule.create(
|
||||
custom_domain_id=custom_domain.id,
|
||||
order=int(new_auto_create_rule_form.order.data),
|
||||
regex=new_auto_create_rule_form.regex.data,
|
||||
flush=True,
|
||||
)
|
||||
|
||||
for mailbox in mailboxes:
|
||||
AutoCreateRuleMailbox.create(
|
||||
auto_create_rule_id=rule.id, mailbox_id=mailbox.id
|
||||
)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
flash("New auto create rule has been created", "success")
|
||||
|
||||
return redirect(
|
||||
url_for(
|
||||
"dashboard.domain_detail_auto_create",
|
||||
custom_domain_id=custom_domain.id,
|
||||
)
|
||||
)
|
||||
elif request.form.get("form-name") == "delete-auto-create-rule":
|
||||
rule_id = request.form.get("rule-id")
|
||||
rule: AutoCreateRule = AutoCreateRule.get(int(rule_id))
|
||||
|
||||
if not rule or rule.custom_domain_id != custom_domain.id:
|
||||
flash("Something wrong, please retry", "error")
|
||||
return redirect(
|
||||
url_for(
|
||||
"dashboard.domain_detail_auto_create",
|
||||
custom_domain_id=custom_domain.id,
|
||||
)
|
||||
)
|
||||
|
||||
rule_order = rule.order
|
||||
AutoCreateRule.delete(rule_id)
|
||||
db.session.commit()
|
||||
flash(f"Rule #{rule_order} has been deleted", "success")
|
||||
|
||||
return redirect(
|
||||
url_for(
|
||||
"dashboard.domain_detail_auto_create", custom_domain_id=custom_domain.id
|
||||
)
|
||||
)
|
||||
|
||||
nb_alias = Alias.filter_by(custom_domain_id=custom_domain.id).count()
|
||||
|
||||
return render_template("dashboard/domain_detail/auto-create.html", **locals())
|
||||
|
|
|
@ -1844,10 +1844,6 @@ class CustomDomain(db.Model, ModelMixin):
|
|||
|
||||
user = db.relationship(User, foreign_keys=[user_id])
|
||||
|
||||
@property
|
||||
def auto_create_alias_enabled(self) -> bool:
|
||||
return self.catch_all or self.auto_create_regex is not None
|
||||
|
||||
@property
|
||||
def mailboxes(self):
|
||||
if self._mailboxes:
|
||||
|
|
|
@ -170,7 +170,3 @@ textarea.parsley-error {
|
|||
.domain_detail_content {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.domain_detail_content .parsley-errors-list {
|
||||
max-width: 20em;
|
||||
}
|
Loading…
Reference in New Issue