Merge pull request #4819 from mailcow/staging

2022-10
This commit is contained in:
Niklas Meyer 2022-10-25 14:16:05 +02:00 committed by GitHub
commit 3028a18a37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1369 additions and 65 deletions

View File

@ -0,0 +1,13 @@
## :memo: Brief description
<!-- Diff summary - START -->
<!-- Diff summary - END -->
## :computer: Commits
<!-- Diff commits - START -->
<!-- Diff commits - END -->
## :file_folder: Modified files
<!-- Diff files - START -->
<!-- Diff files - END -->

View File

@ -14,7 +14,7 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- name: Mark/Close Stale Issues and Pull Requests 🗑️ - name: Mark/Close Stale Issues and Pull Requests 🗑️
uses: actions/stale@v6.0.0 uses: actions/stale@v6.0.1
with: with:
repo-token: ${{ secrets.STALE_ACTION_PAT }} repo-token: ${{ secrets.STALE_ACTION_PAT }}
days-before-stale: 60 days-before-stale: 60

25
.github/workflows/pr_to_nightly.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: Create PR to merge to nightly from staging
on:
push:
branches:
- staging
jobs:
action-pull-request:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Run the Action
uses: devops-infra/action-pull-request@v0.5.1
with:
github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }}
title: Automatic PR to nightly from ${{ github.event.repository.updated_at}}
assignee: DerLinkman
source_branch: staging
target_branch: nightly
reviewer: DerLinkman
label: upstream
template: .github/ISSUE_TEMPLATE/pr_to_nighty_template.yml
get_diff: true

View File

@ -0,0 +1,3 @@
FROM debian:bullseye-slim
RUN apt update && apt install pigz

View File

@ -252,7 +252,7 @@ def permBan(net, unban=False):
if rule not in chain.rules and not unban: if rule not in chain.rules and not unban:
logCrit('Add host/network %s to blacklist' % net) logCrit('Add host/network %s to blacklist' % net)
chain.insert_rule(rule) chain.insert_rule(rule)
r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time()))) r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time())))
elif rule in chain.rules and unban: elif rule in chain.rules and unban:
logCrit('Remove host/network %s from blacklist' % net) logCrit('Remove host/network %s from blacklist' % net)
chain.delete_rule(rule) chain.delete_rule(rule)
@ -267,7 +267,7 @@ def permBan(net, unban=False):
if rule not in chain.rules and not unban: if rule not in chain.rules and not unban:
logCrit('Add host/network %s to blacklist' % net) logCrit('Add host/network %s to blacklist' % net)
chain.insert_rule(rule) chain.insert_rule(rule)
r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time()))) r.hset('F2B_PERM_BANS', '%s' % net, int(round(time.time())))
elif rule in chain.rules and unban: elif rule in chain.rules and unban:
logCrit('Remove host/network %s from blacklist' % net) logCrit('Remove host/network %s from blacklist' % net)
chain.delete_rule(rule) chain.delete_rule(rule)
@ -346,6 +346,8 @@ def snat4(snat_target):
rule.dst = '!' + rule.src rule.dst = '!' + rule.src
target = rule.create_target("SNAT") target = rule.create_target("SNAT")
target.to_source = snat_target target.to_source = snat_target
match = rule.create_match("comment")
match.comment = f'{int(round(time.time()))}'
return rule return rule
while not quit_now: while not quit_now:
@ -356,19 +358,26 @@ def snat4(snat_target):
table.refresh() table.refresh()
chain = iptc.Chain(table, 'POSTROUTING') chain = iptc.Chain(table, 'POSTROUTING')
table.autocommit = False table.autocommit = False
if get_snat4_rule() not in chain.rules: new_rule = get_snat4_rule()
logCrit('Added POSTROUTING rule for source network %s to SNAT target %s' % (get_snat4_rule().src, snat_target)) for position, rule in enumerate(chain.rules):
chain.insert_rule(get_snat4_rule()) match = all((
table.commit() new_rule.get_src() == rule.get_src(),
else: new_rule.get_dst() == rule.get_dst(),
for position, item in enumerate(chain.rules): new_rule.target.parameters == rule.target.parameters,
if item == get_snat4_rule(): new_rule.target.name == rule.target.name
if position != 0: ))
chain.delete_rule(get_snat4_rule()) if position == 0:
table.commit() if not match:
logInfo(f'Added POSTROUTING rule for source network {new_rule.src} to SNAT target {snat_target}')
chain.insert_rule(new_rule)
else:
if match:
logInfo(f'Remove rule for source network {new_rule.src} to SNAT target {snat_target} from POSTROUTING chain at position {position}')
chain.delete_rule(rule)
table.commit()
table.autocommit = True table.autocommit = True
except: except:
print('Error running SNAT4, retrying...') print('Error running SNAT4, retrying...')
def snat6(snat_target): def snat6(snat_target):
global lock global lock
@ -402,7 +411,7 @@ def snat6(snat_target):
table.commit() table.commit()
table.autocommit = True table.autocommit = True
except: except:
print('Error running SNAT6, retrying...') print('Error running SNAT6, retrying...')
def autopurge(): def autopurge():
while not quit_now: while not quit_now:
@ -468,7 +477,7 @@ def whitelistUpdate():
if Counter(new_whitelist) != Counter(WHITELIST): if Counter(new_whitelist) != Counter(WHITELIST):
WHITELIST = new_whitelist WHITELIST = new_whitelist
logInfo('Whitelist was changed, it has %s entries' % len(WHITELIST)) logInfo('Whitelist was changed, it has %s entries' % len(WHITELIST))
time.sleep(60.0 - ((time.time() - start_time) % 60.0)) time.sleep(60.0 - ((time.time() - start_time) % 60.0))
def blacklistUpdate(): def blacklistUpdate():
global quit_now global quit_now
@ -479,7 +488,7 @@ def blacklistUpdate():
new_blacklist = [] new_blacklist = []
if list: if list:
new_blacklist = genNetworkList(list) new_blacklist = genNetworkList(list)
if Counter(new_blacklist) != Counter(BLACKLIST): if Counter(new_blacklist) != Counter(BLACKLIST):
addban = set(new_blacklist).difference(BLACKLIST) addban = set(new_blacklist).difference(BLACKLIST)
delban = set(BLACKLIST).difference(new_blacklist) delban = set(BLACKLIST).difference(new_blacklist)
BLACKLIST = new_blacklist BLACKLIST = new_blacklist
@ -490,7 +499,7 @@ def blacklistUpdate():
if delban: if delban:
for net in delban: for net in delban:
permBan(net=net, unban=True) permBan(net=net, unban=True)
time.sleep(60.0 - ((time.time() - start_time) % 60.0)) time.sleep(60.0 - ((time.time() - start_time) % 60.0))
def initChain(): def initChain():
# Is called before threads start, no locking # Is called before threads start, no locking

File diff suppressed because one or more lines are too long

View File

@ -195,9 +195,65 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/sessions.inc.php';
// Set language // Set language
if (!isset($_SESSION['mailcow_locale']) && !isset($_COOKIE['mailcow_locale'])) { if (!isset($_SESSION['mailcow_locale']) && !isset($_COOKIE['mailcow_locale'])) {
if ($DETECT_LANGUAGE && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { if ($DETECT_LANGUAGE && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
$header_lang = strtolower(substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2)); // regex inspired from @GabrielAnderson on http://stackoverflow.com/questions/6038236/http-accept-language
if (array_key_exists($header_lang, $AVAILABLE_LANGUAGES)) { preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})*)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse);
$_SESSION['mailcow_locale'] = $header_lang;
$langs = $lang_parse[1];
$ranks = $lang_parse[4];
// (create an associative array 'language' => 'preference')
$lang2pref = array();
for ($i=0; $i<count($langs); $i++) {
$lang2pref[strtolower($langs[$i])] = (float) (!empty($ranks[$i]) ? $ranks[$i] : 1);
}
// (comparison function for uksort)
$cmpLangs = function ($a, $b) use ($lang2pref) {
if ($lang2pref[$a] > $lang2pref[$b])
return -1;
elseif ($lang2pref[$a] < $lang2pref[$b])
return 1;
elseif (strlen($a) > strlen($b))
return -1;
elseif (strlen($a) < strlen($b))
return 1;
else
return 0;
};
// sort the languages by prefered language and by the most specific region
uksort($lang2pref, $cmpLangs);
// generate language array without the region part
$AVAILABLE_BASE_LANGUAGES=array();
foreach ($AVAILABLE_LANGUAGES as $code => $lang) {
$base_code = substr($code, 0, 2);
if (!array_key_exists($base_code, $AVAILABLE_BASE_LANGUAGES)) {
$AVAILABLE_BASE_LANGUAGES[$base_code] = $code;
}
}
// Find a perfect match or partial match
// Match en-gb or en
foreach ($lang2pref as $lang => $q) {
if (array_key_exists($lang, $AVAILABLE_LANGUAGES)) {
$_SESSION['mailcow_locale'] = $lang;
break;
} elseif (array_key_exists($lang, $AVAILABLE_BASE_LANGUAGES)) {
$_SESSION['mailcow_locale'] = $AVAILABLE_BASE_LANGUAGES[$lang];
break;
}
}
// Try suggest match
// e.g. suggest en-gb when only en-us is provided
if (!isset($_COOKIE['mailcow_locale'])) {
foreach ($lang2pref as $lang => $q) {
if (array_key_exists(substr($lang, 0, 2), $AVAILABLE_BASE_LANGUAGES)) {
$_SESSION['mailcow_locale'] = $AVAILABLE_BASE_LANGUAGES[substr($lang, 0, 2)];
break;
}
}
} }
} }
else { else {
@ -215,7 +271,7 @@ if (isset($_GET['lang']) && array_key_exists($_GET['lang'], $AVAILABLE_LANGUAGES
/* /*
* load language * load language
*/ */
$lang = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/lang/lang.en.json'), true); $lang = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/lang/lang.en-gb.json'), true);
$langFile = $_SERVER['DOCUMENT_ROOT'] . '/lang/lang.'.$_SESSION['mailcow_locale'].'.json'; $langFile = $_SERVER['DOCUMENT_ROOT'] . '/lang/lang.'.$_SESSION['mailcow_locale'].'.json';
if(file_exists($langFile)) { if(file_exists($langFile)) {

View File

@ -76,33 +76,35 @@ $autodiscover_config = array(
$DETECT_LANGUAGE = true; $DETECT_LANGUAGE = true;
// Change default language // Change default language
$DEFAULT_LANG = 'en'; $DEFAULT_LANG = 'en-gb';
// Available languages // Available languages
// https://www.iso.org/obp/ui/#search // https://www.iso.org/obp/ui/#search
// https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes // https://en.wikipedia.org/wiki/IETF_language_tag
$AVAILABLE_LANGUAGES = array( $AVAILABLE_LANGUAGES = array(
'cs' => 'Čeština (Czech)', // 'ca-es' => 'Català (Catalan)',
'da' => 'Danish (Dansk)', 'cs-cz' => 'Čeština (Czech)',
'de' => 'Deutsch (German)', 'da-dk' => 'Danish (Dansk)',
'en' => 'English', 'de-de' => 'Deutsch (German)',
'es' => 'Español (Spanish)', 'en-gb' => 'English',
'fi' => 'Suomi (Finish)', 'es-es' => 'Español (Spanish)',
'fr' => 'Français (French)', 'fi-fi' => 'Suomi (Finish)',
'hu' => 'Magyar (Hungarian)', 'fr-fr' => 'Français (French)',
'it' => 'Italiano (Italian)', 'hu-hu' => 'Magyar (Hungarian)',
'ko' => '한국어 (Korean)', 'it-it' => 'Italiano (Italian)',
'lv' => 'latviešu (Latvian)', 'ko-kr' => '한국어 (Korean)',
'nl' => 'Nederlands (Dutch)', 'lv-lv' => 'latviešu (Latvian)',
'pl' => 'Język Polski (Polish)', 'nl-nl' => 'Nederlands (Dutch)',
'pt' => 'Português (Portuguese)', 'pl-pl' => 'Język Polski (Polish)',
'ro' => 'Română (Romanian)', 'pt-pt' => 'Português (Portuguese)',
'ru' => 'Pусский (Russian)', 'ro-ro' => 'Română (Romanian)',
'sk' => 'Slovenčina (Slovak)', 'ru-ru' => 'Pусский (Russian)',
'sv' => 'Svenska (Swedish)', 'sk-sk' => 'Slovenčina (Slovak)',
'tr' => 'Türkçe (Turkish)', 'sv-se' => 'Svenska (Swedish)',
'uk' => 'Українська (Ukrainian)', 'tr-tr' => 'Türkçe (Turkish)',
'zh' => '中文 (Chinese)' 'uk-ua' => 'Українська (Ukrainian)',
'zh-cn' => '简体中文 (Simplified Chinese)',
'zh-tw' => '繁體中文 (Traditional Chinese)',
); );
// Change theme (default: lumen) // Change theme (default: lumen)

File diff suppressed because it is too large Load Diff

View File

@ -32,12 +32,12 @@
<ul class="nav navbar-nav navbar-right"> <ul class="nav navbar-nav navbar-right">
{% if mailcow_locale %} {% if mailcow_locale %}
<li class="dropdown{% if available_languages|length == 1 %}lang-link-disabled{% endif %}"> <li class="dropdown{% if available_languages|length == 1 %}lang-link-disabled{% endif %}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><span class="flag-icon flag-icon-{{ mailcow_locale }}"></span><span class="caret"></span></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><span class="flag-icon flag-icon-{{ mailcow_locale[-2:] }}"></span><span class="caret"></span></a>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
{% for key, val in available_languages %} {% for key, val in available_languages %}
<li{% if mailcow_locale == key %} class="active"{% endif %}> <li{% if mailcow_locale == key %} class="active"{% endif %}>
<a href="?{{ query_string({'lang': key}) }}"> <a href="?{{ query_string({'lang': key}) }}">
<span class="flag-icon flag-icon-{{ key }}"></span>{{ val }} <span class="flag-icon flag-icon-{{ key[-2:] }}"></span>{{ val }}
</a> </a>
</li> </li>
{% endfor %} {% endfor %}

View File

@ -45,13 +45,13 @@
</div> </div>
{% if not oauth2_request %} {% if not oauth2_request %}
<button type="button" {% if available_languages|length == 1 %}disabled="true"{% endif %} class="btn btn-xs-lg btn-default pull-right dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button type="button" {% if available_languages|length == 1 %}disabled="true"{% endif %} class="btn btn-xs-lg btn-default pull-right dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="flag-icon flag-icon-{{ mailcow_locale }}"></span> <span class="caret"></span> <span class="flag-icon flag-icon-{{ mailcow_locale[-2:] }}"></span> <span class="caret"></span>
</button> </button>
<ul class="dropdown-menu pull-right login"> <ul class="dropdown-menu pull-right login">
{% for key, val in available_languages %} {% for key, val in available_languages %}
<li{% if mailcow_locale == key %} class="active"{% endif %}> <li{% if mailcow_locale == key %} class="active"{% endif %}>
<a href="?{{ query_string({'lang': key}) }}"> <a href="?{{ query_string({'lang': key}) }}">
<span class="flag-icon flag-icon-{{ key }}"></span>{{ val }} <span class="flag-icon flag-icon-{{ key[-2:] }}"></span>{{ val }}
</a> </a>
</li> </li>
{% endfor %} {% endfor %}

View File

@ -41,7 +41,7 @@ services:
- mysql - mysql
redis-mailcow: redis-mailcow:
image: redis:6-alpine image: redis:7-alpine
volumes: volumes:
- redis-vol-1:/data/ - redis-vol-1:/data/
restart: always restart: always
@ -76,7 +76,7 @@ services:
- clamd - clamd
rspamd-mailcow: rspamd-mailcow:
image: mailcow/rspamd:1.90 image: mailcow/rspamd:1.91
stop_grace_period: 30s stop_grace_period: 30s
depends_on: depends_on:
- dovecot-mailcow - dovecot-mailcow

View File

@ -26,7 +26,7 @@ if(empty($targetLang)) {
} }
// load master lang // load master lang
$masterLang = file_get_contents(__DIR__.'/../data/web/lang/lang.en.json'); $masterLang = file_get_contents(__DIR__.'/../data/web/lang/lang.en-gb.json');
$masterLang = json_decode($masterLang, true); $masterLang = json_decode($masterLang, true);
// load target lang // load target lang

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
DEBIAN_DOCKER_IMAGE="debian:bullseye-slim" DEBIAN_DOCKER_IMAGE="mailcow/backup:1.0"
if [[ ! -z ${MAILCOW_BACKUP_LOCATION} ]]; then if [[ ! -z ${MAILCOW_BACKUP_LOCATION} ]]; then
BACKUP_LOCATION="${MAILCOW_BACKUP_LOCATION}" BACKUP_LOCATION="${MAILCOW_BACKUP_LOCATION}"
@ -52,6 +52,15 @@ BACKUP_LOCATION=$(echo ${BACKUP_LOCATION} | sed 's#/$##')
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
COMPOSE_FILE=${SCRIPT_DIR}/../docker-compose.yml COMPOSE_FILE=${SCRIPT_DIR}/../docker-compose.yml
ENV_FILE=${SCRIPT_DIR}/../.env ENV_FILE=${SCRIPT_DIR}/../.env
THREADS=$(echo ${THREADS:-1})
if ! [[ "${THREADS}" =~ ^[1-9]+$ ]] ; then
echo "Thread input is not a number!"
exit 1
elif [[ "${THREADS}" =~ ^[1-9]+$ ]] ; then
echo "Using ${THREADS} Thread(s) for this run."
echo "Notice: You can set the Thread count with the THREADS Variable before you run this script."
fi
if [ ! -f ${COMPOSE_FILE} ]; then if [ ! -f ${COMPOSE_FILE} ]; then
echo "Compose file not found" echo "Compose file not found"
@ -99,32 +108,32 @@ function backup() {
docker run --name mailcow-backup --rm \ docker run --name mailcow-backup --rm \
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \ -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_vmail-vol-1$):/vmail:ro,z \ -v $(docker volume ls -qf name=^${CMPS_PRJ}_vmail-vol-1$):/vmail:ro,z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="gzip --rsyncable" -Pcvpf /backup/backup_vmail.tar.gz /vmail ${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_vmail.tar.gz /vmail
;;& ;;&
crypt|all) crypt|all)
docker run --name mailcow-backup --rm \ docker run --name mailcow-backup --rm \
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \ -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_crypt-vol-1$):/crypt:ro,z \ -v $(docker volume ls -qf name=^${CMPS_PRJ}_crypt-vol-1$):/crypt:ro,z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="gzip --rsyncable" -Pcvpf /backup/backup_crypt.tar.gz /crypt ${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_crypt.tar.gz /crypt
;;& ;;&
redis|all) redis|all)
docker exec $(docker ps -qf name=redis-mailcow) redis-cli save docker exec $(docker ps -qf name=redis-mailcow) redis-cli save
docker run --name mailcow-backup --rm \ docker run --name mailcow-backup --rm \
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \ -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_redis-vol-1$):/redis:ro,z \ -v $(docker volume ls -qf name=^${CMPS_PRJ}_redis-vol-1$):/redis:ro,z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="gzip --rsyncable" -Pcvpf /backup/backup_redis.tar.gz /redis ${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_redis.tar.gz /redis
;;& ;;&
rspamd|all) rspamd|all)
docker run --name mailcow-backup --rm \ docker run --name mailcow-backup --rm \
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \ -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_rspamd-vol-1$):/rspamd:ro,z \ -v $(docker volume ls -qf name=^${CMPS_PRJ}_rspamd-vol-1$):/rspamd:ro,z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="gzip --rsyncable" -Pcvpf /backup/backup_rspamd.tar.gz /rspamd ${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_rspamd.tar.gz /rspamd
;;& ;;&
postfix|all) postfix|all)
docker run --name mailcow-backup --rm \ docker run --name mailcow-backup --rm \
-v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \ -v ${BACKUP_LOCATION}/mailcow-${DATE}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_postfix-vol-1$):/postfix:ro,z \ -v $(docker volume ls -qf name=^${CMPS_PRJ}_postfix-vol-1$):/postfix:ro,z \
${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="gzip --rsyncable" -Pcvpf /backup/backup_postfix.tar.gz /postfix ${DEBIAN_DOCKER_IMAGE} /bin/tar --warning='no-file-ignored' --use-compress-program="pigz --rsyncable -p ${THREADS}" -Pcvpf /backup/backup_postfix.tar.gz /postfix
;;& ;;&
mysql|all) mysql|all)
SQLIMAGE=$(grep -iEo '(mysql|mariadb)\:.+' ${COMPOSE_FILE}) SQLIMAGE=$(grep -iEo '(mysql|mariadb)\:.+' ${COMPOSE_FILE})
@ -191,7 +200,7 @@ function restore() {
docker run -it --name mailcow-backup --rm \ docker run -it --name mailcow-backup --rm \
-v ${RESTORE_LOCATION}:/backup:z \ -v ${RESTORE_LOCATION}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_vmail-vol-1$):/vmail:z \ -v $(docker volume ls -qf name=^${CMPS_PRJ}_vmail-vol-1$):/vmail:z \
${DEBIAN_DOCKER_IMAGE} /bin/tar -Pxvzf /backup/backup_vmail.tar.gz ${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_vmail.tar.gz
docker start $(docker ps -aqf name=dovecot-mailcow) docker start $(docker ps -aqf name=dovecot-mailcow)
echo echo
echo "In most cases it is not required to run a full resync, you can run the command printed below at any time after testing wether the restore process broke a mailbox:" echo "In most cases it is not required to run a full resync, you can run the command printed below at any time after testing wether the restore process broke a mailbox:"
@ -210,7 +219,7 @@ function restore() {
docker run -it --name mailcow-backup --rm \ docker run -it --name mailcow-backup --rm \
-v ${RESTORE_LOCATION}:/backup:z \ -v ${RESTORE_LOCATION}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_redis-vol-1$):/redis:z \ -v $(docker volume ls -qf name=^${CMPS_PRJ}_redis-vol-1$):/redis:z \
${DEBIAN_DOCKER_IMAGE} /bin/tar -Pxvzf /backup/backup_redis.tar.gz ${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_redis.tar.gz
docker start $(docker ps -aqf name=redis-mailcow) docker start $(docker ps -aqf name=redis-mailcow)
;; ;;
crypt) crypt)
@ -218,7 +227,7 @@ function restore() {
docker run -it --name mailcow-backup --rm \ docker run -it --name mailcow-backup --rm \
-v ${RESTORE_LOCATION}:/backup:z \ -v ${RESTORE_LOCATION}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_crypt-vol-1$):/crypt:z \ -v $(docker volume ls -qf name=^${CMPS_PRJ}_crypt-vol-1$):/crypt:z \
${DEBIAN_DOCKER_IMAGE} /bin/tar -Pxvzf /backup/backup_crypt.tar.gz ${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_crypt.tar.gz
docker start $(docker ps -aqf name=dovecot-mailcow) docker start $(docker ps -aqf name=dovecot-mailcow)
;; ;;
rspamd) rspamd)
@ -226,7 +235,7 @@ function restore() {
docker run -it --name mailcow-backup --rm \ docker run -it --name mailcow-backup --rm \
-v ${RESTORE_LOCATION}:/backup:z \ -v ${RESTORE_LOCATION}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_rspamd-vol-1$):/rspamd:z \ -v $(docker volume ls -qf name=^${CMPS_PRJ}_rspamd-vol-1$):/rspamd:z \
${DEBIAN_DOCKER_IMAGE} /bin/tar -Pxvzf /backup/backup_rspamd.tar.gz ${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_rspamd.tar.gz
docker start $(docker ps -aqf name=rspamd-mailcow) docker start $(docker ps -aqf name=rspamd-mailcow)
;; ;;
postfix) postfix)
@ -234,7 +243,7 @@ function restore() {
docker run -it --name mailcow-backup --rm \ docker run -it --name mailcow-backup --rm \
-v ${RESTORE_LOCATION}:/backup:z \ -v ${RESTORE_LOCATION}:/backup:z \
-v $(docker volume ls -qf name=^${CMPS_PRJ}_postfix-vol-1$):/postfix:z \ -v $(docker volume ls -qf name=^${CMPS_PRJ}_postfix-vol-1$):/postfix:z \
${DEBIAN_DOCKER_IMAGE} /bin/tar -Pxvzf /backup/backup_postfix.tar.gz ${DEBIAN_DOCKER_IMAGE} /bin/tar --use-compress-program="pigz -d -p ${THREADS}" -Pxvf /backup/backup_postfix.tar.gz
docker start $(docker ps -aqf name=postfix-mailcow) docker start $(docker ps -aqf name=postfix-mailcow)
;; ;;
mysql|mariadb) mysql|mariadb)

View File

@ -1,6 +1,6 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
MASTER="en" MASTER="en-gb"
DIR = "#{__dir__}/.." DIR = "#{__dir__}/.."