Files
clover-mail/scripts/compose-install.sh
T

84 lines
4.4 KiB
Bash

#!/usr/bin/env bash
# Optional Compose initialization container; .env is edited on the host first.
set -euo pipefail
cd "${CLOVER_HOME:?请通过 README 中的 Compose 命令传入安装目录}"
project="$(pwd -P)"
[[ "$project" != / && "$project" =~ ^/[a-zA-Z0-9_./-]+$ ]] || { echo '请使用不含空格或特殊字符的独立安装目录。' >&2; exit 1; }
test -f .env || { echo '请先复制 .env.example 为 .env 并编辑域名等参数。' >&2; exit 1; }
exec 9>.clover-compose-install.lock
flock -n 9 || { echo '已有 Compose 初始化任务正在执行。' >&2; exit 1; }
chmod 600 .env
set -a; . ./.env; set +a
compose() { docker compose -f compose.yaml "$@"; }
set_env() {
if grep -q "^${1}=" .env; then sed -i "s|^${1}=.*|${1}=${2}|" .env
else printf '%s=%s\n' "$1" "$2" >> .env; fi
}
for key in PANEL_HOSTNAME MAIL_HOSTNAME MAIL_DOMAIN; do
value="${!key:-}"
if ! [[ "$value" =~ ^[a-zA-Z0-9]([a-zA-Z0-9.-]*[a-zA-Z0-9])?\.[a-zA-Z]{2,}$ ]] || [[ "$value" = example.com || "$value" = *.example.com ]]; then
echo "请先在 .env 填写有效的 ${key}。" >&2; exit 1
fi
done
[[ "${ACME_EMAIL:-}" =~ ^[^[:space:]@]+@[^[:space:]@]+\.[^[:space:]@]+$ && "$ACME_EMAIL" != *@example.com ]] || { echo '请先在 .env 填写自己的 ACME_EMAIL。' >&2; exit 1; }
[[ "$PANEL_HOSTNAME" != "$MAIL_HOSTNAME" ]] || { echo '面板与邮件主机名必须不同。' >&2; exit 1; }
if { [[ "${CLOVER_SHARED_CADDY:-0}" = 1 || "${CLOVER_SHARED_NGINX:-0}" = 1 ]]; } && ! docker inspect clover-panel >/dev/null 2>&1; then
echo '此 Compose 安装入口适用于独占 Web 端口的服务器;共享宿主机入口请使用安装器。' >&2; exit 1
fi
for name in clover-panel docker-mailserver; do
if docker inspect "$name" >/dev/null 2>&1; then
previous="$(docker inspect "$name" --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}')"
[[ "$previous" = "$project" ]] || { echo "${name} 属于另一安装目录,已停止初始化。" >&2; exit 1; }
fi
done
if [[ -z "${SESSION_SECRET:-}" || "$SESSION_SECRET" = change-me-to-random-32+ ]]; then
set_env SESSION_SECRET "$(openssl rand -hex 32)"
elif [[ ${#SESSION_SECRET} -lt 16 ]]; then
echo 'SESSION_SECRET 至少需要 16 字符;已有实例请保留原密钥。' >&2; exit 1
fi
[[ -n "${RECONCILE_TOKEN:-}" ]] || set_env RECONCILE_TOKEN "$(openssl rand -hex 32)"
if [[ -z "${ROUNDCUBE_SSO_SECRET:-}" ]]; then
set_env ROUNDCUBE_SSO_SECRET "$(openssl rand -hex 32)"
elif [[ ${#ROUNDCUBE_SSO_SECRET} -lt 32 ]]; then
echo 'ROUNDCUBE_SSO_SECRET 至少需要 32 字符。' >&2; exit 1
fi
set_env CLOVER_HOME "$project"
memory_mb="$(awk '/MemTotal/{printf "%d", $2/1024}' /proc/meminfo)"
if [[ "$memory_mb" -lt 2000 && "${ENABLE_CLAMAV:-1}" = 1 ]]; then
set_env ENABLE_CLAMAV 0
echo '已根据服务器内存调整邮件防病毒服务配置。'
fi
if [[ -z "${SSL_TYPE:-}" ]]; then
set_env SSL_TYPE manual
set_env SSL_CERT_PATH /tmp/docker-mailserver/ssl/mail.crt
set_env SSL_KEY_PATH /tmp/docker-mailserver/ssl/mail.key
fi
# Compose interpolation must use the generated values, rather than the old
# exported template values inherited by this initialization container.
set -a; . ./.env; set +a
compose config --quiet
echo '正在拉取服务镜像…'
compose pull panel mail roundcube caddy
if [[ "$SSL_TYPE" = manual && "${SSL_CERT_PATH:-}" = /tmp/docker-mailserver/ssl/mail.crt && "${SSL_KEY_PATH:-}" = /tmp/docker-mailserver/ssl/mail.key ]]; then
echo '正在准备初始邮件 TLS 证书…'
compose run --rm --no-deps --entrypoint sh mail -c '
set -eu
mkdir -p /tmp/docker-mailserver/ssl
cert=/tmp/docker-mailserver/ssl/mail.crt
key=/tmp/docker-mailserver/ssl/mail.key
if [ ! -f "$cert" ] && [ ! -f "$key" ]; then
openssl req -x509 -newkey rsa:2048 -nodes -days 3650 -subj "/CN=$MAIL_HOSTNAME" -keyout "$key" -out "$cert" 2>/dev/null
chmod 600 "$key"
fi
test -s "$cert" && test -s "$key"
'
fi
echo '正在构建证书助手并启动服务…'
compose build certificate-helper
compose up -d --no-build mail panel roundcube caddy certificate-helper
bash scripts/admin-bootstrap.sh
bash scripts/configure-roundcube.sh
CLOVER_CLI_TARGET=/host-bin/clover bash scripts/install-cli.sh "$project"
printf '\n安装完成: https://%s/\n' "$PANEL_HOSTNAME"
echo '管理员凭据已在上方显示(已有管理员会保留)。初始邮件证书为自签时,请登录面板签发可信证书。'