[Web] Archive settings, domain and mailbox switches, archive log and API

This commit is contained in:
FreddleSpl0it committed 2026-11-07 00:45:11 +01:00
1 parent fb85a9eb5c
commit 1c24de19c3
25 files changed
+834 -12

No files matched your search

+2
View File
@@ -29,6 +29,7 @@
</ul>
</li>
<li role="presentation" class="nav-item"><button class="nav-link" data-bs-target="#tab-routing" aria-selected="false" aria-controls="tab-routing" role="tab" data-bs-toggle="tab">{{ lang.admin.routing }}</button></li>
<li role="presentation" class="nav-item"><button class="nav-link" data-bs-target="#tab-archive" aria-selected="false" aria-controls="tab-archive" role="tab" data-bs-toggle="tab">{{ lang.admin.archive }}</button></li>
<li role="presentation" class="nav-item"><button class="nav-link" data-bs-target="#tab-sys-mails" aria-selected="false" aria-controls="tab-sys-mails" role="tab" data-bs-toggle="tab">{{ lang.admin.sys_mails }}</button></li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-expanded="false">{{ lang.admin.rspamd_global_filters }}</a>
@@ -47,6 +48,7 @@
{% include 'admin/tab-config-oauth2.twig' %}
{% include 'admin/tab-config-rspamd.twig' %}
{% include 'admin/tab-routing.twig' %}
{% include 'admin/tab-archive.twig' %}
{% include 'admin/tab-config-dkim.twig' %}
{% include 'admin/tab-config-fwdhosts.twig' %}
{% include 'admin/tab-config-f2b.twig' %}
+168
View File
@@ -0,0 +1,168 @@
<div class="tab-pane fade" id="tab-archive" role="tabpanel" aria-labelledby="tab-archive">
<div class="card mb-4">
<div class="card-header d-flex fs-5">
<button class="btn d-md-none flex-grow-1 text-start" data-bs-target="#collapse-tab-archive" data-bs-toggle="collapse" aria-controls="collapse-tab-archive">
{{ lang.admin.archive }}
</button>
<span class="d-none d-md-block">{{ lang.admin.archive }}</span>
</div>
<div id="collapse-tab-archive" class="card-body collapse" data-bs-parent="#admin-content">
<div class="row">
<p class="offset-md-3 col-12 col-md-9 mb-4">{{ lang.admin.archive_info }}</p>
</div>
<form class="form-horizontal" role="form" data-id="archive" method="post" autocomplete="off">
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="archive_enabled">{{ lang.admin.archive_enabled }}:</label>
</div>
<div class="col-12 col-md-9 d-flex align-items-center">
<input type="hidden" name="enabled" value="0">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="archive_enabled" name="enabled" value="1"{% if archive_settings.enabled == 1 %} checked{% endif %}>
</div>
</div>
<div class="offset-md-3 col-12 col-md-9">
<small class="text-muted">{{ lang.admin.archive_enabled_info }}</small>
</div>
</div>
{% if archive_settings.enabled == 1 %}
{# Filled by js/site/archive.js: one line per problem; details in the archive log and the queue manager #}
<div class="row mb-2" id="archive_status">
<div class="offset-md-3 col-12 col-md-9 col-lg-7">
<div id="archive_state"><p class="text-muted mb-2">{{ lang.datatables.loadingRecords }}</p></div>
<small class="d-block">
<a href="/admin/dashboard" id="archive_log_link"><i class="bi bi-journal-text"></i> {{ lang.admin.archive_log }}</a>
<a href="/admin/queue" class="ms-3"><i class="bi bi-inboxes"></i> {{ lang.queue.queue_manager }}</a>
<a href="#" class="ms-3" id="archive_refresh"><i class="bi bi-arrow-clockwise"></i> {{ lang.admin.refresh }}</a>
</small>
</div>
</div>
{% endif %}
<legend class="fs-5 mt-4"><i class="bi bi-plug"></i> {{ lang.admin.archive_section_connection }}</legend><hr>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="archive_address">{{ lang.admin.archive_address }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-5">
<input type="email" class="form-control" id="archive_address" name="address" placeholder="journal@archive.example.com" value="{{ archive_settings.address }}">
</div>
<div class="offset-md-3 col-12 col-md-9">
<small class="text-muted">{{ lang.admin.archive_address_info }}</small>
</div>
</div>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="archive_host">{{ lang.admin.archive_host }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-5">
<div class="input-group">
<input type="text" class="form-control" id="archive_host" name="host" placeholder="archive.example.com" value="{{ archive_settings.host }}">
<span class="input-group-text">{{ lang.admin.archive_port }}</span>
<input type="number" class="form-control" id="archive_port" name="port" min="1" max="65535" style="max-width: 7rem" aria-label="{{ lang.admin.archive_port }}" value="{{ archive_settings.port }}">
</div>
</div>
<div class="offset-md-3 col-12 col-md-9">
<small class="text-muted">{{ lang.admin.archive_host_info }}</small>
</div>
</div>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="archive_tls">{{ lang.admin.archive_tls }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-5">
<select class="full-width-select form-control" id="archive_tls" name="tls">
{% for mode in ['encrypt', 'verify', 'may'] %}
<option value="{{ mode }}"{% if archive_settings.tls == mode %} selected{% endif %}>{{ attribute(lang.admin, 'archive_tls_' ~ mode) }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="archive_username">{{ lang.admin.archive_username }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-5">
<input type="text" class="form-control" id="archive_username" name="username" autocomplete="off" value="{{ archive_settings.username }}">
</div>
</div>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="archive_password">{{ lang.admin.archive_password }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-5">
<input type="password" class="form-control" id="archive_password" name="password" autocomplete="new-password"
placeholder="{% if archive_settings.has_password == 1 %}{{ lang.edit.unchanged_if_empty }}{% endif %}">
{% if archive_settings.has_password == 1 %}
<div class="form-check mt-1">
<input class="form-check-input" type="checkbox" id="archive_password_remove" name="password_remove" value="1">
<label class="form-check-label" for="archive_password_remove">{{ lang.admin.archive_password_remove }}</label>
</div>
{% endif %}
</div>
</div>
<legend class="fs-5 mt-4"><i class="bi bi-file-earmark-text"></i> {{ lang.admin.archive_section_headers }}</legend><hr>
<div class="row mb-2">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="archive_profile">{{ lang.admin.archive_profile }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-5">
<select class="full-width-select form-control" id="archive_profile" name="profile">
{% for profile in ['archivecow', 'envelope', 'none'] %}
<option value="{{ profile }}"{% if archive_settings.profile == profile %} selected{% endif %}>{{ attribute(lang.admin, 'archive_profile_' ~ profile) }}</option>
{% endfor %}
</select>
</div>
<div class="offset-md-3 col-12 col-md-9">
{% for profile in ['archivecow', 'envelope', 'none'] %}
<small class="text-muted archive-profile-info archive-profile-info-{{ profile }}{% if archive_settings.profile != profile %} d-none{% endif %}">{{ attribute(lang.admin, 'archive_profile_' ~ profile ~ '_info') }}</small>
{% endfor %}
</div>
</div>
{# Fields of the other profiles are hidden and disabled, so their stored values are kept #}
{% set archivecow = archive_settings.profile == 'archivecow' %}
{% set envelope = archive_settings.profile == 'envelope' %}
<div class="row mb-2 archive-profile archive-profile-archivecow{% if not archivecow %} d-none{% endif %}">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="archive_signing_secret">{{ lang.admin.archive_signing_secret }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-5">
<input type="password" class="form-control" id="archive_signing_secret" name="signing_secret" autocomplete="new-password"
placeholder="{% if archive_settings.has_signing_secret == 1 %}{{ lang.edit.unchanged_if_empty }}{% endif %}"{% if not archivecow %} disabled{% endif %}>
</div>
<div class="offset-md-3 col-12 col-md-9">
<small class="text-muted">{{ lang.admin.archive_signing_secret_info }}</small>
</div>
</div>
<div class="row mb-2 archive-profile archive-profile-envelope{% if not envelope %} d-none{% endif %}">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="archive_header_from">{{ lang.admin.archive_header_from }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-5">
<input type="text" class="form-control" id="archive_header_from" name="header_from" value="{{ archive_settings.header_from }}"{% if not envelope %} disabled{% endif %}>
</div>
</div>
<div class="row mb-2 archive-profile archive-profile-envelope{% if not envelope %} d-none{% endif %}">
<div class="col-md-3 d-flex align-items-center justify-content-md-end">
<label class="control-label" for="archive_header_rcpt">{{ lang.admin.archive_header_rcpt }}:</label>
</div>
<div class="col-12 col-md-9 col-lg-5">
<input type="text" class="form-control" id="archive_header_rcpt" name="header_rcpt" value="{{ archive_settings.header_rcpt }}"{% if not envelope %} disabled{% endif %}>
</div>
</div>
<hr class="mt-4">
<div class="row mb-2">
<div class="offset-md-3 col-12 col-md-9">
{% set archive_ready = archive_settings.address and (archive_settings.profile != 'archivecow' or archive_settings.has_signing_secret == 1) %}
<button type="button" class="btn btn-sm d-block d-sm-inline btn-success mb-1" data-action="edit_selected" data-item="archive" data-id="archive" data-api-url='edit/archive' data-api-attr='{}'><i class="bi bi-check-lg"></i> {{ lang.admin.save }}</button>
<button type="button" class="btn btn-sm d-block d-sm-inline btn-secondary mb-1 archive-test" data-action="edit_selected" data-item="archive" data-id="archive_test" data-api-url='edit/archive' data-api-attr='{"action":"test"}'{% if not archive_ready %} disabled{% endif %}><i class="bi bi-plug"></i> {{ lang.admin.archive_test }}</button>
<button type="button" class="btn btn-sm d-block d-sm-inline btn-secondary mb-1 archive-test" data-action="edit_selected" data-item="archive" data-id="archive_test" data-api-url='edit/archive' data-api-attr='{"action":"testmail"}'{% if not archive_ready %} disabled{% endif %}><i class="bi bi-envelope"></i> {{ lang.admin.archive_testmail }}</button>
<small class="text-muted d-block" id="archive_test_info">{{ lang.admin.archive_test_info }}</small>
</div>
</div>
</form>
</div>
</div>
</div>
+16
View File
@@ -17,6 +17,7 @@
<li role="presentation"><button class="dropdown-item" data-bs-target="#tab-api-logs" aria-selected="false" aria-controls="tab-api-logs" role="tab" data-bs-toggle="tab">API</button></li>
<li role="presentation"><button class="dropdown-item" data-bs-target="#tab-api-rl" aria-selected="false" aria-controls="tab-api-rl" role="tab" data-bs-toggle="tab">Ratelimits</button></li>
<li role="presentation"><button class="dropdown-item" data-bs-target="#tab-cron-logs" aria-selected="false" aria-controls="tab-api-rl" role="tab" data-bs-toggle="tab">Crontasks</button></li>
<li role="presentation"><button class="dropdown-item" data-bs-target="#tab-archive-logs" aria-selected="false" aria-controls="tab-archive-logs" role="tab" data-bs-toggle="tab">{{ lang.admin.archive }}</button></li>
<li role="presentation"><span class="dropdown-header fs-6">{{ lang.debug.external_logs }}</span></li>
<li role="presentation"><button class="dropdown-item" data-bs-target="#tab-rspamd-history" aria-selected="false" aria-controls="tab-rspamd-history" role="tab" data-bs-toggle="tab">Rspamd</button></li>
<li role="presentation"><span class="dropdown-header fs-6">{{ lang.debug.static_logs }}</span></li>
@@ -547,6 +548,21 @@
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-archive-logs">
<div class="debug-log-info">{{ lang.admin.archive_log_info }}</div>
<div class="card">
<div class="card-header d-flex align-items-center fs-5">
<span class="mt-2 ms-2">{{ lang.admin.archive }}</span>
<div class="btn-group ms-auto">
<button class="btn btn-sm btn-secondary refresh_table" data-table="archive_log">{{ lang.admin.refresh }}</button>
</div>
</div>
<div class="card-body">
<table id="archive_log" class="table table-striped dt-responsive w-100"></table>
</div>
</div>
</div>
</div> <!-- /tab-content -->
</div> <!-- /col-md-12 -->
</div> <!-- /row -->
@@ -8,6 +8,7 @@
<input type="hidden" value="0" name="active">
<input type="hidden" value="0" name="backupmx">
<input type="hidden" value="0" name="gal">
<input type="hidden" value="0" name="archive">
<input type="hidden" value="0" name="relay_all_recipients">
<input type="hidden" value="0" name="relay_unknown_only">
@@ -70,6 +71,14 @@
</div>
</div>
</div>
<div class="row{% if not archive_enabled %} d-none{% endif %}">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">
<label><input type="checkbox" class="form-check-input" value="1" name="archive"{% if template.attributes.archive == '1' %} checked{% endif %}> {{ lang.edit.archive_domain }}</label>
<small class="text-muted">{{ lang.edit.archive_domain_info }}</small>
</div>
</div>
</div>
<div class="row mb-2">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">
+9
View File
@@ -25,6 +25,7 @@
<input type="hidden" value="0" name="active">
<input type="hidden" value="0" name="backupmx">
<input type="hidden" value="0" name="gal">
<input type="hidden" value="0" name="archive">
<input type="hidden" value="0" name="relay_all_recipients">
<input type="hidden" value="0" name="relay_unknown_only">
<div class="row mb-4">
@@ -133,6 +134,14 @@
</div>
</div>
</div>
<div class="row{% if not archive_enabled %} d-none{% endif %}" data-acl="{{ acl.archive }}">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">
<label><input type="checkbox" class="form-check-input" value="1" name="archive"{% if result.archive == '1' %} checked{% endif %}{% if acl.archive != 1 %} disabled{% endif %}> {{ lang.edit.archive_domain }}</label>
<small class="text-muted">{{ lang.edit.archive_domain_info }}</small>
</div>
</div>
</div>
<hr>
<div class="row mb-2">
<div class="offset-sm-2 col-sm-10">
@@ -114,6 +114,17 @@
</select>
</div>
</div>
<div class="row mb-2{% if not archive_enabled %} d-none{% endif %}">
<label class="control-label col-sm-2" for="archive">{{ lang.edit.archive }}</label>
<div class="col-sm-10">
<select class="form-control" name="archive" id="archive">
<option value="inherit"{% if template.attributes.archive not in ['0', '1'] %} selected{% endif %}>{{ lang.edit.archive_inherit }}</option>
<option value="1"{% if template.attributes.archive == '1' %} selected{% endif %}>{{ lang.edit.archive_on }}</option>
<option value="0"{% if template.attributes.archive == '0' %} selected{% endif %}>{{ lang.edit.archive_off }}</option>
</select>
<small class="text-muted d-block">{{ lang.edit.archive_info }}</small>
</div>
</div>
<div class="row mb-4">
<label class="control-label col-sm-2">ACL</label>
<div class="col-sm-10">
+13
View File
@@ -331,6 +331,19 @@
<small class="text-muted">{{ lang.edit.allow_from_smtp_info }}</small>
</div>
</div>
<div class="row mt-2{% if not archive_enabled %} d-none{% endif %}" data-acl="{{ acl.archive }}">
<label class="control-label col-sm-2" for="archive">{{ lang.edit.archive }}
<br><span class="badge {% if result.archive == 1 %}bg-success{% else %}bg-secondary{% endif %}">{% if result.archive == 1 %}{{ lang.edit.archive_active }}{% else %}{{ lang.edit.archive_inactive }}{% endif %}</span>
</label>
<div class="col-sm-10">
<select class="form-control" name="archive" id="archive"{% if acl.archive != 1 %} disabled{% endif %}>
<option value="inherit"{% if result.attributes.archive not in ['0', '1'] %} selected{% endif %}>{{ lang.edit.archive_inherit }}</option>
<option value="1"{% if result.attributes.archive == '1' %} selected{% endif %}>{{ lang.edit.archive_on }}</option>
<option value="0"{% if result.attributes.archive == '0' %} selected{% endif %}>{{ lang.edit.archive_off }}</option>
</select>
<small class="text-muted d-block">{{ lang.edit.archive_info }}</small>
</div>
</div>
<hr>
<div class="row">
<div class="offset-sm-2 col-sm-10">
+1
View File
@@ -81,6 +81,7 @@
var csrf_token = '{{ csrf_token }}';
var pagination_size = Math.trunc('{{ pagination_size }}');
var role = '{{ role }}';
var archive_enabled = {{ archive_enabled ? 1 : 0 }};
var is_dual = {{ is_dual }};
var ALLOW_ADMIN_EMAIL_LOGIN = {{ allow_admin_email_login }};
</script>
+40 -1
View File
@@ -39,7 +39,7 @@
<div class="row mb-4">
<label class="control-label col-sm-2 text-sm-end text-sm-end" for="description">{{ lang.mailbox.template }}</label>
<div class="col-sm-10">
<select data-live-search="true" id="mailbox_templates" class="form-control" title="{{ lang.mailbox.template }}">
<select data-live-search="true" id="mailbox_templates" name="template" class="form-control" title="{{ lang.mailbox.template }}">
</select>
</div>
</div>
@@ -154,6 +154,17 @@
</select>
</div>
</div>
<div class="row mb-2{% if not archive_enabled %} d-none{% endif %}" data-acl="{{ acl.archive }}">
<label class="control-label col-sm-2 text-sm-end" for="addMailbox_archive">{{ lang.edit.archive }}</label>
<div class="col-sm-10">
<select class="form-control" name="archive" id="addMailbox_archive"{% if acl.archive != 1 %} disabled{% endif %}>
<option value="inherit" selected>{{ lang.edit.archive_inherit }}</option>
<option value="1">{{ lang.edit.archive_on }}</option>
<option value="0">{{ lang.edit.archive_off }}</option>
</select>
<small class="text-muted d-block">{{ lang.edit.archive_info }}</small>
</div>
</div>
<div class="row mb-4">
<label class="control-label col-sm-2 text-sm-end text-sm-end">ACL</label>
<div class="col-sm-10">
@@ -360,6 +371,17 @@
</select>
</div>
</div>
<div class="row mb-2{% if not archive_enabled %} d-none{% endif %}">
<label class="control-label col-sm-2 text-sm-end" for="addMailboxTemplate_archive">{{ lang.edit.archive }}</label>
<div class="col-sm-10">
<select class="form-control" name="archive" id="addMailboxTemplate_archive">
<option value="inherit" selected>{{ lang.edit.archive_inherit }}</option>
<option value="1">{{ lang.edit.archive_on }}</option>
<option value="0">{{ lang.edit.archive_off }}</option>
</select>
<small class="text-muted d-block">{{ lang.edit.archive_info }}</small>
</div>
</div>
<div class="row mb-4">
<label class="control-label col-sm-2 text-sm-end text-sm-end">ACL</label>
<div class="col-sm-10">
@@ -451,6 +473,7 @@
<div class="modal-body">
<form class="form-horizontal" data-cached-form="true" data-id="add_domain" role="form">
<input type="hidden" value="0" name="gal">
<input type="hidden" value="0" name="archive">
<input type="hidden" value="0" name="active">
<input type="hidden" value="0" name="backupmx">
<input type="hidden" value="0" name="relay_all_recipients">
@@ -525,6 +548,14 @@
</div>
</div>
{% endif %}
<div class="row mb-2{% if not archive_enabled %} d-none{% endif %}" data-acl="{{ acl.archive }}">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">
<label><input type="checkbox" class="form-check-input" value="1" name="archive" id="addDomain_archive"> {{ lang.edit.archive_domain }}</label>
<small class="text-muted">{{ lang.edit.archive_domain_info }}</small>
</div>
</div>
</div>
<div class="row mb-4">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">
@@ -664,6 +695,14 @@
</div>
</div>
</div>
<div class="row{% if not archive_enabled %} d-none{% endif %}">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">
<label><input type="checkbox" class="form-check-input" value="1" name="archive"> {{ lang.edit.archive_domain }}</label>
<small class="text-muted">{{ lang.edit.archive_domain_info }}</small>
</div>
</div>
</div>
<div class="row mb-2">
<div class="offset-sm-2 col-sm-10">
<div class="form-check">