1
0
mirror of https://github.com/mailcow/mailcow-dockerized.git synced 2025-12-13 18:06:01 +00:00

Allow making spam aliases permanent (#6888)

* Allow making spam aliases permanent

* added german translation

* updated Spamalias Twig + Rename in Spam Alias

* compose: update image tags to align to vendor version

---------

Co-authored-by: DerLinkman <niklas.meyer@servercow.de>
This commit is contained in:
Josh
2025-11-13 07:05:01 -08:00
committed by GitHub
parent 7b29c1f304
commit 0413d26855
11 changed files with 80 additions and 30 deletions

View File

@@ -167,7 +167,7 @@ DELIMITER //
CREATE EVENT clean_spamalias CREATE EVENT clean_spamalias
ON SCHEDULE EVERY 1 DAY DO ON SCHEDULE EVERY 1 DAY DO
BEGIN BEGIN
DELETE FROM spamalias WHERE validity < UNIX_TIMESTAMP(); DELETE FROM spamalias WHERE validity < UNIX_TIMESTAMP() AND permanent = 0;
END; END;
// //
DELIMITER ; DELIMITER ;

View File

@@ -390,7 +390,7 @@ hosts = unix:/var/run/mysqld/mysqld.sock
dbname = ${DBNAME} dbname = ${DBNAME}
query = SELECT goto FROM spamalias query = SELECT goto FROM spamalias
WHERE address='%s' WHERE address='%s'
AND validity >= UNIX_TIMESTAMP() AND (validity >= UNIX_TIMESTAMP() OR permanent != 0)
EOF EOF
if [ ! -f /opt/postfix/conf/dns_blocklists.cf ]; then if [ ! -f /opt/postfix/conf/dns_blocklists.cf ]; then
@@ -524,4 +524,4 @@ if [[ $? != 0 ]]; then
else else
postfix -c /opt/postfix/conf start postfix -c /opt/postfix/conf start
sleep 126144000 sleep 126144000
fi fi

View File

@@ -49,6 +49,12 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
// Default to 1 yr // Default to 1 yr
$_data["validity"] = 8760; $_data["validity"] = 8760;
} }
if (isset($_data["permanent"]) && filter_var($_data["permanent"], FILTER_VALIDATE_BOOL)) {
$permanent = 1;
}
else {
$permanent = 0;
}
$domain = $_data['domain']; $domain = $_data['domain'];
$description = $_data['description']; $description = $_data['description'];
$valid_domains[] = mailbox('get', 'mailbox_details', $username)['domain']; $valid_domains[] = mailbox('get', 'mailbox_details', $username)['domain'];
@@ -65,13 +71,14 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
return false; return false;
} }
$validity = strtotime("+" . $_data["validity"] . " hour"); $validity = strtotime("+" . $_data["validity"] . " hour");
$stmt = $pdo->prepare("INSERT INTO `spamalias` (`address`, `description`, `goto`, `validity`) VALUES $stmt = $pdo->prepare("INSERT INTO `spamalias` (`address`, `description`, `goto`, `validity`, `permanent`) VALUES
(:address, :description, :goto, :validity)"); (:address, :description, :goto, :validity, :permanent)");
$stmt->execute(array( $stmt->execute(array(
':address' => readable_random_string(rand(rand(3, 9), rand(3, 9))) . '.' . readable_random_string(rand(rand(3, 9), rand(3, 9))) . '@' . $domain, ':address' => readable_random_string(rand(rand(3, 9), rand(3, 9))) . '.' . readable_random_string(rand(rand(3, 9), rand(3, 9))) . '@' . $domain,
':description' => $description, ':description' => $description,
':goto' => $username, ':goto' => $username,
':validity' => $validity ':validity' => $validity,
':permanent' => $permanent
)); ));
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'success', 'type' => 'success',
@@ -2103,15 +2110,23 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
); );
continue; continue;
} }
if (empty($_data['validity'])) { if (empty($_data['validity']) && empty($_data['permanent'])) {
continue; continue;
} }
$validity = round((int)time() + ($_data['validity'] * 3600)); if (isset($_data['permanent']) && filter_var($_data['permanent'], FILTER_VALIDATE_BOOL)) {
$stmt = $pdo->prepare("UPDATE `spamalias` SET `validity` = :validity WHERE $permanent = 1;
$validity = 0;
}
else if (isset($_data['validity'])) {
$permanent = 0;
$validity = round((int)time() + ($_data['validity'] * 3600));
}
$stmt = $pdo->prepare("UPDATE `spamalias` SET `validity` = :validity, `permanent` = :permanent WHERE
`address` = :address"); `address` = :address");
$stmt->execute(array( $stmt->execute(array(
':address' => $address, ':address' => $address,
':validity' => $validity ':validity' => $validity,
':permanent' => $permanent
)); ));
$_SESSION['return'][] = array( $_SESSION['return'][] = array(
'type' => 'success', 'type' => 'success',
@@ -4584,10 +4599,12 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
`description`, `description`,
`validity`, `validity`,
`created`, `created`,
`modified` `modified`,
`permanent`
FROM `spamalias` FROM `spamalias`
WHERE `goto` = :username WHERE `goto` = :username
AND `validity` >= :unixnow"); AND (`validity` >= :unixnow
OR `permanent` != 0)");
$stmt->execute(array(':username' => $_data, ':unixnow' => time())); $stmt->execute(array(':username' => $_data, ':unixnow' => time()));
$tladata = $stmt->fetchAll(PDO::FETCH_ASSOC); $tladata = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $tladata; return $tladata;
@@ -5162,7 +5179,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$stmt = $pdo->prepare("SELECT COALESCE(SUM(`quota`), 0) as `in_use` FROM `mailbox` WHERE (`kind` = '' OR `kind` = NULL) AND `domain` = :domain AND `username` != :username"); $stmt = $pdo->prepare("SELECT COALESCE(SUM(`quota`), 0) as `in_use` FROM `mailbox` WHERE (`kind` = '' OR `kind` = NULL) AND `domain` = :domain AND `username` != :username");
$stmt->execute(array(':domain' => $row['domain'], ':username' => $_data)); $stmt->execute(array(':domain' => $row['domain'], ':username' => $_data));
$MailboxUsage = $stmt->fetch(PDO::FETCH_ASSOC); $MailboxUsage = $stmt->fetch(PDO::FETCH_ASSOC);
$stmt = $pdo->prepare("SELECT IFNULL(COUNT(`address`), 0) AS `sa_count` FROM `spamalias` WHERE `goto` = :address AND `validity` >= :unixnow"); $stmt = $pdo->prepare("SELECT IFNULL(COUNT(`address`), 0) AS `sa_count` FROM `spamalias` WHERE `goto` = :address AND (`validity` >= :unixnow OR `permanent` != 0)");
$stmt->execute(array(':address' => $_data, ':unixnow' => time())); $stmt->execute(array(':address' => $_data, ':unixnow' => time()));
$SpamaliasUsage = $stmt->fetch(PDO::FETCH_ASSOC); $SpamaliasUsage = $stmt->fetch(PDO::FETCH_ASSOC);
$mailboxdata['max_new_quota'] = ($DomainQuota['quota'] * 1048576) - $MailboxUsage['in_use']; $mailboxdata['max_new_quota'] = ($DomainQuota['quota'] * 1048576) - $MailboxUsage['in_use'];

View File

@@ -4,7 +4,7 @@ function init_db_schema()
try { try {
global $pdo; global $pdo;
$db_version = "07102025_1015"; $db_version = "10312025_0525";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'"); $stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@@ -554,7 +554,8 @@ function init_db_schema()
"description" => "TEXT NOT NULL", "description" => "TEXT NOT NULL",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)", "created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
"modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP", "modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP",
"validity" => "INT(11)" "validity" => "INT(11)",
"permanent" => "TINYINT(1) NOT NULL DEFAULT '0'"
), ),
"keys" => array( "keys" => array(
"primary" => array( "primary" => array(

View File

@@ -175,6 +175,10 @@ jQuery(function($){
'</div>'; '</div>';
item.chkbox = '<input type="checkbox" class="form-check-input" data-id="tla" name="multi_select" value="' + encodeURIComponent(item.address) + '" />'; item.chkbox = '<input type="checkbox" class="form-check-input" data-id="tla" name="multi_select" value="' + encodeURIComponent(item.address) + '" />';
item.address = escapeHtml(item.address); item.address = escapeHtml(item.address);
item.validity = {
value: item.validity,
permanent: item.permanent
};
} }
else { else {
item.chkbox = '<input type="checkbox" class="form-check-input" disabled />'; item.chkbox = '<input type="checkbox" class="form-check-input" disabled />';
@@ -218,9 +222,21 @@ jQuery(function($){
title: lang.alias_valid_until, title: lang.alias_valid_until,
data: 'validity', data: 'validity',
defaultContent: '', defaultContent: '',
createdCell: function(td, cellData) { render: function (data, type) {
createSortableDate(td, cellData) var date = new Date(data.value ? data.value * 1000 : 0);
} switch (type) {
case "sort":
if (data.permanent) {
return 0;
}
return date.getTime();
default:
if (data.permanent) {
return lang.forever;
}
return date.toLocaleDateString(LOCALE, DATETIME_FORMAT);
}
},
}, },
{ {
title: lang.created_on, title: lang.created_on,

View File

@@ -987,7 +987,7 @@
"sogo_visible": "Alias Sichtbarkeit in SOGo", "sogo_visible": "Alias Sichtbarkeit in SOGo",
"sogo_visible_n": "Alias in SOGo verbergen", "sogo_visible_n": "Alias in SOGo verbergen",
"sogo_visible_y": "Alias in SOGo anzeigen", "sogo_visible_y": "Alias in SOGo anzeigen",
"spam_aliases": "Temp. Alias", "spam_aliases": "Spam-Alias",
"stats": "Statistik", "stats": "Statistik",
"status": "Status", "status": "Status",
"sync_jobs": "Synchronisationen", "sync_jobs": "Synchronisationen",
@@ -1281,7 +1281,9 @@
"encryption": "Verschlüsselung", "encryption": "Verschlüsselung",
"excludes": "Ausschlüsse", "excludes": "Ausschlüsse",
"expire_in": "Ungültig in", "expire_in": "Ungültig in",
"expire_never": "Niemals ungültig",
"fido2_webauthn": "FIDO2/WebAuthn", "fido2_webauthn": "FIDO2/WebAuthn",
"forever": "Für immer",
"force_pw_update": "Das Passwort für diesen Benutzer <b>muss</b> geändert werden, damit die Zugriffssperre auf die Groupware-Komponenten wieder freigeschaltet wird.", "force_pw_update": "Das Passwort für diesen Benutzer <b>muss</b> geändert werden, damit die Zugriffssperre auf die Groupware-Komponenten wieder freigeschaltet wird.",
"from": "von", "from": "von",
"generate": "generieren", "generate": "generieren",
@@ -1346,7 +1348,8 @@
"sogo_profile_reset": "SOGo-Profil zurücksetzen", "sogo_profile_reset": "SOGo-Profil zurücksetzen",
"sogo_profile_reset_help": "Das Profil wird inklusive <b>aller</b> Kalender- und Kontaktdaten <b>unwiederbringlich gelöscht</b>.", "sogo_profile_reset_help": "Das Profil wird inklusive <b>aller</b> Kalender- und Kontaktdaten <b>unwiederbringlich gelöscht</b>.",
"sogo_profile_reset_now": "Profil jetzt zurücksetzen", "sogo_profile_reset_now": "Profil jetzt zurücksetzen",
"spam_aliases": "Temporäre E-Mail-Aliasse", "spam_aliases": "Spam E-Mail-Aliasse",
"spam_aliases_info": "Ein Spam-Alias ist eine temporäre E-Mailadresse, die benutzt werden kann, um eine echte E-Mail Adressen zu schützen. <br>Optional kann eine Ablaufzeit gesetzt werden, sodass der Alias nach dem definierten Zeitraum automatisch deaktiviert wird, was missbrauchte oder geleakte Adressen effektiv entsorgt.",
"spam_score_reset": "Auf Server-Standard zurücksetzen", "spam_score_reset": "Auf Server-Standard zurücksetzen",
"spamfilter": "Spamfilter", "spamfilter": "Spamfilter",
"spamfilter_behavior": "Bewertung", "spamfilter_behavior": "Bewertung",

View File

@@ -1288,7 +1288,9 @@
"encryption": "Encryption", "encryption": "Encryption",
"excludes": "Excludes", "excludes": "Excludes",
"expire_in": "Expire in", "expire_in": "Expire in",
"expire_never": "Never Expire",
"fido2_webauthn": "FIDO2/WebAuthn", "fido2_webauthn": "FIDO2/WebAuthn",
"forever": "Forever",
"force_pw_update": "You <b>must</b> set a new password to be able to access groupware related services.", "force_pw_update": "You <b>must</b> set a new password to be able to access groupware related services.",
"from": "from", "from": "from",
"generate": "generate", "generate": "generate",
@@ -1355,7 +1357,8 @@
"sogo_profile_reset": "Reset SOGo profile", "sogo_profile_reset": "Reset SOGo profile",
"sogo_profile_reset_help": "This will destroy a user's SOGo profile and <b>delete all contact and calendar data irretrievable</b>.", "sogo_profile_reset_help": "This will destroy a user's SOGo profile and <b>delete all contact and calendar data irretrievable</b>.",
"sogo_profile_reset_now": "Reset profile now", "sogo_profile_reset_now": "Reset profile now",
"spam_aliases": "Temporary email aliases", "spam_aliases": "Spam email aliases",
"spam_aliases_info": "A spam alias is a temporary email address that can be used to protect real email addresses. <br>Optionally, an expiration time can be set so that the alias is automatically deactivated after the defined period, effectively disposing of abused or leaked addresses.",
"spam_score_reset": "Reset to server default", "spam_score_reset": "Reset to server default",
"spamfilter": "Spam filter", "spamfilter": "Spam filter",
"spamfilter_behavior": "Rating", "spamfilter_behavior": "Rating",

View File

@@ -1084,6 +1084,7 @@
"aliases_send_as_all": "No verificar permisos del remitente para los siguientes dominios (y sus aliases)", "aliases_send_as_all": "No verificar permisos del remitente para los siguientes dominios (y sus aliases)",
"change_password": "Cambiar contraseña", "change_password": "Cambiar contraseña",
"create_syncjob": "Crear nuevo trabajo de sincronización", "create_syncjob": "Crear nuevo trabajo de sincronización",
"created_on": "Creado",
"daily": "Cada día", "daily": "Cada día",
"day": "Día", "day": "Día",
"description": "Descripción", "description": "Descripción",
@@ -1095,6 +1096,9 @@
"edit": "Editar", "edit": "Editar",
"encryption": "Cifrado", "encryption": "Cifrado",
"excludes": "Excluye", "excludes": "Excluye",
"expire_in": "Expirará en",
"expire_never": "Nunca expirará",
"forever": "Siempre",
"hour": "Hora", "hour": "Hora",
"hourly": "Cada hora", "hourly": "Cada hora",
"hours": "Horas", "hours": "Horas",
@@ -1115,7 +1119,8 @@
"shared_aliases": "Alias compartidos", "shared_aliases": "Alias compartidos",
"shared_aliases_desc": "Los alias compartidos no se ven afectados por la configuración específica del usuario, como el filtro de correo no deseado o la política de cifrado. Los filtros de spam correspondientes solo pueden ser realizados por un administrador como una política de dominio.", "shared_aliases_desc": "Los alias compartidos no se ven afectados por la configuración específica del usuario, como el filtro de correo no deseado o la política de cifrado. Los filtros de spam correspondientes solo pueden ser realizados por un administrador como una política de dominio.",
"sogo_profile_reset": "Resetear perfil SOGo", "sogo_profile_reset": "Resetear perfil SOGo",
"spam_aliases": "Alias de email temporales", "spam_aliases": "Alias de email de spam",
"spam_aliases_info": "Un alias de spam es una dirección de correo electrónico temporal que se puede usar para proteger direcciones de correo electrónico reales. <br>Opcionalmente, se puede establecer un tiempo de expiración para que el alias se desactive automáticamente después del período definido, eliminando efectivamente las direcciones abusadas o filtradas.",
"spamfilter": "Filtro anti-spam", "spamfilter": "Filtro anti-spam",
"spamfilter_behavior": "Clasificación", "spamfilter_behavior": "Clasificación",
"spamfilter_bl": "Lista negra", "spamfilter_bl": "Lista negra",

View File

@@ -1187,6 +1187,7 @@
"created_on": "作成日", "created_on": "作成日",
"daily": "毎日", "daily": "毎日",
"day": "日", "day": "日",
"description": "説明",
"delete_ays": "削除プロセスを確認してください。", "delete_ays": "削除プロセスを確認してください。",
"direct_aliases": "直接エイリアスアドレス", "direct_aliases": "直接エイリアスアドレス",
"direct_aliases_desc": "直接エイリアスアドレスは、スパムフィルターおよびTLSポリシー設定の影響を受けます。", "direct_aliases_desc": "直接エイリアスアドレスは、スパムフィルターおよびTLSポリシー設定の影響を受けます。",
@@ -1201,7 +1202,9 @@
"encryption": "暗号化", "encryption": "暗号化",
"excludes": "除外", "excludes": "除外",
"expire_in": "有効期限まで", "expire_in": "有効期限まで",
"expire_never": "有効期限なし",
"fido2_webauthn": "FIDO2/WebAuthn", "fido2_webauthn": "FIDO2/WebAuthn",
"forever": "有効期限なし",
"force_pw_update": "グループウェア関連サービスにアクセスするには、新しいパスワードを<b>必ず</b>設定する必要があります。", "force_pw_update": "グループウェア関連サービスにアクセスするには、新しいパスワードを<b>必ず</b>設定する必要があります。",
"from": "送信元", "from": "送信元",
"generate": "生成", "generate": "生成",

View File

@@ -8,6 +8,7 @@
</div> </div>
<div id="collapse-tab-SpamAliases" class="card-body collapse" data-bs-parent="#user-content"> <div id="collapse-tab-SpamAliases" class="card-body collapse" data-bs-parent="#user-content">
<div class="row"> <div class="row">
<p>{{ lang.user.spam_aliases_info|raw }}</p>
<div class="col-md-12 col-sm-12 col-12"> <div class="col-md-12 col-sm-12 col-12">
<table id="tla_table" class="table table-striped dt-responsive w-100"></table> <table id="tla_table" class="table table-striped dt-responsive w-100"></table>
</div> </div>
@@ -18,12 +19,13 @@
<a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" id="toggle_multi_select_all" data-id="tla" href="#"><i class="bi bi-check-all"></i> {{ lang.mailbox.toggle_all }}</a> <a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary" id="toggle_multi_select_all" data-id="tla" href="#"><i class="bi bi-check-all"></i> {{ lang.mailbox.toggle_all }}</a>
<a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary dropdown-toggle" data-bs-toggle="dropdown" href="#">{{ lang.mailbox.quick_actions }}</a> <a class="btn btn-sm btn-xs-half d-block d-sm-inline btn-secondary dropdown-toggle" data-bs-toggle="dropdown" href="#">{{ lang.mailbox.quick_actions }}</a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"1"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.hour }}</a></li> <li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"1","permanent":"0"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.hour }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"24"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.day }}</a></li> <li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"24","permanent":"0"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.day }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"168"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.week }}</a></li> <li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"168","permanent":"0"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.week }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"744"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.month }}</a></li> <li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"744","permanent":"0"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.month }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"8760"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.year }}</a></li> <li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"8760","permanent":"0"}' href="#">{{ lang.user.expire_in }} 1 {{ lang.user.year }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"87600"}' href="#">{{ lang.user.expire_in }} 10 {{ lang.user.years }}</a></li> <li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"validity":"87600","permanent":"0"}' href="#">{{ lang.user.expire_in }} 10 {{ lang.user.years }}</a></li>
<li><a class="dropdown-item" data-action="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{"permanent":"1"}' href="#">{{ lang.user.expire_never }}</a></li>
<li><hr class="dropdown-divider"></li> <li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" data-action="delete_selected" data-id="tla" data-api-url='delete/time_limited_alias' href="#">{{ lang.mailbox.remove }}</a></li> <li><a class="dropdown-item" data-action="delete_selected" data-id="tla" data-api-url='delete/time_limited_alias' href="#">{{ lang.mailbox.remove }}</a></li>
</ul> </ul>

View File

@@ -117,7 +117,7 @@ services:
- rspamd - rspamd
php-fpm-mailcow: php-fpm-mailcow:
image: ghcr.io/mailcow/phpfpm:1.94 image: ghcr.io/mailcow/phpfpm:8.2.29
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0" command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
depends_on: depends_on:
- redis-mailcow - redis-mailcow
@@ -339,7 +339,7 @@ services:
- dovecot - dovecot
postfix-mailcow: postfix-mailcow:
image: ghcr.io/mailcow/postfix:1.81 image: ghcr.io/mailcow/postfix:3.7.11
depends_on: depends_on:
mysql-mailcow: mysql-mailcow:
condition: service_started condition: service_started