diff --git a/.gitignore b/.gitignore index 09732d6..fcc0392 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,10 @@ config.json.tmp data/ *.journal.jsonl +# 从示例复制出的本地 compose 文件 +compose.yml +docker-compose.yml + # Python __pycache__/ *.pyc diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 39af9f5..8b3d71e 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -700,7 +700,7 @@ uv run python tests/test_moves.py ### P4:可选部署 - 反向代理、TLS、远程访问鉴权; -- 仓库已提供 `Dockerfile` / `docker-compose.yml` / `config.docker.example.json`;容器内执行 `python app.py --config /config/config.json`,挂载 `/config` 与 `/data`; +- 仓库已提供 `Dockerfile` / `compose.example.yml` / `config.docker.example.json`;容器内执行 `python app.py --config /config/config.json`,挂载 `/config` 与 `/data`; - Docker 不作为本机开发、联调或日常运行的前置条件。 --- diff --git a/README.md b/README.md index 6ab113c..581a439 100644 --- a/README.md +++ b/README.md @@ -209,26 +209,33 @@ uv run python tools/import_fans_history.py --from-file data.js --days 90 ## 快速部署(Docker) -仓库根目录已包含 `Dockerfile`、`docker-compose.yml`、`.dockerignore` 和 `config.docker.example.json`。 +仓库根目录已包含 `Dockerfile`、`compose.example.yml`、`.dockerignore` 和 `config.docker.example.json`。Compose 文件是示例,需要显式指定: ```powershell # 1. 准备服务器配置;至少填写 B 站 Cookie / Telegram,并保留 /data 绝对路径 Copy-Item config.docker.example.json config.json # 2. 构建并后台启动 -docker compose up -d --build +docker compose -f compose.example.yml up -d --build # 3. 查看日志与健康状态 -docker compose logs -f +docker compose -f compose.example.yml logs -f curl http://127.0.0.1:8080/api/health ``` +如果希望省略 `-f`,可以复制为 `compose.yml`(该文件不提交): + +```powershell +Copy-Item compose.example.yml compose.yml +docker compose up -d --build +``` + - 容器执行 `python app.py --config /config/config.json`,同时运行只读 Web、调度和 Telegram; - `config.json` 挂载到 `/config/config.json`,`data/` 挂载到 `/data/`; - 服务器配置需使用 `web.host = 0.0.0.0` 并设置 `web.auth_token`,否则服务拒绝启动; - `storage.database_path` 使用 `/data/app.db`,`storage.backup_dir` 使用 `/data/backup`; - 首次部署可先在本地执行 `uv run python app.py auth --account read` 扫码,再把生成好的 `config.json` 放到服务器; -- 也可在容器内扫码:`docker compose run --rm board python app.py auth --account read --no-browser`,二维码会打印在临时文件路径,可用 `docker cp` 取出; +- 也可在容器内扫码:`docker compose -f compose.example.yml run --rm board python app.py auth --account read --no-browser`,二维码会打印在临时文件路径,可用 `docker cp` 取出; - 数据卷包含数据库、备份与预览库,建议定期备份 `/data`。 不使用 Compose 时: diff --git a/docker-compose.yml b/compose.example.yml similarity index 100% rename from docker-compose.yml rename to compose.example.yml diff --git a/project.md b/project.md index b13800f..c73969a 100644 --- a/project.md +++ b/project.md @@ -701,7 +701,7 @@ fan_delta_cum = follower(d) - follower(首日) ```text Dockerfile 单容器镜像(只安装 httpx 0.28.1) -docker-compose.yml 挂载 ./config.json → /config、./data → /data +compose.example.yml 挂载 ./config.json → /config、./data → /data(复制为 compose.yml 后使用) .dockerignore 排除凭据、数据、测试与本地环境 config.docker.example.json 服务器配置样例(0.0.0.0 + /data 绝对路径) ``` diff --git a/reaction_board/web/server.py b/reaction_board/web/server.py index b200f86..b47b7ff 100644 --- a/reaction_board/web/server.py +++ b/reaction_board/web/server.py @@ -397,13 +397,17 @@ border-color:#3a2c22;color:#f3e9dc}button{background:#e9b97c;color:#2a1a10}} }: content_type += "; charset=utf-8" payload = target.read_bytes() + vendor_root = (STATIC_ROOT / "vendor").resolve() + try: + target.relative_to(vendor_root) + cache_control = "public, max-age=86400" + except ValueError: + # 应用自身的 JS/CSS/HTML 每次重新校验,保证升级后浏览器立刻拿到新版。 + cache_control = "no-cache" self.send_response(HTTPStatus.OK) self.send_header("Content-Type", content_type) self.send_header("Content-Length", str(len(payload))) - self.send_header( - "Cache-Control", - "no-cache" if target.name == "index.html" else "public, max-age=3600", - ) + self.send_header("Cache-Control", cache_control) self._security_headers() self.end_headers() if method != "HEAD":