#!/usr/bin/env bash
# install.sh â YofMail ä¸é®å®è£èæ¬ï¼èªæç®¡ Docker é¨ç½²ï¼
#
# ç¨æ³ï¼åä¸ç¨æ·æ¨èï¼:
#   curl -fsSL https://raw.githubusercontent.com/One-OK/YofMail/main/install.sh | bash
#
# æåä¸è½½åæ§è¡ï¼æ¨èï¼å¯åå®¡æ¥èæ¬ï¼:
#   curl -fsSL https://raw.githubusercontent.com/One-OK/YofMail/main/install.sh -o install.sh
#   bash install.sh
#
# ç¯å¢åéï¼å¯éï¼æªè®¾ç½®åäº¤äºå¼è¯¢é®æä½¿ç¨é»è®¤å¼ï¼:
#   YOFMAIL_DOMAIN        å¯¹å¤è®¿é®çååï¼å¦ mail.example.com
#   YOFMAIL_INSTALL_DIR   å®è£ç®å½ï¼é»è®¤ /opt/yofmail
#   YOFMAIL_IMAGE_TAG     éåæ ç­¾ï¼é»è®¤ latest
#   YOFMAIL_CN_REGISTRY   å½åéåä»åºï¼å¦ ccr.ccs.tencentyun.com/yofmail
#                         ï¼çç©ºåä» GitHub ææºç æ¬å°æå»ºï¼éåº¦è¾æ¢ï¼
#   YOFMAIL_R2_ENDPOINT   R2/S3 endpointï¼å¯éï¼çç©ºåæ¬å°æä»¶å­å¨éä»¶ï¼
#   YOFMAIL_R2_KEY        R2 access key
#   YOFMAIL_R2_SECRET     R2 secret key
#   YOFMAIL_R2_BUCKET     R2 bucket å
#
# å±é©æä½å¼å³ï¼é»è®¤å¨é¨å³é­ï¼éæ¾å¼è®¾ä¸º 1 æçæï¼:
#   YOFMAIL_MODIFY_DOCKER=1  åè®¸æ¬èæ¬æ¹å /etc/docker/daemon.json å¹¶éå¯ Docker
#                            ï¼ä¼ç­æä¸­æ­æ¬å®¿ä¸»æºä¸çææå®¹å¨ï¼ä¸åªæ¯ YofMailï¼
#   YOFMAIL_FORCE_RESET=1    åè®¸ `git reset --hard` ä¸¢å¼å®è£ç®å½ä¸­çæ¬å°æ¹å¨
#
# èæ¬ç¹ç¹:
#   - èªå¨æ£æµå¹¶å®è£ Docker / docker composeï¼å®æ¹æº + SHA-256 æ ¡éªï¼
#   - èªå¨çæææå¿éçå¯é¥ï¼JWT_SECRET / MASTER_KEY / API_SHARED_SECRETï¼
#   - .env ä»¥ 0600 åå»ºï¼å¯é¥/è®¿é®ä»¤çä¸ä¼æå°å°ç»ç«¯
#   - å¹ç­ï¼å¯éå¤æ§è¡ï¼å·²å­å¨çéç½®ä¸è¦ç
#   - å®æåæå°è®¿é®å°ååä¸ä¸æ­¥æå¼
#
set -euo pipefail

# ââ å½ä»¤è¡åæ° ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# --license <KEY>  é¢ç½® YofMail æ¿æ´»ç ï¼åå§ååå¯¼å®æåæå¡ç«¯ä¼èªå¨ç»å®ï¼è£å®å³ç¨ã
#                  ä¸ä¼ åæåæµç¨ï¼æ¿æ´»ç çå¾ Web åå§ååå¯¼éæå¨å¡«å¥ã
YOFMAIL_LICENSE=""
while [[ $# -gt 0 ]]; do
  case "$1" in
    --license)
      if [[ $# -lt 2 ]]; then echo "éè¯¯: --license éè¦è·ä¸ä¸ªæ¿æ´»ç åæ°" >&2; exit 1; fi
      YOFMAIL_LICENSE="$2"; shift 2 ;;
    --license=*) YOFMAIL_LICENSE="${1#*=}"; shift ;;
    -h|--help)
      echo "ç¨æ³: bash install.sh [--license <æ¿æ´»ç >]"
      echo "  --license <KEY>   é¢ç½®æ¿æ´»ç ï¼åå§åå®æåèªå¨æ¿æ´»ï¼è£å®å³ç¨ï¼"
      echo "  -h, --help        æ¾ç¤ºæ¬å¸®å©"
      exit 0 ;;
    *) echo "è­¦å: å¿½ç¥æªç¥åæ° $1" >&2; shift ;;
  esac
done

# ââ é¢è²ä¸æ¥å¿ ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
if [[ -t 1 ]]; then
    C_RESET=$'\033[0m'; C_BOLD=$'\033[1m'; C_GREEN=$'\033[32m'
    C_YELLOW=$'\033[33m'; C_RED=$'\033[31m'; C_BLUE=$'\033[34m'
else
    C_RESET=""; C_BOLD=""; C_GREEN=""; C_YELLOW=""; C_RED=""; C_BLUE=""
fi

info()    { printf "%s[yofmail]%s %s\n"        "${C_BLUE}"   "${C_RESET}" "$*"; }
success() { printf "%s[yofmail]%s %s%s%s\n"    "${C_GREEN}"  "${C_RESET}" "${C_BOLD}" "$*" "${C_RESET}"; }
warn()    { printf "%s[yofmail]%s %s%s%s\n"    "${C_YELLOW}" "${C_RESET}" "${C_BOLD}" "$*" "${C_RESET}"; }
die()     { printf "%s[yofmail]%s %séè¯¯: %s%s\n" "${C_RED}" "${C_RESET}" "${C_BOLD}" "$*" "${C_RESET}"; exit 1; }

# ââ æ­¥éª¤æ é¢ ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
step() { printf "\n%sââ %s ââ%s\n" "${C_BOLD}${C_BLUE}" "$*" "${C_RESET}"; }

# ââ å·¥å·å½æ° ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
has() { command -v "$1" >/dev/null 2>&1; }

ask() {
    local prompt="$1" default="${2:-}" varname="$3" var
    [[ -z "$varname" ]] && die "ask: æªæä¾åéå"
    if [[ -n "${!varname:-}" ]]; then return; fi   # å·²éè¿ç¯å¢åéæä¾
    if [[ -n "$default" ]]; then
        read -r -p "$(printf '%s[yofmail]%s %s [%s]: ' "${C_BLUE}" "${C_RESET}" "$prompt" "$default")" var || true
        printf -v "$varname" '%s' "${var:-$default}"
    else
        read -r -p "$(printf '%s[yofmail]%s %s: ' "${C_BLUE}" "${C_RESET}" "$prompt")" var || die "ç¨æ·åæ¶"
        printf -v "$varname" '%s' "$var"
    fi
}

rand_b64() { openssl rand -base64 32 2>/dev/null || head -c 32 /dev/urandom | base64; }

# ââ é¢å±äº«è®¿é®ä»¤çï¼R6ï¼âââââââââââââââââââââââââââââââââââââââââââââââââââââââ
# å¹ç­ï¼è¥ .env å·²åå«éç©ºç ACCESS_TOKEN åè·³è¿ï¼å¦åçæ 32 å­èåå­è¿å¶ä»¤ç
# è¿½å å° .envï¼ä¾åç«¯éè¿ ?t=<token> çè®¿é®é¾æ¥åäººæºéªè¯ç»å½ã
#
# H-18ï¼ä»¤çæ¯è½ç»è¿ Turnstile çå­æ®ï¼**ç»ä¸æå°å° stdout**ãå®è£èæ¬çè¾åºä¼è¿
# CI æ¥å¿ãç»ç«¯ scrollbackã`tee` åºæ¥çå®è£æ¥å¿ï¼çè³è¢«åä¸»æºå¶ä»ç¨æ·çå°ã
# è¿éåªæå¸¦ä»¤ççè®¿é®é¾æ¥åè¿ 0600 çä¸æ¬¡æ§æä»¶ï¼æååªåè¯ç¨æ·æä»¶è·¯å¾ã
ACCESS_TOKEN_VALUE=""
ACCESS_URL_FILE=""
ensure_access_token() {
  if [[ -f "$ENV_FILE" ]] && grep -q '^ACCESS_TOKEN=.' "$ENV_FILE"; then
    info ".env å·²åå« ACCESS_TOKENï¼ä¿çç°æéç½®"
    ACCESS_TOKEN_VALUE=$(grep '^ACCESS_TOKEN=' "$ENV_FILE" | tail -n1 | cut -d= -f2-)
    return 0
  fi
  local token
  token=$(openssl rand -hex 32 2>/dev/null || head -c 64 /dev/urandom | xxd -p | tr -d '\n')
  [[ -z "$token" ]] && die "æ æ³çæ ACCESS_TOKEN"
  # .env å«å¯é¥ï¼è¿½å ååç¡®ä¿æéæ¶ç´§ï¼èé¨ç½²å¯è½éç 0644ï¼
  [[ -f "$ENV_FILE" ]] && chmod 600 "$ENV_FILE"
  # è¿½å å° .envï¼ä¸è¦çå¶ä»éç½®ï¼
  {
    echo ""
    echo "# ââ è®¿é®ä»¤çï¼å®è£æ¶çæï¼æ¶èå¸¦ ?t= çè®¿é®é¾æ¥å³å¯åäººæºéªè¯ï¼ââââââââ"
    echo "ACCESS_TOKEN=$token"
  } >> "$ENV_FILE"
  ACCESS_TOKEN_VALUE="$token"
  success "å·²çæ ACCESS_TOKEN å¹¶åå¥ .envï¼å¼ä¸ä¼æå°å°ç»ç«¯ï¼"
}

# æå¸¦ä»¤ççè®¿é®é¾æ¥åå¥ 0600 æä»¶ï¼åªåæ¾æä»¶è·¯å¾ï¼ä¸åæ¾ä»¤çæ¬èº«ã
write_access_url_file() {
  [[ -n "$ACCESS_TOKEN_VALUE" ]] || return 0
  local host="${YOFMAIL_DOMAIN:-localhost}"
  ACCESS_URL_FILE="$INSTALL_DIR/access-url.txt"
  install -m 600 /dev/null "$ACCESS_URL_FILE" 2>/dev/null || {
    umask 077
    : > "$ACCESS_URL_FILE"
    chmod 600 "$ACCESS_URL_FILE"
  }
  {
    echo "# YofMail åäººæºéªè¯è®¿é®é¾æ¥ï¼å«é¢å±äº«ä»¤çï¼ç­åå­æ®ï¼è¯·å¿è½¬å/æªå¾ï¼"
    echo "# æ¶èå°æµè§å¨åå»ºè®®å é¤æ¬æä»¶ï¼rm -f $ACCESS_URL_FILE"
    echo "http://${host}:8787/?t=${ACCESS_TOKEN_VALUE}"
  } > "$ACCESS_URL_FILE"
}

# ââ cloudflared å®è£ï¼R3ï¼å®¿ä¸»äºè¿å¶ï¼âââââââââââââââââââââââââââââââââââââââââ
# å¹ç­ï¼å·²å®è£åè·³è¿ãåªä» Cloudflare å®æ¹ GitHub Release ä¸è½½ï¼ä¸åèµ°
# ghproxy.com è¿ç±»ç¬¬ä¸æ¹è½¬åï¼è¯¥éåæ©å·²å¤±æï¼ä¸ä¼è®©ä»»æç¬¬ä¸æ¹å³å®ä½ å¨å®¿ä¸»æºä¸
# ä»¥ root æ§è¡çäºè¿å¶åå®¹ï¼ãä¸è½½åæ ¡éªå®æ¹åå¸ç SHA-256 æè¦ï¼ä»»ä½ä¸æ­¥å¤±è´¥é½
# ç«å³æ¾å¼å®è£èä¸æ¯ç»§ç»­è·ä¸ä¸ªæ¥æºä¸æ / åå®¹ä¸ºç©ºçäºè¿å¶ã
# è¥éèªå®ä¹æ¥æºï¼è¯·èªè¡ä¸è½½æ ¡éªåæ¾å° /usr/local/bin/cloudflared åéè·æ¬èæ¬ã
install_cloudflared() {
  if has cloudflared; then
    info "cloudflared å·²å®è£ï¼$(cloudflared version 2>/dev/null | head -n1 || echo '?')ï¼ï¼è·³è¿"
    return 0
  fi
  warn "æªæ£æµå° cloudflaredï¼å¼å§å®è£å° /usr/local/bin/cloudflared ..."

  local os arch base bin_url tmp="/tmp/cloudflared-$$.bin"
  os=$(uname -s | tr '[:upper:]' '[:lower:]')
  arch=$(uname -m)
  case "$arch" in
    x86_64|amd64) arch="amd64" ;;
    aarch64|arm64) arch="arm64" ;;
    *) warn "æªç¥æ¶æ $archï¼åé amd64"; arch="amd64" ;;
  esac
  base="https://github.com/cloudflare/cloudflared/releases/latest/download"
  bin_url="$base/cloudflared-${os}-${arch}"

  if ! curl -fsSL --retry 3 --retry-delay 2 "$bin_url" -o "$tmp"; then
    rm -f "$tmp"
    warn "cloudflared ä¸è½½å¤±è´¥ï¼å®æ¹æºä¸å¯è¾¾ï¼ï¼å¬ç½é§éåè½å°ä¸å¯ç¨ã"
    warn "è¯·æå¨ä» https://github.com/cloudflare/cloudflared/releases ä¸è½½å¹¶æ ¡éªåæ¾å° /usr/local/bin/cloudflaredã"
    return 1
  fi
  # ç©ºæä»¶ / HTML éè¯¯é¡µ ä¹å¯è½ä»¥ 200 è¿åï¼å¿é¡»æ¾å¼å¤å®
  if [[ ! -s "$tmp" ]]; then
    rm -f "$tmp"
    warn "cloudflared ä¸è½½åå®¹ä¸ºç©ºï¼æ¾å¼å®è£ã"
    return 1
  fi

  # æ ¡éªå®æ¹ SHA-256ï¼Cloudflare ä¸äºè¿å¶åç®å½åå¸ <name>.sha256ï¼
  if has sha256sum; then
    local want got
    want=$(curl -fsSL --retry 2 "${bin_url}.sha256" 2>/dev/null | awk '{print $1}' | head -n1 || true)
    if [[ -n "$want" ]]; then
      got=$(sha256sum "$tmp" | awk '{print $1}')
      if [[ "$want" != "$got" ]]; then
        rm -f "$tmp"
        warn "cloudflared æ ¡éªåä¸å¹éï¼ææ $wantï¼å®é $gotï¼ï¼æ¾å¼å®è£ã"
        return 1
      fi
      info "cloudflared SHA-256 æ ¡éªéè¿"
    else
      warn "æªè½è·åå®æ¹ SHA-256 æè¦ï¼è·³è¿æ ¡éªï¼è¯·èªè¡ç¡®è®¤äºè¿å¶æ¥æºï¼"
    fi
  else
    warn "æªæ£æµå° sha256sumï¼è·³è¿æ ¡éªï¼è¯·èªè¡ç¡®è®¤äºè¿å¶æ¥æºï¼"
  fi

  chmod +x "$tmp"
  mv "$tmp" /usr/local/bin/cloudflared
  if ! cloudflared --version >/dev/null 2>&1; then
    warn "cloudflared å®è£åæ æ³æ§è¡ --version æ ¡éªï¼å¬ç½é§éåè½å¯è½ä¸å¯ç¨"
    return 1
  fi
  success "cloudflared å®è£æåï¼$(cloudflared version | head -n1)ï¼"
}

# ââ éç½® ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
INSTALL_DIR="${YOFMAIL_INSTALL_DIR:-/opt/yofmail}"
IMAGE_TAG="${YOFMAIL_IMAGE_TAG:-latest}"
CN_REGISTRY="${YOFMAIL_CN_REGISTRY:-}"
REPO_URL="${YOFMAIL_REPO_URL:-https://github.com/One-OK/YofMail.git}"
# æ¥èªå½ä»¤è¡ --license çæ¿æ´»ç ï¼ç©ºåè¡¨ç¤ºä¸å¨èæ¬å®è£é¶æ®µé¢ç½®ï¼æ¹ç± Web åå¯¼æå¨å¡«å¥ï¼
LICENSE_KEY="${YOFMAIL_LICENSE:-}"
REPO_BRANCH="${YOFMAIL_REPO_BRANCH:-main}"

# ââ å¥å£æ£æ¥ ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
[[ "$(id -u)" -eq 0 ]] || die "è¯·ä½¿ç¨ root ç¨æ·è¿è¡ï¼æå  sudo: sudo bash install.sh"

step "YofMail ä¸é®å®è£"
info "å®è£ç®å½: $INSTALL_DIR"
info "éåæ ç­¾: $IMAGE_TAG"
[[ -n "$CN_REGISTRY" ]] && info "å½åéå: $CN_REGISTRY" || info "éåç­ç¥: ä»æºç æ¬å°æå»º"

# ââ æ­¥éª¤ 1: Docker ç¯å¢èªæ£ä¸èªå¨å®è£ ââââââââââââââââââââââââââââââââââââââââ
step "1/6 æ£æµ Docker ç¯å¢"

install_docker_compose() {
    # ä¼åå°è¯å®è£ docker-compose-pluginï¼Debian æäºçæ¬ä»åºæ²¡æè¯¥åï¼åä¸è½½ç¬ç«äºè¿å¶ã
    if docker compose version >/dev/null 2>&1 || has docker-compose; then
        return
    fi
    info "å°è¯å®è£ docker compose..."
    if has apt-get; then
        apt-get update -y
        apt-get install -y docker-compose-plugin 2>/dev/null && return
        warn "apt ä¸­æ  docker-compose-pluginï¼æ¹ä» GitHub ä¸è½½ç¬ç«äºè¿å¶..."
    fi
    # ä»ä» Docker å®æ¹ Release ä¸è½½ï¼ghproxy.com éåå·²å¤±æï¼ä¸ç¬¬ä¸æ¹è½¬åç­äº
    # è®©å«äººå³å®ä½ ä»¥ root æ§è¡çäºè¿å¶åå®¹ï¼ãä¸è½½å°ä¸´æ¶æä»¶ãæ ¡éªå®æ¹ SHA-256ã
    # ç¡®è®¤éç©ºåæè½å° /usr/local/binï¼ä»»ä½ä¸æ­¥å¤±è´¥é½ç´æ¥æ¾å¼èä¸æ¯çä¸åä¸ªæä»¶ã
    local compose_url="https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64"
    local tmp="/tmp/docker-compose-$$.bin"
    if ! curl -fsSL --retry 3 --retry-delay 2 "$compose_url" -o "$tmp" || [[ ! -s "$tmp" ]]; then
        rm -f "$tmp"
        die "docker compose ä¸è½½å¤±è´¥ï¼$compose_urlï¼ãè¯·æå¨å®è£ docker-compose-plugin åéè¯ã"
    fi
    if has sha256sum; then
        local want got
        want=$(curl -fsSL --retry 2 "${compose_url}.sha256" 2>/dev/null | awk '{print $1}' | head -n1 || true)
        if [[ -n "$want" ]]; then
            got=$(sha256sum "$tmp" | awk '{print $1}')
            if [[ "$want" != "$got" ]]; then
                rm -f "$tmp"
                die "docker compose æ ¡éªåä¸å¹éï¼ææ $wantï¼å®é $gotï¼ï¼å·²æ¾å¼å®è£ã"
            fi
        else
            warn "æªè½è·å docker compose å®æ¹ SHA-256 æè¦ï¼è·³è¿æ ¡éª"
        fi
    fi
    chmod +x "$tmp"
    mv "$tmp" /usr/local/bin/docker-compose
}

install_docker() {
    warn "æªæ£æµå° Dockerï¼å¼å§èªå¨å®è£..."
    if has apt-get; then
        # Debian / Ubuntu
        apt-get update -y
        apt-get install -y ca-certificates curl gnupg
        install -m 0755 -d /etc/apt/keyrings
        curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg \
            | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
        chmod a+r /etc/apt/keyrings/docker.gpg
        echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
$(. /etc/os-release; echo "$VERSION_CODENAME") stable" \
            > /etc/apt/sources.list.d/docker.list
        apt-get update -y
        apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin || install_docker_compose
    elif has yum; then
        # CentOS / RHEL / Rocky
        yum install -y yum-utils
        yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
        yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin || install_docker_compose
    else
        die "æ æ³è¯å«çåç®¡çå¨ï¼è¯·æå¨å®è£ Docker: https://docs.docker.com/engine/install/"
    fi
    systemctl enable --now docker
    success "Docker å·²å®è£å¹¶å¯å¨"
}

if ! has docker; then
    install_docker
elif ! docker info >/dev/null 2>&1; then
    warn "Docker daemon æªè¿è¡ï¼å°è¯å¯å¨..."
    systemctl start docker || service docker start || die "è¯·æå¨å¯å¨ Docker"
    success "Docker daemon å·²å¯å¨"
else
    success "Docker å·²å°±ç»ª"
fi

# ç¡®è®¤ docker compose å¯ç¨ï¼v2 plugin å½¢å¼ï¼
if docker compose version >/dev/null 2>&1; then
    COMPOSE="docker compose"
elif has docker-compose; then
    COMPOSE="docker-compose"
else
    die "æªæ¾å° docker compose æä»¶ï¼è¯·å®è£ docker-compose-plugin"
fi
info "compose å½ä»¤: $COMPOSE"

# éç½® Docker daemon å½åéåå éï¼å¹ç­ï¼**é»è®¤å³é­**ï¼
#
# H-20ï¼/etc/docker/daemon.json æ¯å®¿ä¸»æºå¨å±éç½®ï¼éå¯ dockerd ä¼ä¸­æ­è¯¥ä¸»æºä¸
# **ææ**å®¹å¨ï¼ä¸åªæ¯ YofMailï¼ãå®è£ä¸ä¸ªåºç¨ä¸åºè¯¥æè¿ç§å¯ä½ç¨ï¼å æ­¤æ¹ä¸ºæ¾å¼
# opt-inï¼åªæè®¾ç½® YOFMAIL_MODIFY_DOCKER=1 æ¶æä¼æ¹åå¹¶éå¯ Dockerã
setup_docker_mirror() {
    local daemon_file="/etc/docker/daemon.json"
    local mirror='{"registry-mirrors": ["https://mirror.ccs.tencentyun.com"]}'

    if [[ "${YOFMAIL_MODIFY_DOCKER:-0}" != "1" ]]; then
        if [[ ! -f "$daemon_file" ]] || ! grep -q 'registry-mirrors' "$daemon_file" 2>/dev/null; then
            warn "æªéç½® Docker registry éåå éï¼å½åç½ç»æååºç¡éåå¯è½å¾æ¢ï¼ã"
            warn "å¦éæ¬èæ¬ä»£ä¸ºéç½® $daemon_file å¹¶éå¯ Dockerï¼ä¼ç­æä¸­æ­å®¿ä¸»æºä¸ææå®¹å¨ï¼ï¼"
            warn "è¯·æ¾å¼è®¾ç½® YOFMAIL_MODIFY_DOCKER=1 åéè·ï¼ææå¨æ·»å ï¼"
            warn '  {"registry-mirrors": ["https://mirror.ccs.tencentyun.com"]}'
        fi
        return
    fi

    if [[ -f "$daemon_file" ]]; then
        if grep -q 'mirror.ccs.tencentyun.com' "$daemon_file"; then
            return
        fi
        warn "Docker daemon.json å·²å­å¨ï¼ä¸ºé¿åç ´åæ¢æéç½®ä¸åèªå¨åå¹¶ã"
        warn "è¯·æå¨æ·»å éåå é: https://mirror.ccs.tencentyun.com"
        return
    fi

    warn "YOFMAIL_MODIFY_DOCKER=1ï¼å°åå¥ $daemon_file å¹¶éå¯ Docker daemon"
    warn "ï¼è¿ä¼ç­æä¸­æ­æ¬å®¿ä¸»æºä¸çææå®¹å¨ï¼"
    mkdir -p "$(dirname "$daemon_file")"
    echo "$mirror" > "$daemon_file"
    systemctl restart docker || service docker restart || true
    success "Docker éåå éå·²éç½®"
}
setup_docker_mirror

# ââ æ­¥éª¤ 2: è·åæºç æå®è£ç®å½ ââââââââââââââââââââââââââââââââââââââââââââââââ
step "2/6 åå¤å®è£ç®å½"

ensure_code() {
    # å¦æå½åç®å½å·²ç»æ¯ YofMail ä»£ç ï¼æ docker-compose.yml å apps/api/Dockerfileï¼ï¼ç´æ¥ä½¿ç¨ã
    # åä¸ååæµåºæ¯ï¼ç§æä»åºæ æ³ curl ç´æ¥ä¸è½½ install.shï¼éå¸¸å scp/git clone å°å®è£ç®å½åæ§è¡ã
    if [[ -f "$INSTALL_DIR/docker-compose.yml" && -f "$INSTALL_DIR/apps/api/Dockerfile" ]]; then
        info "æ£æµå° $INSTALL_DIR å·²æ YofMail æºç ï¼ç´æ¥ä½¿ç¨"
        cd "$INSTALL_DIR"
        if [[ -d .git ]]; then
            info "å°è¯ git pull æ´æ°ï¼ä»å¿«è¿ï¼ä¸ä¼è¦çæ¬å°æ¹å¨ï¼..."
            git pull --ff-only origin "$REPO_BRANCH" 2>/dev/null \
                || warn "git pull æªå¿«è¿æåï¼å¯è½ææ¬å°æ¹å¨ï¼ï¼ç»§ç»­ä½¿ç¨å½åä»£ç "
        fi
        return 0
    fi
    return 1
}

if ensure_code; then
    : # å·²å¤ç
elif [[ -f "$INSTALL_DIR/docker-compose.yml" ]]; then
    info "æ£æµå°å·²æå®è£ï¼ä½ç¼ºå°å®æ´æºç ï¼æ§è¡æ´æ°æµç¨..."
    cd "$INSTALL_DIR"
    if [[ -d .git ]]; then
        git fetch --quiet origin "$REPO_BRANCH" 2>/dev/null || true
        # H-21ï¼ç»ä¸éé» `git reset --hard`ââé£ä¼æ å£°éæ¯è¿ç»´å¨æå¡å¨ä¸åçæ¬å°
        # ä¿®æ¹ï¼æ¹è¿ç compose/è¯ä¹¦/è¡¥ä¸ï¼ãé»è®¤åªåå¿«è¿åå¹¶ï¼å¿«è¿ä¸äºå°±ä¿çç°ç¶
        # å¹¶åè¯ç¨æ·æä¹å¤çãç¡®å®è¦ä¸¢å¼æ¬å°æ¹å¨æ¶æ¾å¼è®¾ YOFMAIL_FORCE_RESET=1ã
        if [[ "${YOFMAIL_FORCE_RESET:-0}" == "1" ]]; then
            warn "YOFMAIL_FORCE_RESET=1ï¼å°ä¸¢å¼ $INSTALL_DIR ä¸­æææ¬å°æªæäº¤æ¹å¨"
            git reset --hard "origin/$REPO_BRANCH" 2>/dev/null || warn "git æ´æ°å¤±è´¥ï¼ç»§ç»­ä½¿ç¨ç°æä»£ç "
            success "ä»£ç å·²å¼ºå¶éç½®å° origin/$REPO_BRANCH"
        elif git merge --ff-only "origin/$REPO_BRANCH" >/dev/null 2>&1; then
            success "ä»£ç å·²å¿«è¿æ´æ°å° origin/$REPO_BRANCH"
        else
            warn "æ æ³å¿«è¿æ´æ°ï¼å­å¨æ¬å°æ¹å¨æåæ¯å·²ååï¼ï¼ä¿çå½åä»£ç ä¸å¨ã"
            warn "è¯·æå¨å¤çï¼cd $INSTALL_DIR && git status"
            warn "ç¡®è®¤å¯ä»¥ä¸¢å¼æ¬å°æ¹å¨æ¶ï¼ç¨ YOFMAIL_FORCE_RESET=1 éè·æ¬èæ¬ã"
        fi
    else
        warn "$INSTALL_DIR ä¸æ¯ git ä»åºï¼è·³è¿ä»£ç æ´æ°"
    fi
else
    info "é¦æ¬¡å®è£ï¼åéä»åºå° $INSTALL_DIR ..."
    mkdir -p "$(dirname "$INSTALL_DIR")"
    if ! git clone --depth 1 -b "$REPO_BRANCH" "$REPO_URL" "$INSTALL_DIR"; then
        die "git clone å¤±è´¥ãç§æä»åºè¯·åçæ Personal Access Token æéç½® SSH key ååæ§è¡æ¬èæ¬ã"
    fi
    cd "$INSTALL_DIR"
    success "ä»£ç å·²åé"
fi

# ââ æ­¥éª¤ 3: çæ .env âââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
step "3/6 çæéç½®æä»¶ (.env)"

ENV_FILE="$INSTALL_DIR/.env"
if [[ -f "$ENV_FILE" ]] && grep -q "JWT_SECRET=." "$ENV_FILE"; then
    info ".env å·²å­å¨ä¸åå«å¯é¥ï¼ä¿çç°æéç½®"
else
    info "çææ°ç .env ä¸éæºå¯é¥..."

    ask "å¯¹å¤è®¿é®ååï¼å¦ mail.example.comï¼" "" YOFMAIL_DOMAIN
    [[ -n "${YOFMAIL_DOMAIN:-}" ]] || die "å¿é¡»æä¾å¯¹å¤è®¿é®åå"

    JWT_SECRET=$(rand_b64)
    MASTER_KEY=$(rand_b64)
    API_SHARED_SECRET=$(rand_b64)

    # H-19ï¼åä»¥ 0600 åå»ºç©ºæä»¶ï¼åå¾éåå¯é¥ã
    # ä¹åæ¯ `cat > .env` ç¶åæ `chmod 600`ï¼ä¸­é´å­å¨ä¸ä¸ª 0644ï¼æ umask å³å®ç
    # æ´å®½æ¾æéï¼çæ¶é´çªå£ï¼åä¸»æºä¸çä»»ä½ç¨æ·é½è½å¨è¿ä¸ªçªå£éè¯»èµ°å¨é¨å¯é¥ã
    install -m 600 /dev/null "$ENV_FILE" 2>/dev/null || {
        umask 077
        : > "$ENV_FILE"
        chmod 600 "$ENV_FILE"
    }

    cat > "$ENV_FILE" <<EOF
# YofMail éç½® â ç± install.sh èªå¨çæäº $(date -u +"%Y-%m-%dT%H:%M:%SZ")
# éæ°çæå¯é¥ä¼å¯¼è´å·²ç»å½ç¨æ·æçº¿ãå·²å å¯ç CF API Token æ æ³è§£å¯ã

# ââ æå¡ âââââââââââââââââââââââââââââââââââââââââââââ
PORT=8787
DATABASE_PATH=/app/data/yofmail.db

# ââ å¯é¥ï¼èªå¨çæï¼è¯·å¿å¤æ³ï¼âââââââââââââââââââââââââ
JWT_SECRET=$JWT_SECRET
MASTER_KEY=$MASTER_KEY
API_SHARED_SECRET=$API_SHARED_SECRET

# ââ Web / CORS âââââââââââââââââââââââââââââââââââââââ
PUBLIC_WEB_URL=https://$YOFMAIL_DOMAIN

# ââ R2/S3 å¯¹è±¡å­å¨ï¼å¯éï¼çç©ºåéä»¶å­æ¬å°ï¼âââââââââââââ
R2_ENDPOINT=${YOFMAIL_R2_ENDPOINT:-}
R2_REGION=auto
R2_ACCESS_KEY_ID=${YOFMAIL_R2_KEY:-}
R2_SECRET_ACCESS_KEY=${YOFMAIL_R2_SECRET:-}
R2_BUCKET=${YOFMAIL_R2_BUCKET:-yofmail-storage}
R2_PUBLIC_BASE=

# ââ è¡ä¸ºè°ä¼ ââââââââââââââââââââââââââââââââââââââââââ
SPAM_SCORE_THRESHOLD=0.7
LOG_LEVEL=info

# ââ å¯ééæ ââââââââââââââââââââââââââââââââââââââââââ
RESEND_API_KEY=
TURNSTILE_SECRET_KEY=
ALLOWED_INGEST_CIDRS=

# ââ æ¨ªåæ©å±ï¼å¯éï¼ââââââââââââââââââââââââââââââââââ
REDIS_URL=

# ââ éæåç«¯æç®¡ï¼ç± docker-compose èªå¨è®¾ç½®ï¼âââââââââ
WEB_DIST_DIR=/app/web/dist

# ââ æ¿æ´»ç ï¼install.sh --license é¢ç½®ï¼çç©ºåå¨ Web åå§ååå¯¼ä¸­æå¨å¡«å¥ï¼ââââââ
LICENSE_KEY=${LICENSE_KEY:-}
EOF
    # å¹ç­æ¶å°¾ï¼æä»¶å·²å¨åå¥åç½®ä¸º 0600ï¼è¿éåªæ¯é²å¾¡æ§å°åç¡®è®¤ä¸æ¬¡ã
    chmod 600 "$ENV_FILE"
    success ".env å·²çæ"
fi

# ç¡®ä¿é¢å±äº«è®¿é®ä»¤çå­å¨ï¼R6 åäººæºéªè¯ï¼
ensure_access_token

# ââ æ­¥éª¤ 4: æå»ºææåéå ââââââââââââââââââââââââââââââââââââââââââââââââââââ
step "4/6 åå¤ Docker éå"

if [[ -n "$CN_REGISTRY" ]]; then
    # å½åéåè·¯å¾ï¼ç´æ¥ pullï¼æ éæ¬å°æå»º
    export CN_IMAGE="$CN_REGISTRY/yofmail-api:$IMAGE_TAG"
    info "ä»å½åéåä»åºæå: $CN_IMAGE"
    if ! docker pull "$CN_IMAGE"; then
        die "æåéåå¤±è´¥ï¼è¯·æ£æ¥:
  1. å·²ç»å½è¾è®¯äº TCRï¼docker login $CN_REGISTRYï¼
  2. éåæ ç­¾ $IMAGE_TAG å­å¨
  3. ç½ç»å¯è¾¾"
    fi
    success "éåå·²å°±ç»ª: $CN_IMAGE"
else
    # æ¬å°æå»ºè·¯å¾
    info "æªéç½®å½åéåï¼å¼å§æ¬å°æå»ºï¼é¦æ¬¡è¾æ¢ï¼çº¦ 3-5 åéï¼..."
    info "æç¤º: å½åæå¡å¨æå»ºå¯è½å  npm registry è¾æ¢ï¼å¯éè·å¹¶è®¾ç½® YOFMAIL_CN_REGISTRY"

    # ä½¿ç¨å½å npm registry å éæå»º
    export NPM_MIRROR="https://registry.npmmirror.com"
    if ! docker build \
        --build-arg NPM_MIRROR="$NPM_MIRROR" \
        -f apps/api/Dockerfile \
        -t yofmail-api:"$IMAGE_TAG" \
        . ; then
        die "éåæå»ºå¤±è´¥ï¼è¯·æ£æ¥ç½ç»ææå¨æ§è¡: docker build -f apps/api/Dockerfile -t yofmail-api:$IMAGE_TAG ."
    fi

    # åæ¶æå»ºåç«¯
    if [[ -f apps/web/Dockerfile ]]; then
        info "æå»ºåç«¯éå..."
        docker build \
            --build-arg NPM_MIRROR="$NPM_MIRROR" \
            -f apps/web/Dockerfile \
            -t yofmail-web:"$IMAGE_TAG" \
            . || warn "åç«¯æå»ºå¤±è´¥ï¼å°ä»é¨ç½² API"
    fi
    success "éåæå»ºå®æ"
fi

# ââ æ­¥éª¤ 5: å¯å¨æå¡ ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
step "5/6 å¯å¨æå¡"

# åç¡®ä¿å®¿ä¸» cloudflared å°±ç»ªï¼R3ï¼ï¼compose ä¼æ /usr/local/bin/cloudflared åªè¯»æè½½è¿å®¹å¨
install_cloudflared || warn "cloudflared æªå°±ç»ªï¼å¬ç½è®¿é®åè½å°æä¸å¯ç¨ï¼å¯ç¨åæå¨å®è£ï¼"

export IMAGE_TAG
export CN_IMAGE="${CN_IMAGE:-}"
# éåå·²å¨ä¸æ¹æå»º/æåå®æï¼é¨åæ§ç buildx æ æ³éè¿ compose èªå¨ buildï¼æä½¿ç¨ --no-buildã
$COMPOSE up -d --no-build
success "æå¡å·²å¯å¨"

# ç­å¾å¥åº·æ£æ¥
info "ç­å¾ API å¥åº·æ£æ¥..."
for i in {1..30}; do
    if curl -sf "http://localhost:8787/health" >/dev/null 2>&1; then
        success "API å·²å°±ç»ª"
        break
    fi
    sleep 2
    [[ $i -eq 30 ]] && warn "API 30 ç§åæªååºå¥åº·æ£æ¥ï¼è¯·æ¥çæ¥å¿: $COMPOSE logs api"
done

# ââ æ­¥éª¤ 6: å®ææå¼ ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
step "6/6 å®è£å®æ"

# ä»¤çåªåå¥ 0600 æä»¶ï¼ç»ç«¯éåªåºç°æä»¶è·¯å¾ï¼H-18ï¼
write_access_url_file

if [[ -n "$LICENSE_KEY" ]]; then
  success "æ£æµå° --license æ¿æ´»ç ï¼å·²åå¥ .env ç LICENSE_KEYã"
  info "åå§ååå¯¼åå»ºç®¡çååä¼èªå¨ç»å®è¯¥æ¿æ´»ç ï¼æ éåæå¨ç²è´´ã"
else
  info "æªä¼ å¥ --licenseï¼æ¿æ´»ç å¯å¨ Web åå§ååå¯¼å®æåäºãæ¿æ´»ãé¡µæå¨å¡«å¥ã"
fi

cat <<EOF

${C_BOLD}${C_GREEN}âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ${C_RESET}

  YofMail å·²æåé¨ç½²ï¼

  ${C_BOLD}ä¸ä¸æ­¥:${C_RESET}

  1. ${C_BOLD}éç½®ååä»£çä¸ HTTPS${C_RESET}ï¼æ¨è nginx + certbotï¼:
       server_name ${YOFMAIL_DOMAIN:-mail.example.com};
       location / { proxy_pass http://127.0.0.1:8787; }

  2. ${C_BOLD}è®¿é® Web çé¢${C_RESET}:
       https://${YOFMAIL_DOMAIN:-mail.example.com}

  3. ${C_BOLD}é¦æ¬¡è®¿é®èªå¨è¿å¥åå§ååå¯¼ï¼åå»ºç®¡çåï¼ä¸»ï¼è´¦å·${C_RESET}
     ï¼å¬å¼æ³¨åå·²å³é­ï¼å­è´¦å·ç±ç®¡çåå¨"è®¾ç½® â ç¨æ·ç®¡ç"ä¸­åå»ºï¼

  4. ${C_BOLD}éç½® Cloudflare Email Routing${C_RESET}ï¼æ¥æ¶é®ä»¶å¿éï¼:
       - å¨æ¬å°ä»åº apps/email/wrangler.toml è®¾ç½®:
         API_INTERNAL_URL = "https://${YOFMAIL_DOMAIN:-mail.example.com}/internal"
       - wrangler secret put API_SHARED_SECRET  ï¼å¼ä¸ .env ä¸­ä¸è´ï¼
       - wrangler deploy é¨ç½² Email Worker
       - å¨ Cloudflare æ§å¶å°å¼å¯ Email Routing
       - æ·»å  catch-all è·¯ç± â è½¬åå° yofmail-email Worker

  ${C_BOLD}å¸¸ç¨å½ä»¤:${C_RESET}
    æ¥çæ¥å¿:   cd $INSTALL_DIR && $COMPOSE logs -f
    éå¯æå¡:   cd $INSTALL_DIR && $COMPOSE restart
    åæ­¢æå¡:   cd $INSTALL_DIR && $COMPOSE down
    æ´æ°çæ¬:   cd $INSTALL_DIR && bash install.sh   # éæ°æ§è¡å³å¯

  ${C_BOLD}è®¿é®ä»¤çï¼åäººæºéªè¯ï¼:${C_RESET}
    å¸¦ä»¤ççè®¿é®é¾æ¥å·²åå¥ä¸é¢è¿ä¸ªä»æ¬äººå¯è¯»ï¼0600ï¼çæä»¶ï¼åºäºå®å¨èè
    ä¸å¨ç»ç«¯åæ¾ï¼ç»ç«¯è¾åºä¼è¿æ¥å¿ãscrollbackãCI è®°å½ï¼ï¼
      ${ACCESS_URL_FILE:-$INSTALL_DIR/access-url.txt}
    æ¥ç:   cat ${ACCESS_URL_FILE:-$INSTALL_DIR/access-url.txt}
    æ¶èå°æµè§å¨åè¯·å é¤:   rm -f ${ACCESS_URL_FILE:-$INSTALL_DIR/access-url.txt}
    è¯¥é¾æ¥ç­åå­æ®ï¼è¯·å¿è½¬åãæªå¾ææäº¤å°ä»åºã

  ${C_BOLD}éç½®æä»¶:${C_RESET}  $ENV_FILE
  ${C_BOLD}æ°æ®ç®å½:${C_RESET}  Docker volume yofmail-data

${C_BOLD}${C_GREEN}âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ${C_RESET}

EOF
