#!/usr/bin/env bash # ============================================================================= # FranklinWH Toolbox - Caddy 安装与站点配置脚本 # ============================================================================= # 作用:部署自编译 Caddy(含 spaceship DNS 插件)并配置本项目站点: # 1. 拷贝项目内自编译二进制 deploy/bin/caddy -> /usr/bin/caddy # 2. 安装/更新 systemd 服务(参考 caddy-new.service:注入 spaceship DNS token) # 3. 生成站点 Caddyfile(TLS 走 spaceship DNS-01,风格同线上 002-*.caddy): # - 前端静态:/fwh-toolbox/* -> localhost:${CADDY_FRONTEND_PORT}(8300) # - 后端 API :/fwh-toolbox/api/* -> localhost:${CADDY_BACKEND_PORT}(8301) # - (可选)Casdoor:/casdoor/* -> localhost:${CADDY_CASDOOR_PORT}(8010) # - (可选)MinIO 控制台:/fwh-minio/* -> localhost:${CADDY_MINIO_CONSOLE_PORT}(9199) # # 站点文件默认写入 /etc/caddy/sites/,并确保主 /etc/caddy/Caddyfile 里存在 # `import sites/*.caddy`(若缺失则自动追加),最后 validate + reload。 # # 前置条件: # - 项目内已放置自编译 caddy 二进制:deploy/bin/caddy(内置 dns.providers.spaceship) # - DNS 托管在 spaceship,并已申请 API key/secret # # 环境变量: # CADDY_DOMAIN 对外域名(必填,如 lmt.zenpower.cc) # CADDY_BIN_SRC 自编译二进制来源路径 (默认 <脚本目录>/../bin/caddy) # CADDY_BIN_DST 目标机二进制安装路径 (默认 /usr/bin/caddy) # CADDY_SERVICE_FILE systemd 服务文件路径 (默认 /lib/systemd/system/caddy.service) # LIBDNS_SPACESHIP_APIKEY spaceship DNS API Key(必填,写入 systemd 服务) # LIBDNS_SPACESHIP_APISECRET spaceship DNS API Secret(必填,写入 systemd 服务) # CADDY_DNS_RESOLVER DNS 传播检查解析器 (默认 8.8.8.8) # 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 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" CADDY_DOMAIN="${CADDY_DOMAIN:-}" CADDY_BIN_SRC="${CADDY_BIN_SRC:-${SCRIPT_DIR}/../bin/caddy}" CADDY_BIN_DST="${CADDY_BIN_DST:-/usr/bin/caddy}" CADDY_SERVICE_FILE="${CADDY_SERVICE_FILE:-/lib/systemd/system/caddy.service}" LIBDNS_SPACESHIP_APIKEY="${LIBDNS_SPACESHIP_APIKEY:-}" LIBDNS_SPACESHIP_APISECRET="${LIBDNS_SPACESHIP_APISECRET:-}" CADDY_DNS_RESOLVER="${CADDY_DNS_RESOLVER:-8.8.8.8}" 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 二进制(deploy/bin/caddy -> /usr/bin/caddy) # --------------------------------------------------------------------------- ensure_caddy() { if [[ ! -f "${CADDY_BIN_SRC}" ]]; then err "未找到自编译二进制:${CADDY_BIN_SRC}" err "请将含 spaceship 插件的 caddy 放到该路径(见 deploy/bin/caddy)。" return 1 fi info "部署自编译 Caddy:${CADDY_BIN_SRC} -> ${CADDY_BIN_DST}" # 若正在运行,先停以便覆盖二进制 sudo systemctl stop caddy >/dev/null 2>&1 || true sudo install -m 0755 "${CADDY_BIN_SRC}" "${CADDY_BIN_DST}" # 校验必需的 spaceship DNS 模块存在 if ! "${CADDY_BIN_DST}" list-modules 2>/dev/null | grep -q '^dns.providers.spaceship$'; then err "已安装的 caddy 缺少 dns.providers.spaceship 模块,无法用 spaceship DNS 签证书。" return 1 fi ok "Caddy 就绪:$(${CADDY_BIN_DST} version 2>/dev/null | head -n1)" } # --------------------------------------------------------------------------- # 1b. 安装/更新 systemd 服务(注入 spaceship DNS token) # 参考 caddy-new.service:User=caddy,run --environ,Environment 注入 token # --------------------------------------------------------------------------- install_service() { [[ -n "${LIBDNS_SPACESHIP_APIKEY}" ]] || { err "缺少 LIBDNS_SPACESHIP_APIKEY"; return 1; } [[ -n "${LIBDNS_SPACESHIP_APISECRET}" ]] || { err "缺少 LIBDNS_SPACESHIP_APISECRET"; return 1; } # 确保 caddy 用户存在 id caddy >/dev/null 2>&1 || sudo useradd --system --home /var/lib/caddy --shell /usr/sbin/nologin caddy || true sudo mkdir -p /var/log/caddy /var/lib/caddy sudo chown -R caddy:caddy /var/log/caddy /var/lib/caddy 2>/dev/null || true info "写入 systemd 服务:${CADDY_SERVICE_FILE}" sudo tee "${CADDY_SERVICE_FILE}" >/dev/null </dev/null 2>&1 || true ok "systemd 服务已安装(token 已注入,文件权限 600)" } # --------------------------------------------------------------------------- # 2. 组装 tls 指令块(spaceship DNS-01,风格同线上 002-*.caddy) # token 通过 systemd Environment 注入,此处用 {env.XXX} 引用,不写明文。 # --------------------------------------------------------------------------- _tls_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_BIN_DST}" validate --config "${CADDY_MAIN_FILE}" >/dev/null 2>&1; then ok "配置校验通过" else warn "配置校验未通过,输出详细信息:" sudo "${CADDY_BIN_DST}" 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 install_service 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})" ) 提示 : systemd 服务含明文 DNS token,已设权限 600;勿提交到仓库。 详情参考 : deploy/DEPLOYMENT.md 第 9 章 EOF } main "$@"