# Caddy 部署辅助脚本

## 文件

用户**只跑** `install_caddy.sh`，不要直接执行 `lib/*`。

- `install_caddy.sh`：按 `binary` 或 `docker` 模式准备 Caddy 部署。
- `lib/caddy_helper.sh`：helper 模板（不要当安装脚本执行），由 `install_caddy.sh` 部署。
- `lib/shell_config.sh`：由 `install_caddy.sh` 使用的内部 shell hook 实现。
- `examples/compose.yaml`：示例，不是完整部署。

交互安装会询问是否把 helper source 片段写入当前用户的 shell rc。非交互安装默认写入，可用 `--no-configure-shell` 关闭。shell hook 指向部署后的 helper，而不是仓库里的模板文件。
如果后续 helper 发布地址变化，可以直接修改 `install_caddy.sh` 顶部的 `HELPER_SCRIPT_URL`。
如果后续内部 shell 配置库发布地址变化，可以修改 `SHELL_CONFIG_SCRIPT_URL`。

## 部署模式

### Binary

- 默认从上游 GitHub Releases 的 `latest` 重定向解析并下载最新版本。
- 二进制文件写入 `/usr/local/bin/caddy`。
- 配置文件写入 `/etc/caddy/Caddyfile`。
- 日志写入 `/etc/caddy/logs`。
- `/etc/caddy/state` 用于保存 Caddy 状态和证书数据。
- 默认不启用静态站点。
- 只有使用 `--enable-static-site` 时才会创建 `/etc/caddy/site`。
- 写入的 `/etc/systemd/system/caddy.service` 基于上游 `caddyserver/dist` 的 unit 文件。

生成的目录和文件：

- `/etc/caddy`：Caddy 主配置根目录。
- `/etc/caddy/Caddyfile`：脚本生成并由服务使用的 Caddyfile。
- `/etc/caddy/.caddy_helper.sh`：部署后的 helper 脚本，供 shell 启动文件加载。
- `/etc/caddy/.helper.env`：安装脚本生成的 helper 配置，目前用于保存首选编辑器。
- `/etc/caddy/logs`：运行日志和访问日志目录。
- `/etc/caddy/state`：Caddy 状态目录，用于保存证书、ACME 账户数据、续签状态和本地存储数据。
- `/etc/caddy/site`：可选静态站点目录，只有使用 `--enable-static-site` 时才会创建。
- `/usr/local/bin/caddy`：安装后的 Caddy 二进制文件。
- `/etc/systemd/system/caddy.service`：binary 模式生成的 systemd unit 文件。

### Docker

- 默认镜像为 `caddy:latest`。
- 默认根目录为 `~/projects/caddy`。
- 默认使用 `network_mode: host`，因为这里的 Docker 部署目标是 Linux 网关场景。
- `compose.yml` 写入 `~/projects/caddy/compose.yml`。
- GitOps 源配置写入 `~/projects/caddy/Caddyfile`。
- 将 `./Caddyfile` 只读 bind mount 到 `/etc/caddy/Caddyfile`，Git 管理的源文件就是运行时配置。
- 证书和运行数据保存在 `caddy_data`，镜像配置状态保存在 `caddy_config`，日志保存在 `caddy_logs`。
- 默认不启用静态站点。
- 只有使用 `--enable-static-site` 时才会把可选的 `caddy_site` named volume 挂载到 `/srv`。

`~/projects/caddy` 下生成的目录和文件：

- `Caddyfile`：保存在部署目录中的源 Caddyfile，并以只读方式 bind mount 到 `/etc/caddy/Caddyfile`。
- `.caddy_helper.sh`：部署后的 helper 脚本，供 shell 启动文件加载。
- `.helper.env`：安装脚本生成的 helper 配置，目前用于保存首选编辑器。
- `compose.yml`：脚本生成的 Docker Compose 文件。

生成的 Docker volumes：

- `caddy_data`：容器 `/data`，保存证书、ACME 账户、续签状态和 Caddy 管理的运行数据。
- `caddy_config`：容器 `/config`，保存官方 Docker 镜像使用的配置状态。
- `caddy_logs`：容器 `/var/log/caddy`，保存 `runtime.log` 和 `access.log`。
- `caddy_site`：可选的容器 `/srv` 静态站点 volume，只有使用 `--enable-static-site` 时才会创建。

## 安装

```bash
cd bitfennec_simple_page/http/linux/caddy
bash install_caddy.sh
```

请使用登录用户运行安装器，不要在整条命令前加 `sudo`。安装器只会在写系统目录时自行请求提权，这样 shell hook 才会写入目标用户的 rc 文件。

不带参数运行会进入交互式安装向导。向导会询问部署模式、Docker 根目录或 binary 版本、编辑器、是否启用静态站点、是否配置 shell，并在写文件前显示确认摘要。
自动化场景可以继续传参数，以非交互方式运行：

```bash
bash install_caddy.sh <binary|docker> [options]
```

| 参数 | 作用 | 默认值 |
| --- | --- | --- |
| `binary` / `docker` | 非交互安装时选择部署模式 | 交互式询问 |
| `--enable-static-site` | 创建并启用可选的静态站点目录 | 关闭 |
| `--editor <name>` | 设置 `caddy edit` 使用的编辑器 | `vim` |
| `--docker-root <path>` | 覆盖 Docker 根目录 | `~/projects/caddy` |
| `--version <x.y.z>` | 在 binary 模式固定 Caddy 版本 | 最新版 |
| `--configure-shell` | 写入当前用户的 shell hook | 开启 |
| `--no-configure-shell` | 不修改当前用户的 shell rc | 关闭 |

示例：

```bash
bash install_caddy.sh
bash install_caddy.sh binary
bash install_caddy.sh binary --enable-static-site --editor hx
bash install_caddy.sh docker --docker-root ~/projects/caddy-gateway
bash install_caddy.sh docker --no-configure-shell
```

安装后，重新开一个 shell，或者执行：

```bash
exec $SHELL -l
```

如果后续只需要重新写入 shell helper hook，仍使用同一个入口：

```bash
# Binary 模式
bash install_caddy.sh configure-shell binary

# Docker 模式
bash install_caddy.sh configure-shell docker --docker-root <docker-root>
```

## 命令

先在 bash 里加载包装脚本：

```bash
source /etc/caddy/.caddy_helper.sh
```

如果是 Docker 模式，则改为加载部署目录里的 helper：

```bash
source <docker-root>/.caddy_helper.sh
```

helper 现在会同时注册 bash 和 zsh 的补全。部署新版 helper 后，zsh 不再需要手动执行 `bashcompinit`。
`install_caddy.sh configure-shell` 会自动检测当前 shell：bash 写入 `~/.bashrc`，zsh 写入 `~/.zshrc`，fish 写入 `~/.config/fish/config.fish`，其他 shell 写入 `~/.profile`。Docker 模式下，它也会把选定的 Docker root 刷新到 `.helper.env`。
Docker 模式下，helper 会从当前目录或上级目录查找带有 `compose.yml` 或 `compose.yaml`，并且带有 `Caddyfile` 或 `.helper.env` 的部署目录。这样部署目录被复制或移动后，不会继续依赖最初安装时的路径。
Docker 源 `Caddyfile` 修改后，使用 `caddy reload`；容器会直接读取 bind-mounted 源文件。

### 移动 Docker 部署目录

生成的 Compose 文件会 bind mount `./Caddyfile`，所以运行时配置会跟随部署目录移动。证书、运行数据和日志在移动部署目录后仍留在 Docker volumes 里。它们不会跟着目录移动，需要单独备份。

按下面步骤移动目录：

```bash
cd <old-docker-root>
docker compose stop

mv <old-docker-root> <new-docker-root>

cd <new-docker-root>
bash <(curl -fsSL https://www.mgrsc.top/http/linux/caddy/install_caddy.sh) configure-shell docker --docker-root "$PWD"
exec $SHELL -l
caddy status
caddy up
```

移动目录后需要执行 `configure-shell` 命令，因为 shell 启动文件里的 hook 和 `.helper.env` 可能仍保存旧 Docker root。

如果最终目录名变了，Docker Compose 可能把移动后的目录识别为另一个 project。这会导致 `docker compose ps` 看不到旧容器，或者 `docker compose up -d` 和已有的 `container_name: caddy` 冲突。目录改名后如需避免 project name 漂移，用固定 project name 运行 Compose：

```bash
docker compose -p caddy up -d
```

从旧版 `caddy_etc` 部署迁移时，更新 `compose.yml` 后需要重建服务容器：

```bash
docker compose down
docker compose up -d
```

不要使用 `docker compose down -v`，除非你明确要删除证书、配置、日志和静态站点 volumes。旧的 `caddy_etc` volume 不再被新版 Compose 文件使用；确认里面没有需要保留的文件后再手动删除。

常用命令：

```bash
caddy up
caddy down
caddy restart
caddy reload
caddy status
caddy edit
caddy version
caddy logs runtime
caddy logs runtime -f
caddy logs access
caddy logs access -f
caddy fmt
caddy validate
```

`caddy version` 现在会先输出部署详情，再输出 Caddy 自带的版本信息，内容包括：

- 当前安装模式
- 安装根目录和配置路径
- 日志目录、状态目录、bind-mounted 配置或 Docker named volumes
- helper 脚本与 helper env 路径
- 静态站点是否启用
- 安装脚本生成的目录、文件、bind mounts 和 Docker named volumes，以及当前是 `present` 还是 `missing`

## 日志

- `runtime.log`：运行日志，用来看启动、reload、ACME 证书申请、续签、告警和错误。
- `access.log`：访问日志，用来看客户端请求、路径、状态码和响应信息。

Docker 模式下，这两个文件位于 `caddy_logs` named volume 的 `/var/log/caddy` 中；使用 `caddy logs runtime` 或 `caddy logs access` 查看，不再读取宿主机侧 `logs` 目录。follow 模式使用正在运行的 `caddy` 服务容器，不会创建一次性的 Compose 容器。

生成的 Caddyfile 按 Caddy 的日志模型组织：

- 全局 options 块配置默认运行日志 logger，并排除 `http.log.access`。
- HTTP 访问日志由站点级 `log` 指令启用，不由全局 options 块启用。
- `(site_logs)` snippet 会把站点访问日志写入同一个 `access.log`；新增站点如果也要写入共享访问日志，需要在对应站点块里 `import site_logs`。

## 权限

- Binary 模式需要 root 或可用的 sudo，因为它会写 `/usr/local/bin`、`/etc/caddy` 和 `/etc/systemd/system`。
- Docker 模式会在所选 docker 根目录写部署文件，并使用 Docker named volumes 保存运行态，通常使用有 Docker 权限的当前用户即可。

## 验证

发布改动前，对修改过的 shell 脚本运行 `shellcheck` 和 `shfmt`。
