-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmpcontrol
More file actions
executable file
·236 lines (206 loc) · 6.67 KB
/
Copy pathmpcontrol
File metadata and controls
executable file
·236 lines (206 loc) · 6.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
#!/bin/sh
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$SCRIPT_DIR"
# Load .env if present
if [ -f .env ]; then
set -a
. ./.env
set +a
fi
COMPOSE_FLAGS=""
if [ "${ENABLE_ROUNDCUBE}" = "true" ]; then
COMPOSE_FLAGS="--profile roundcube"
fi
usage() {
cat <<EOF
Usage: ./mpcontrol <command> [args]
Commands:
init Create .env and accounts.yml from templates
build Build Docker images
rebuild Rebuild images without cache (use after base image updates)
up Build images and start all services
down Stop all services
restart Stop and start all services
reindex [mailbox...] Rebuild Dovecot text search indexes (all accounts, or selected mailboxes only)
logs Tail logs from all services
export <file> Stop services and pack all data into archive
import <file> Unpack archive into project directory (does not start services)
clean Stop services and permanently delete all private data
EOF
}
cmd_init() {
echo "Initializing mailproxy in $SCRIPT_DIR"
if [ -f .env ]; then
echo ".env already exists"
elif [ -f .env.example ]; then
cp .env.example .env
echo "Created .env from .env.example"
else
echo "No .env.example found — create .env manually"
fi
if [ -f accounts.yml ]; then
echo "accounts.yml already exists"
elif [ -f accounts.example.yml ]; then
cp accounts.example.yml accounts.yml
echo "Created accounts.yml from accounts.example.yml"
else
echo "No accounts.example.yml found — create accounts.yml manually"
fi
chmod 600 accounts.yml .env 2>/dev/null || true
mkdir -p maildata
chmod 700 maildata || true
echo "Ensured maildata/ exists with restricted permissions"
mkdir -p certs
echo "Ensured certs/ exists"
echo
echo "Next steps:"
echo " 1) Edit accounts.yml with your credentials"
echo " 2) Edit .env if needed (e.g. set ENABLE_ROUNDCUBE=true)"
echo " 3) Run: ./mpcontrol up"
echo " 4) Follow logs: ./mpcontrol logs"
}
cmd_build() {
# shellcheck disable=SC2086
docker compose $COMPOSE_FLAGS build
}
cmd_rebuild() {
# shellcheck disable=SC2086
docker compose $COMPOSE_FLAGS build --no-cache
}
cmd_up() {
# shellcheck disable=SC2086
docker compose $COMPOSE_FLAGS build
# shellcheck disable=SC2086
docker compose $COMPOSE_FLAGS up -d --remove-orphans
}
is_true() {
val=$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')
case "$val" in
true|1|yes|on) return 0 ;;
*) return 1 ;;
esac
}
accounts_mailboxes() {
if [ ! -f accounts.yml ]; then
return
fi
awk '
/^[[:space:]]*-[[:space:]]*address:[[:space:]]*/ {
v = $0
sub(/^[[:space:]]*-[[:space:]]*address:[[:space:]]*/, "", v)
gsub(/[[:space:]]+$/, "", v)
if (v != "") print v
next
}
/^[[:space:]]*address:[[:space:]]*/ {
v = $0
sub(/^[[:space:]]*address:[[:space:]]*/, "", v)
gsub(/[[:space:]]+$/, "", v)
if (v != "") print v
}
' accounts.yml | awk '!seen[$0]++'
}
cmd_reindex() {
if is_true "${DOVECOT_FTS:-false}"; then
echo "DOVECOT_FTS=${DOVECOT_FTS:-false}: FTS indexing is enabled."
else
echo "DOVECOT_FTS=${DOVECOT_FTS:-false}: FTS is disabled."
echo "Reindex will refresh base Dovecot mailbox indexes only (no full-text FTS acceleration)."
fi
if [ "$#" -eq 0 ]; then
mailboxes=$(accounts_mailboxes)
if [ -z "$mailboxes" ]; then
echo "Error: no account mailboxes found in accounts.yml" >&2
exit 1
fi
echo "Reindexing all folders for account mailboxes from accounts.yml..."
for mailbox in $mailboxes; do
echo "Reindexing all folders for ${mailbox}..."
# shellcheck disable=SC2086
docker compose $COMPOSE_FLAGS exec -T dovecot doveadm force-resync -u "$mailbox" "*"
# shellcheck disable=SC2086
docker compose $COMPOSE_FLAGS exec -T dovecot doveadm index -u "$mailbox" "*"
done
echo "Done."
return
fi
for mailbox in "$@"; do
echo "Reindexing all folders for ${mailbox}..."
# shellcheck disable=SC2086
docker compose $COMPOSE_FLAGS exec -T dovecot doveadm force-resync -u "$mailbox" "*"
# shellcheck disable=SC2086
docker compose $COMPOSE_FLAGS exec -T dovecot doveadm index -u "$mailbox" "*"
done
echo "Done."
}
cmd_down() {
# shellcheck disable=SC2086
docker compose $COMPOSE_FLAGS down
}
cmd_logs() {
# Redirect both stdout and stderr to /dev/tty so output is visible even
# when the script itself is called inside a pipeline or non-tty context.
# shellcheck disable=SC2086
docker compose $COMPOSE_FLAGS logs -f --tail=200 >/dev/tty 2>/dev/tty
}
cmd_restart() {
cmd_down
cmd_up
}
cmd_export() {
FILE="$1"
if [ -z "$FILE" ]; then
echo "Error: file argument required." >&2
echo "Usage: ./mpcontrol export <file.tar.gz>" >&2
exit 1
fi
cmd_down
tar -czf "$FILE" certs/ logs/ maildata/ rcdata/ accounts.yml .env
echo "Exported to $FILE"
echo "Start servers again with: ./mpcontrol up"
}
cmd_import() {
FILE="$1"
if [ -z "$FILE" ]; then
echo "Error: file argument required." >&2
echo "Usage: ./mpcontrol import <file.tar.gz>" >&2
exit 1
fi
echo "Clearing logs/, maildata/, rcdata/ and certs/ ..."
rm -rf logs maildata rcdata certs
tar -xzf "$FILE" --no-same-owner
echo "Imported from $FILE"
echo "Start servers with: ./mpcontrol up"
}
cmd_clean() {
echo "WARNING: This will permanently delete all mail, credentials and config."
echo " logs/, maildata/, rcdata/, certs/, accounts.yml, .env will be removed."
printf 'Type YES (uppercase) to confirm: '
read -r answer
if [ "$answer" != "YES" ]; then
echo "Aborted."
exit 1
fi
# shellcheck disable=SC2086
docker compose $COMPOSE_FLAGS down 2>/dev/null || true
rm -rf logs maildata rcdata certs
rm -f accounts.yml .env
echo "Done. Run './mpcontrol init' to start fresh."
}
COMMAND="${1:-}"
if [ $# -gt 0 ]; then shift; fi
case "$COMMAND" in
init) cmd_init ;;
build) cmd_build ;;
rebuild) cmd_rebuild ;;
up) cmd_up ;;
reindex) cmd_reindex "$@" ;;
down) cmd_down ;;
logs) cmd_logs ;;
restart) cmd_restart ;;
export) cmd_export "$@" ;;
import) cmd_import "$@" ;;
clean) cmd_clean ;;
*) usage; exit 1 ;;
esac