Compare commits

...

44 Commits

Author SHA1 Message Date
Niklas Meyer
3028a18a37 Merge pull request #4819 from mailcow/staging
2022-10
2022-10-25 14:16:05 +02:00
Niklas Meyer
26a5fcf989 Merge pull request #4815 from ethrgeist:bump-redis-7
[redis] Bump Redis to version 7
2022-10-25 12:20:30 +02:00
Niklas Meyer
509086ef54 Merge pull request #4816 from mailcow/feature/rspamd-3.3
[RSPAMD] Update to 3.3
2022-10-25 11:42:34 +02:00
Niklas Meyer
963510ed22 Merge pull request #4806 from mailcow/feature/pigz-backup
[Backup] Swapped PIGZ instead of gzip (allow Threading)
2022-10-25 11:05:40 +02:00
DerLinkman
2c0f8cda50 [RSPAMD] Update to 3.3 2022-10-25 10:35:23 +02:00
DerLinkman
50d2671d75 Fixed leading / warning removal 2022-10-25 10:06:53 +02:00
DerLinkman
b73d879f3c Removed thread prompt again. Added notice message 2022-10-25 09:55:29 +02:00
Knuth
725a5fe5b9 Bump Redis to version 7 2022-10-25 09:47:03 +02:00
DerLinkman
65ca42ca42 Restored Thread Prompt due to implementation in restore 2022-10-24 15:10:15 +02:00
DerLinkman
b22ff59f7b Added PIGZ for Restoring as well. 2022-10-24 12:28:41 +02:00
DerLinkman
58527857d9 Removed debug message 2022-10-21 11:54:23 +02:00
DerLinkman
6306c4555c Removed Thread Prompt and set default value to 1 Thread 2022-10-21 11:48:29 +02:00
Peter
922603f906 Rename turkish language file for #4657 2022-10-20 17:57:13 +02:00
Niklas Meyer
f8d45de749 Merge pull request #4657 from tomy0000000:master
🌐 Add Traditional Chinese Translation
2022-10-20 11:27:58 +02:00
DerLinkman
cb1602c2de Fix English Flag rendering 2022-10-19 16:09:22 +02:00
DerLinkman
8026b6c874 Swapped PIGZ instead of gzip 2022-10-19 11:15:12 +02:00
Peter
042676fff7 [GH-Actions][actionpr] Update to v0.5.1 2022-10-12 18:27:30 +02:00
Peter
44d53146af [GH-Actions][stale] Update to v6.0.1 2022-10-12 18:26:39 +02:00
Niklas Meyer
ce4fb069d5 Merge pull request #4789 from mailcow/feat/prtonightlyaction
Update PR to nightly template
2022-10-09 17:26:17 +02:00
Peter
9444000d46 Use milkmaker as PR author
Use template to be able to use get_diff var
2022-10-09 17:02:28 +02:00
Peter
eacd9ac240 Add pr_to_nighty_template.yml 2022-10-09 17:01:21 +02:00
Tomy Hsieh
cf38d6ca69 🛠 fix: Improve language preference algo 2022-10-06 23:22:54 +08:00
Tomy Hsieh
905993d66e 🛠 fix: Language detection 2022-10-06 22:21:12 +08:00
DerLinkman
0d7fe2e347 Some corrections to pr action 2022-10-06 14:14:07 +02:00
DerLinkman
33bd871a63 Corrected some PR Action Code 2022-10-06 14:06:18 +02:00
DerLinkman
772122b255 Added auto PR for nightly builds 2022-10-06 14:04:27 +02:00
Niklas Meyer
9fb346751c Merge pull request #4724 from mnin/master
[Netfilter] Fix creating endless SNAT rules for ipv4
2022-10-06 12:18:23 +02:00
Tomy Hsieh
7d46de33d8 Merge from upstream branch 'staging'
# Conflicts:
#	data/web/inc/vars.inc.php
2022-09-30 16:03:49 +08:00
Martin Wilhelmi
f34d3620b1 Remove trailing whitespaces 2022-08-22 22:16:01 +02:00
Martin Wilhelmi
70e99447f9 Fix adding same SNAT rule endless to the ipv4 POSTROUTING chain 2022-08-22 22:15:56 +02:00
Tomy Hsieh
7b57b3392c switch to IETF language tag 2022-08-09 15:44:09 +08:00
Tomy Hsieh
492451bfee Tailor translation
`quarantine`, `success`
2022-08-09 15:10:44 +08:00
Tomy Hsieh
a1e8077f45 Tailor translation
`user`
2022-08-02 18:53:58 +08:00
Tomy Hsieh
956e4e2aa7 Tailor translation
`mailbox`
2022-08-01 08:20:02 +08:00
Tomy Hsieh
c2e0a275e1 Tailor translation
`edit`, `fido2`
2022-07-16 22:02:58 +08:00
Tomy Hsieh
514079fe96 Tailor translation
`danger`
2022-07-11 02:44:31 +08:00
Tomy Hsieh
27e9210d52 Tailor translation
`admin`
2022-07-07 22:07:02 +08:00
Tomy Hsieh
1b6e5b7116 Tailor translation
`add`
2022-07-06 13:33:18 +08:00
Tomy Hsieh
0dab215e27 Tailor translation
`debug`, `diagnostics`, `tfa`
2022-07-04 23:00:22 +08:00
Tomy Hsieh
6ec136e63f Tailor translation
`footer`, `header`, `info`, `login`, `oath2`, `ratelimit`, `start`, `warning`
2022-07-03 09:17:09 +08:00
Tomy Hsieh
bba5671eaf Tailor translation: acl 2022-07-03 01:18:34 +08:00
Tomy Hsieh
88d7593d89 Switch language key
zh_Hans -> zh-cn
zh_Hant -> zh-tw
2022-07-02 17:01:50 +08:00
Tomy Hsieh
bd23b80d45 Translation: Spacing
Add spaces between halfwidth and fullwidth characters
2022-07-02 13:06:13 +08:00
Tomy Hsieh
f96e0c4071 Adding Traditional Chinese Translation 2022-07-02 11:29:37 +08:00
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__}/.."