| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- #!/usr/bin/env bash
- # =============================================================================
- # FranklinWH Toolbox - Caddy 安装与站点配置脚本
- # =============================================================================
- # 作用:安装 Caddy 2(缺失才装),并为本项目生成/部署一个站点 Caddyfile:
- # 1. 前端静态:/fwh-toolbox/* -> localhost:${CADDY_FRONTEND_PORT}(8300)
- # 2. 后端 API :/fwh-toolbox/api/* -> localhost:${CADDY_BACKEND_PORT}(8301)
- # 3. (可选)Casdoor:/casdoor/* -> localhost:${CADDY_CASDOOR_PORT}(8010)
- # 4. (可选)MinIO 控制台:/fwh-minio/* -> localhost:${CADDY_MINIO_CONSOLE_PORT}(9199)
- # 生成的片段风格参考线上 /etc/caddy/sites/002-lmt.zenpower.cc.caddy。
- #
- # 站点文件默认写入 /etc/caddy/sites/,并确保主 /etc/caddy/Caddyfile 里存在
- # `import sites/*.caddy`(若缺失则自动追加),最后 validate + reload。
- #
- # 前置条件:
- # - Ubuntu/Debian(apt)。其他发行版请参考 DEPLOYMENT.md 第 9 章手动安装。
- #
- # 环境变量:
- # CADDY_DOMAIN 对外域名(必填,如 lmt.zenpower.cc)
- # CADDY_TLS_MODE TLS 模式:internal | acme | manual (默认 internal)
- # CADDY_ACME_EMAIL acme 模式下的邮箱 (默认空)
- # CADDY_TLS_CERT manual 模式下证书路径 (默认空)
- # CADDY_TLS_KEY manual 模式下私钥路径 (默认空)
- # CADDY_SITES_DIR 站点片段目录 (默认 /etc/caddy/sites)
- # CADDY_MAIN_FILE 主配置文件 (默认 /etc/caddy/Caddyfile)
- # CADDY_FRONTEND_PORT 前端静态端口 (默认 8300)
- # CADDY_BACKEND_PORT 后端 API 端口 (默认 8301)
- # CADDY_INCLUDE_CASDOOR 是否加入 /casdoor 反代 (y/n) (默认 n)
- # CADDY_CASDOOR_PORT Casdoor 端口 (默认 8010)
- # CADDY_INCLUDE_MINIO 是否加入 /fwh-minio 反代 (y/n) (默认 n)
- # CADDY_MINIO_CONSOLE_PORT MinIO 控制台端口 (默认 9199)
- # CADDY_SERVE_STATIC 由 Caddy 直接 file_server 托管前端 (y/n) (默认 n)
- # CADDY_STATIC_ROOT file_server 的静态根目录 (默认 /var/www/fwh-toolbox/fwh-toolbox)
- # =============================================================================
- set -euo pipefail
- CADDY_DOMAIN="${CADDY_DOMAIN:-}"
- CADDY_TLS_MODE="${CADDY_TLS_MODE:-internal}"
- CADDY_ACME_EMAIL="${CADDY_ACME_EMAIL:-}"
- CADDY_TLS_CERT="${CADDY_TLS_CERT:-}"
- CADDY_TLS_KEY="${CADDY_TLS_KEY:-}"
- CADDY_SITES_DIR="${CADDY_SITES_DIR:-/etc/caddy/sites}"
- CADDY_MAIN_FILE="${CADDY_MAIN_FILE:-/etc/caddy/Caddyfile}"
- CADDY_FRONTEND_PORT="${CADDY_FRONTEND_PORT:-8300}"
- CADDY_BACKEND_PORT="${CADDY_BACKEND_PORT:-8301}"
- CADDY_INCLUDE_CASDOOR="${CADDY_INCLUDE_CASDOOR:-n}"
- CADDY_CASDOOR_PORT="${CADDY_CASDOOR_PORT:-8010}"
- CADDY_INCLUDE_MINIO="${CADDY_INCLUDE_MINIO:-n}"
- CADDY_MINIO_CONSOLE_PORT="${CADDY_MINIO_CONSOLE_PORT:-9199}"
- CADDY_SERVE_STATIC="${CADDY_SERVE_STATIC:-n}"
- CADDY_STATIC_ROOT="${CADDY_STATIC_ROOT:-/var/www/fwh-toolbox/fwh-toolbox}"
- c_reset='\033[0m'; c_blue='\033[1;34m'; c_green='\033[1;32m'; c_yellow='\033[1;33m'; c_red='\033[1;31m'
- info() { echo -e "${c_blue}[INFO]${c_reset} $*"; }
- ok() { echo -e "${c_green}[ OK ]${c_reset} $*"; }
- warn() { echo -e "${c_yellow}[WARN]${c_reset} $*"; }
- err() { echo -e "${c_red}[FAIL]${c_reset} $*" >&2; }
- has() { command -v "$1" >/dev/null 2>&1; }
- # ---------------------------------------------------------------------------
- # 1. 安装 Caddy(缺失才装)
- # ---------------------------------------------------------------------------
- ensure_caddy() {
- if has caddy; then
- ok "Caddy 已安装:$(caddy version 2>/dev/null | head -n1)"
- return 0
- fi
- info "安装 Caddy 2 ..."
- if ! has apt-get; then
- err "当前系统未提供 apt-get,请参考 DEPLOYMENT.md 第 9 章手动安装 Caddy。"
- return 1
- fi
- sudo apt-get install -y debian-keyring debian-archive-keyring apt-transport-https curl gnupg
- curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
- | sudo gpg --dearmor --yes -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
- curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
- | sudo tee /etc/apt/sources.list.d/caddy-stable.list >/dev/null
- sudo apt-get update -y
- sudo apt-get install -y caddy
- sudo systemctl enable caddy >/dev/null 2>&1 || true
- ok "Caddy 安装完成:$(caddy version 2>/dev/null | head -n1)"
- }
- # ---------------------------------------------------------------------------
- # 2. 组装 tls 指令块
- # ---------------------------------------------------------------------------
- _tls_block() {
- case "${CADDY_TLS_MODE}" in
- internal)
- printf '%s' "tls internal"
- ;;
- acme)
- if [[ -n "${CADDY_ACME_EMAIL}" ]]; then
- printf '%s' "tls ${CADDY_ACME_EMAIL}"
- else
- # 走全局/默认 ACME,无需站点级 tls 指令
- printf ''
- fi
- ;;
- manual)
- if [[ -z "${CADDY_TLS_CERT}" || -z "${CADDY_TLS_KEY}" ]]; then
- err "TLS 模式=manual 需要同时提供 CADDY_TLS_CERT 与 CADDY_TLS_KEY"
- return 1
- fi
- printf '%s' "tls ${CADDY_TLS_CERT} ${CADDY_TLS_KEY}"
- ;;
- *)
- err "未知 CADDY_TLS_MODE:${CADDY_TLS_MODE}(可选 internal|acme|manual)"
- return 1
- ;;
- esac
- }
- # ---------------------------------------------------------------------------
- # 3. 生成站点 Caddyfile
- # ---------------------------------------------------------------------------
- write_site() {
- [[ -n "${CADDY_DOMAIN}" ]] || { err "CADDY_DOMAIN 不能为空"; return 1; }
- sudo mkdir -p "${CADDY_SITES_DIR}"
- local site_file="${CADDY_SITES_DIR}/fwh-toolbox.caddy"
- local tls_directive; tls_directive="$(_tls_block)" || return 1
- # 组装 tls 行(为空则不输出该行)
- local tls_line=""
- [[ -n "${tls_directive}" ]] && tls_line=$'\t'"${tls_directive}"$'\n'
- info "生成站点配置:${site_file}"
- # 前端处理块:file_server 直托管 或 反代到静态端口
- local frontend_block
- if [[ "${CADDY_SERVE_STATIC}" == "y" ]]; then
- frontend_block=$(cat <<EOF
- # 前端静态:由 Caddy 直接托管
- reverse_proxy /fwh-toolbox/api/* localhost:${CADDY_BACKEND_PORT}
- handle_path /fwh-toolbox/* {
- root * ${CADDY_STATIC_ROOT}
- try_files {path} /index.html
- file_server
- }
- EOF
- )
- else
- frontend_block=$(cat <<EOF
- # fwh-toolbox 前端 + API
- handle /fwh-toolbox/* {
- # API:/fwh-toolbox/api/* -> 后端 ${CADDY_BACKEND_PORT}(去掉前缀)
- handle_path /fwh-toolbox/api/* {
- reverse_proxy localhost:${CADDY_BACKEND_PORT}
- }
- # 其余:前端静态 ${CADDY_FRONTEND_PORT}
- reverse_proxy localhost:${CADDY_FRONTEND_PORT}
- }
- EOF
- )
- fi
- # 可选:Casdoor
- local casdoor_block=""
- if [[ "${CADDY_INCLUDE_CASDOOR}" == "y" ]]; then
- casdoor_block=$(cat <<EOF
- # casdoor login
- redir /casdoor /casdoor/ 301
- handle_path /casdoor/* {
- reverse_proxy localhost:${CADDY_CASDOOR_PORT}
- }
- EOF
- )
- fi
- # 可选:MinIO 控制台
- local minio_block=""
- if [[ "${CADDY_INCLUDE_MINIO}" == "y" ]]; then
- minio_block=$(cat <<EOF
- # minio 控制台
- redir /fwh-minio /fwh-minio/ 301
- handle_path /fwh-minio/* {
- reverse_proxy localhost:${CADDY_MINIO_CONSOLE_PORT}
- }
- EOF
- )
- fi
- # 写入站点文件(用 sudo tee,内容经 heredoc 组装)
- sudo tee "${site_file}" >/dev/null <<EOF
- ${CADDY_DOMAIN} {
- ${tls_line} encode zstd gzip
- # 前端 redirect(补斜杠)
- redir /fwh-toolbox /fwh-toolbox/ 301
- ${frontend_block}
- ${casdoor_block}
- ${minio_block}
- }
- EOF
- ok "已生成站点配置:${site_file}"
- }
- # ---------------------------------------------------------------------------
- # 4. 确保主 Caddyfile 引入 sites/*.caddy
- # ---------------------------------------------------------------------------
- ensure_import() {
- local import_line="import sites/*.caddy"
- if [[ ! -f "${CADDY_MAIN_FILE}" ]]; then
- info "主配置 ${CADDY_MAIN_FILE} 不存在,创建并写入 import 指令"
- sudo mkdir -p "$(dirname "${CADDY_MAIN_FILE}")"
- echo "${import_line}" | sudo tee "${CADDY_MAIN_FILE}" >/dev/null
- return 0
- fi
- if sudo grep -qE '^\s*import\s+sites/\*\.caddy' "${CADDY_MAIN_FILE}"; then
- ok "主配置已包含 import sites/*.caddy"
- else
- info "在主配置追加:${import_line}"
- echo "" | sudo tee -a "${CADDY_MAIN_FILE}" >/dev/null
- echo "${import_line}" | sudo tee -a "${CADDY_MAIN_FILE}" >/dev/null
- fi
- }
- # ---------------------------------------------------------------------------
- # 5. 校验并重载
- # ---------------------------------------------------------------------------
- validate_reload() {
- info "校验 Caddy 配置 ..."
- if sudo caddy validate --config "${CADDY_MAIN_FILE}" >/dev/null 2>&1; then
- ok "配置校验通过"
- else
- warn "配置校验未通过,输出详细信息:"
- sudo caddy validate --config "${CADDY_MAIN_FILE}" || true
- err "请检查后手动 reload:sudo systemctl reload caddy"
- return 1
- fi
- info "重载 Caddy ..."
- if sudo systemctl reload caddy >/dev/null 2>&1; then
- ok "Caddy 已重载"
- else
- warn "reload 失败,尝试 restart ..."
- sudo systemctl restart caddy && ok "Caddy 已重启" || { err "Caddy 启动失败,请查看 journalctl -u caddy"; return 1; }
- fi
- }
- main() {
- info "== 安装/配置 Caddy 反向代理 =="
- [[ -n "${CADDY_DOMAIN}" ]] || { err "必须提供 CADDY_DOMAIN(对外域名)"; exit 1; }
- ensure_caddy
- write_site
- ensure_import
- validate_reload
- cat <<EOF
- ${c_green}Caddy 反向代理配置完成${c_reset}
- 域名 : ${CADDY_DOMAIN}
- TLS 模式 : ${CADDY_TLS_MODE}
- 站点文件 : ${CADDY_SITES_DIR}/fwh-toolbox.caddy
- 前端入口 : https://${CADDY_DOMAIN}/fwh-toolbox/
- 后端 API : https://${CADDY_DOMAIN}/fwh-toolbox/api/ (-> localhost:${CADDY_BACKEND_PORT})
- $( [[ "${CADDY_INCLUDE_CASDOOR}" == "y" ]] && echo " Casdoor : https://${CADDY_DOMAIN}/casdoor/ (-> localhost:${CADDY_CASDOOR_PORT})" )
- $( [[ "${CADDY_INCLUDE_MINIO}" == "y" ]] && echo " MinIO 控制台 : https://${CADDY_DOMAIN}/fwh-minio/ (-> localhost:${CADDY_MINIO_CONSOLE_PORT})" )
- 详情参考 : deploy/DEPLOYMENT.md 第 9 章
- EOF
- }
- main "$@"
|