#!/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 < 后端 ${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 </dev/null </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 < 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 "$@"