{# Routing rules page — Settings › Routing. Combined list + create form: one template, one URL. Reorder controls are plain POST forms (no HTMX) per the brief; progressive enhancement can be layered on without changing the action URLs. Context: rules — list[RoutingRule], sorted order asc / created_at asc. channels — list[Channel], sorted by name; drives the create on fresh form. default_order — integer default when order input is blank. form — dict with keys source_pattern, priority_pattern, channel_id, order. Pre-populated on validation error. errors — dict[field -> str]; rendered beside each field. notice — one of "created" / "deleted" / "not-found". #} {% extends "settings/_base.html" %} {% from "_macros.html" import confirm_dialog %} {% block section_header %}

Routing rules

Match source and priority to a channel. Specific rules win over wildcards.

{% endblock %} {% block settings_content %} {# Flash messages. Denylist allowed values to stop attacker-injected query params from reaching the template. #} {% if notice == "created" %}
Rule created.
{% elif notice == "deleted" %}
Rule deleted.
{% elif notice == "not-found" %}
Rule not found.
{% endif %} {# -------------------------------------------------------------- Rule table (or empty state). --------------------------------------------------------------- #} {% if rules %}
{% for r in rules %} {% set is_first = loop.first %} {% set is_last = loop.last %} {% endfor %}
Order Source Priority Channel Actions
{{ loop.index }}
{{ r.source_pattern }} {{ r.priority_pattern }} {% set matched_channel = None %} {% for c in channels %} {% if c.id == r.channel_id %}{% set matched_channel = c %}{% endif %} {% endfor %} {% if matched_channel %} {{ matched_channel.name }} {% else %} (deleted) {% endif %}
{{ confirm_dialog( form_id="delete-routing-" ~ r.id, title="Delete routing rule?", message="This rule will stop matching new drops. Existing drops are unaffected.", confirm_text="Delete rule", danger=True, ) }}
{% else %}
No routing rules yet.

Add one below. Without any rules, drops fall back to the default channel.

{% endif %} {# -------------------------------------------------------------- Create form (inline) — or the "no channels" empty state. If no channels exist, the form can't be submitted usefully (the

Use * to match any source.

{% if errors.source_pattern %}

{{ errors.source_pattern }}

{% endif %}
{% if errors.priority_pattern %}

{{ errors.priority_pattern }}

{% endif %}
{% if errors.channel_id %}

{{ errors.channel_id }}

{% endif %}

Lower order = higher precedence. Default {{ default_order }}.

{% if errors.order %}

{{ errors.order }}

{% endif %}
{% else %}

Create a channel first.

Routing rules point at channels — you need at least one channel before you can build a rule.

+ New channel
{% endif %} {% endblock %}