Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ sh scripts/run-test.sh

You can also run tests using a local Postgres DB to speed things up. This can be done by

- creating an empty test DB and running the database migration by `dropdb test && createdb test && DB_URI=postgresql://localhost:5432/test alembic upgrade head`
- creating an empty test DB and running the database migration by `dropdb test && createdb test && DB_URI=postgresql://localhost:5432/test uv run alembic upgrade head`

- replacing the `DB_URI` in `test.env` file by `DB_URI=postgresql://localhost:5432/test`

Expand Down
5 changes: 5 additions & 0 deletions app/alias_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,9 +279,12 @@ def try_auto_create_via_domain(address: str) -> Optional[Alias]:
return None
custom_domain, rule = can_create

alias_name = None
if rule:
alias_note = f"Created by rule {rule.order} with regex {rule.regex}"
mailboxes = rule.mailboxes
if rule.display_name:
alias_name = rule.display_name
else:
alias_note = "Created by catchall option"
mailboxes = custom_domain.mailboxes
Expand All @@ -308,6 +311,8 @@ def try_auto_create_via_domain(address: str) -> Optional[Alias]:
LOG.d(
f"User {custom_domain.user} created alias {alias} via domain {custom_domain}"
)
if alias_name:
alias.name = alias_name
if not custom_domain.user.disable_automatic_alias_note:
alias.note = alias_note
Session.flush()
Expand Down
12 changes: 12 additions & 0 deletions app/dashboard/views/domain_detail.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,10 @@ class AutoCreateRuleForm(FlaskForm):
"regex", validators=[validators.DataRequired(), validators.Length(max=128)]
)

display_name = StringField(
"display name", validators=[validators.Optional(), validators.Length(max=128)]
)

order = IntegerField(
"order",
validators=[validators.DataRequired(), validators.NumberRange(min=0, max=100)],
Expand Down Expand Up @@ -433,10 +437,18 @@ def domain_detail_auto_create(custom_domain_id):
)
)

display_name = None
if new_auto_create_rule_form.display_name.data:
raw_display = new_auto_create_rule_form.display_name.data
display_name = (
raw_display.replace("\r", " ").replace("\n", " ").strip()
)

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,
display_name=display_name or None,
flush=True,
)

Expand Down
3 changes: 3 additions & 0 deletions app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2706,6 +2706,9 @@ class AutoCreateRule(Base, ModelMixin):
# the order in which rules are evaluated in case there are multiple rules
order = sa.Column(sa.Integer, default=0, nullable=False)

# optional display name applied to aliases created by this rule
display_name = sa.Column(sa.String(128), nullable=True, default=None)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't server_default needed too for the default value on the update clause in the migration?


custom_domain = orm.relationship(CustomDomain, backref="_auto_create_rules")

mailboxes = orm.relationship(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""Add display name to auto create rule
Revision ID: f3d65fe0b5b4
Revises: 3ffdea52697d
Create Date: 2025-11-13 15:42:00.000000
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = 'f3d65fe0b5b4'
down_revision = '3ffdea52697d'
branch_labels = None
depends_on = None


def upgrade():
op.add_column('auto_create_rule', sa.Column('display_name', sa.String(length=128), nullable=True))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here



def downgrade():
op.drop_column('auto_create_rule', 'display_name')
15 changes: 14 additions & 1 deletion templates/dashboard/domain_detail/auto-create.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ <h1 class="h2 mb-1">{{ custom_domain.domain }} auto create alias rules</h1>
<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 %}">
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 />
Expand All @@ -36,6 +36,12 @@ <h1 class="h2 mb-1">{{ custom_domain.domain }} auto create alias rules</h1>
Order: <b>{{ auto_create_rule.order }}</b>
<br />
<input readonly value="{{ auto_create_rule.regex }}" class="form-control">
{% if auto_create_rule.display_name %}

Display name:
<b>{{ auto_create_rule.display_name }}</b>
<br />
{% endif %}
New alias will belong to
{% for mailbox in auto_create_rule.mailboxes %}

Expand Down Expand Up @@ -81,6 +87,13 @@ <h3>New rule</h3>
<a href="https://regex101.com" target="_blank" rel="noopener noreferrer">https://regex101.com↗</a>
</div>
</div>
<div class="form-group">
<label>
Display name <span class="small">(optional)</span>
</label>
{{ new_auto_create_rule_form.display_name(class="form-control", placeholder="My Newsletter") }}
{{ render_field_errors(new_auto_create_rule_form.display_name) }}
</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") }}
Expand Down
32 changes: 32 additions & 0 deletions tests/test_alias_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
DeletedAlias,
CustomDomain,
AutoCreateRule,
AutoCreateRuleMailbox,
Directory,
DirectoryMailbox,
User,
Expand Down Expand Up @@ -135,6 +136,37 @@ def test_auto_create_alias(flask_client):
assert result is None, f"Case {test_id} - Failed address {address}"


def test_auto_create_alias_applies_rule_display_name(flask_client):
user = create_new_user()
custom_domain = CustomDomain.create(
user_id=user.id,
catch_all=False,
domain=random_domain(),
verified=True,
flush=True,
)
rule = AutoCreateRule.create(
custom_domain_id=custom_domain.id,
order=0,
regex="list-.*",
display_name="Weekly Digest",
flush=True,
)
AutoCreateRuleMailbox.create(
auto_create_rule_id=rule.id,
mailbox_id=user.default_mailbox_id,
flush=True,
)
Session.commit()

address = f"list-welcome@{custom_domain.domain}"
alias = try_auto_create(address)
assert alias is not None
alias = Alias.get_by(email=address)
assert alias is not None
assert alias.name == "Weekly Digest"


# get_alias_recipient_name
def test_get_alias_recipient_name_no_overrides():
user = create_new_user()
Expand Down
Loading