DOCAT 配置参数详解(环境变量 / nginx 反代 / 认证实测)
逐项拆解 DOCAT 1.6.1 配置面:DOCAT_STORAGE_PATH 与 DOCAT_SERVE_FILES 环境变量隔离实测、MAX_UPLOAD_SIZE 上传 413 实测、nginx 反代模板逐段说明、claim 令牌与 Basic Auth 双层认证及全量错误码矩阵,全部配置改动附真机回显。
DOCAT 配置参数详解(环境变量 / nginx 反代 / 认证实测)
DOCAT 1.6.1 的配置面非常克制:后端只有两个环境变量,加上官方 nginx 模板中的两个可调参数,就能覆盖绝大多数部署场景。本文基于 Ubuntu 22.04 + DOCAT 1.6.1 实测环境(部署过程见 《DOCAT Ubuntu 22.04 安装部署指南》),对每一个配置项做"定义 → 默认值 → 实测验证"三段式拆解,所有命令与回显均为真机记录。
组件定位与选型背景见 《DOCAT 介绍与选型指南》,日常操作命令见 《DOCAT 常用 CLI 命令详解》。
一、配置体系总览
| 层 | 配置手段 | 生效方式 |
|---|---|---|
| 后端环境变量 | DOCAT_STORAGE_PATH、DOCAT_SERVE_FILES | systemd 单元 Environment= 行,重启 docat-api 服务生效 |
| 前端构建期变量 | VITE_DOCAT_VERSION | yarn build 前注入,构建后固化在产物中 |
| Web 层 | nginx 站点配置(监听端口、上传上限、反代、认证) | systemctl reload/restart nginx 生效 |
| 应用行为 | 项目认领令牌(claim/Docat-Api-Key) | 数据层机制,无需配置文件 |
DOCAT 没有 docat.conf 之类的配置文件——后端配置全部走环境变量(定义见 1.6.1 源码 docat/app.py),Web 层配置全部在 nginx 站点文件(官方模板 docat/nginx/default)。
二、后端环境变量逐项详解
2.1 DOCAT_STORAGE_PATH —— 存储根目录
- 定义位置:源码
docat/app.py:DOCAT_STORAGE_PATH = Path(os.getenv("DOCAT_STORAGE_PATH", Path("/var/docat"))) - 默认值:
/var/docat - 作用:文档与元数据的总根目录。目录结构为
<存储根>/doc/<项目>/<版本>/(文档)与<存储根>/db.json(项目认领令牌等元数据)。 - 本文实测值:systemd 单元中
Environment=DOCAT_STORAGE_PATH=/var/docat,实测目录布局:
# ls -la /var/docat/
total 20
drwxr-xr-x 3 www-data www-data 4096 Oct 28 05:20 .
drwxr-xr-x 1 root root 4096 Oct 28 05:12 ..
-rw-r--r-- 1 www-data www-data 566 Oct 28 05:20 db.json
drwxr-xr-x 3 www-data www-data 4096 Oct 28 05:23 doc
隔离性实测:用不同 DOCAT_STORAGE_PATH 启动第二个 API 实例(监听 8002,存储指向 /var/docat-test),验证两套数据完全隔离:
# DOCAT_SERVE_FILES=1 DOCAT_STORAGE_PATH=/var/docat-test python3 -m uvicorn --host 127.0.0.1 --port 8002 docat.app:app &
# curl -s http://127.0.0.1:8002/api/stats
{"n_projects":0,"n_versions":0,"storage":"0 bytes"}
# curl -s -X POST -F 'file=@/tmp/getting-started-1.0.0.zip' http://127.0.0.1:8002/api/isolated-project/0.1.0
{"message":"Documentation uploaded successfully"}
# curl -s http://127.0.0.1:8002/api/stats
{"n_projects":1,"n_versions":1,"storage":"1 MB"}
# curl -s http://127.0.0.1:8143/api/stats
{"n_projects":1,"n_versions":2,"storage":"3 MB"}
# ls /var/docat-test/doc/
isolated-project
主实例(8143,存储 /var/docat)始终只看到自己的 getting-started 项目,新上传的 isolated-project 只存在于 /var/docat-test。多实例并存(如测试实例与生产实例)时,每个实例必须配独立端口 + 独立存储根。
2.2 DOCAT_SERVE_FILES —— 免 nginx 直连模式
- 定义位置:源码
docat/app.py末尾:if os.environ.get("DOCAT_SERVE_FILES"): app.mount("/doc", StaticFiles(directory=DOCAT_UPLOAD_FOLDER, html=True), name="docs")(源码注释标注为 serve_local_docs for local testing without a nginx) - 默认值:未设置(关闭)。设为任意非空值即开启。
- 作用:API 进程自己把存储目录挂载到
/doc路径提供静态文档服务,不依赖 nginx 也能浏览文档。 - 适用:快速验证、内网临时使用。生产环境建议关闭(nginx 直接静态直出的吞吐与缓存能力远优于应用进程)。
实测(8002 实例同时开启了该变量):
# curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8002/doc/isolated-project/0.1.0/
200
# curl -s http://127.0.0.1:8002/api/stats
{"n_projects":1,"n_versions":1,"storage":"1 MB"}
同一进程内 /api/* 与 /doc/* 都可用,浏览器直接访问 8002 端口即可得到完整界面与文档。
2.3 MAX_UPLOAD_SIZE —— 上传大小上限(Web 层生效)
- 定义位置:官方应用包构建脚本(Dockerfile)中
ENV MAX_UPLOAD_SIZE=100M,由 nginx 模板的client_max_body_size $MAX_UPLOAD_SIZE;引用(启动时经 envsubst 替换) - 默认值:100M
- 作用:控制 nginx 允许的请求体大小,即单个文档 zip 的上传上限。注意它只约束 nginx 反代路径——直连 API 进程(127.0.0.1:5000)时该限制不存在。
实测(制作 105 MiB 的 zip,走 nginx 上传):
# dd if=/dev/urandom of=/tmp/big.bin bs=1M count=105 && cd /tmp && zip -q big-docs.zip big.bin
# ls -la /tmp/big-docs.zip
-rw-r--r-- 1 root root 110118146 Oct 28 05:18 /tmp/big-docs.zip
# curl -s -w '\nHTTP %{http_code}\n' -X POST -F 'file=@/tmp/big-docs.zip' http://127.0.0.1:8143/api/demo-renamed/9.9.9 | tail -3
</html>
HTTP 413
同一文件直连 API 进程(绕过 nginx),返回应用层的成功响应(顺带验证了"zip 根没有 index.html"时的提示语义):
# curl -s --max-time 60 -w '\nHTTP %{http_code}\n' -X POST -F 'file=@/tmp/big-docs.zip' http://127.0.0.1:5000/api/demo-renamed/9.9.9 | tail -2
{"message":"Documentation uploaded successfully, but no index.html found at root of archive."}
HTTP 201
调优方法:手动部署形态下直接把 nginx 站点文件中的 client_max_body_size 100M; 改为目标值(如 500M)后 systemctl reload nginx;无需改后端。
2.4 VITE_DOCAT_VERSION —— 前端版本注入(构建期)
- 定义位置:官方前端构建流程:
ENV VITE_DOCAT_VERSION=$DOCAT_VERSION,构建命令yarn build - 默认值:官方应用包内为其构建时注入的版本号;从源码自建时不设则显示 unknown
- 作用:前端界面展示的版本标识,构建后固化在 JS 产物中,运行期不可改。
- 本文实测:构建前
export VITE_DOCAT_VERSION=1.6.1,构建产物即携带该标识。
三、nginx 反代配置逐项详解
官方模板(docat/nginx/default)逐项说明(本文实测使用的完整站点文件见 B 篇第八节,监听端口 8143):
3.1 upstream 与 /api 反代
upstream python_backend {
server 127.0.0.1:5000;
}
...
location /api {
client_max_body_size 100M;
proxy_pass http://python_backend;
}
- 后端只监听
127.0.0.1,外部无法绕过 nginx 直连; client_max_body_size必须写在/api这个 location 内(或 server 块),否则 nginx 默认 1M 会在上传较大文档时返回 413;- 官方模板
proxy_pass http://python_backend;不带路径后缀,/api 前缀原样透传给 FastAPI(路由本身就定义在 /api 下),不要自作主张改写成proxy_pass http://python_backend/;(尾部斜杠会剥掉 /api 前缀导致全部 404)。
3.2 /doc 静态文档段
location /doc {
root /var/docat;
absolute_redirect off;
}
root /var/docat与后端DOCAT_STORAGE_PATH指向同一目录;URI/doc/<项目>/<版本>/x.html映射到磁盘/var/docat/doc/<项目>/<版本>/x.html(location 前缀/doc不剥离,恰好与存储内的doc/子目录同名对齐——这是官方模板刻意的设计,改动任一侧都会 404);absolute_redirect off:目录访问缺省页时 nginx 生成的重定向保持相对路径,避免反代场景下跳到错误的绝对主机名。
3.3 前端 SPA 段
root /var/www/html;
index index.html index.htm index.pdf /index.html;
location / {
try_files $uri $uri/ /index.html =404;
}
- 前端是单页应用,
try_files ... /index.html让任意前端路由都回落到入口页面,直接刷新/help等路径不会 404; index行中的index.pdf与/index.html兜底是官方模板原始内容,保留即可。
3.4 安全响应头
add_header Content-Security-Policy "frame-ancestors 'self';";
实测确认响应头已生效(该头禁止第三方页面 iframe 嵌套文档站,防点击劫持):
# curl -sI http://127.0.0.1:8143/ | grep -i content-security
Content-Security-Policy: frame-ancestors 'self';
四、认证配置详解
4.1 内置机制:项目认领令牌(claim / Docat-Api-Key)
DOCAT 的内置认证是"先到先得的项目认领":项目未认领时任何人可上传新版本;一旦认领(每个项目仅能认领一次),覆盖上传、删除、重命名、隐藏等变更操作都必须携带认领时返回的令牌,令牌以 HTTP 头 Docat-Api-Key 传递。
实测一:认领与重复认领:
# curl -s http://127.0.0.1:8143/api/demo-project/claim
{"message":"Project demo-project successfully claimed","token":"7aede69addc833b337103e1fc48ec1c8"}
# curl -s -w '\nHTTP %{http_code}\n' http://127.0.0.1:8143/api/demo-project/claim
{"message":"Project demo-project is already claimed!"}
HTTP 409
实测二:无令牌变更被拒(401):
# curl -s -w '\nHTTP %{http_code}\n' -X POST -F 'file=@/tmp/demo-docs.zip' http://127.0.0.1:8143/api/demo-project/1.0.0
{"message":"Please provide a header with a valid Docat-Api-Key token for demo-project"}
HTTP 401
# curl -s -w '\nHTTP %{http_code}\n' -X DELETE http://127.0.0.1:8143/api/demo-project/1.0.0
{"message":"Please provide a header with a valid Docat-Api-Key token for demo-project"}
HTTP 401
实测三:携带令牌后放行(201):
# curl -s -w '\nHTTP %{http_code}\n' -X POST -H 'Docat-Api-Key: 7aede69addc833b337103e1fc48ec1c8' -F 'file=@/tmp/demo-docs.zip' http://127.0.0.1:8143/api/demo-project/1.0.0
{"message":"Documentation uploaded successfully"}
HTTP 201
令牌校验算法(源码 docat/utils.py):PBKDF2-HMAC-SHA256,10 万轮迭代,盐值与哈希同存于 db.json 的 claims 表——因此 db.json 属于必须备份的关键资产(备份恢复实测见 D 篇第七节)。
新增版本不受令牌限制:向未存在的版本号上传不需要令牌(官方设计即"新增自由、覆盖/删除受控"),若要连"新增"也管住,见 4.2 的 Web 层方案。
4.2 Web 层机制:HTTP Basic Auth 保护写操作
公网部署时官方 README 明确建议:对全部 POST/PUT/DELETE 请求叠加 HTTP Basic Auth。做法是在 /api location 中加 limit_except GET HEAD 段,并指向 htpasswd 文件。
实测配置(在 8081 端口起一个带认证的平行站点验证行为差异,htpasswd 创建用户 admin):
# htpasswd -bc /etc/nginx/.htpasswd admin q1w2e3r4
Adding password for user admin
# cat /etc/nginx/.htpasswd
admin:$apr1$cSwohkJz$xlDQBw1evlGKFviHKn1QA/
location /api {
limit_except GET HEAD {
auth_basic 'Restricted';
auth_basic_user_file /etc/nginx/.htpasswd;
}
client_max_body_size 100M;
proxy_pass http://python_backend_auth;
}
三态实测:
# curl -s -w ' => HTTP %{http_code}\n' http://127.0.0.1:8081/api/stats
{"n_projects":1,"n_versions":1,"storage":"1 MB"} => HTTP 200
# curl -s -o /dev/null -w 'HTTP %{http_code}\n' -X POST -F 'file=@/tmp/demo-docs.zip' http://127.0.0.1:8081/api/demo-renamed/2.0.0
HTTP 401
# curl -s -o /dev/null -w 'HTTP %{http_code}\n' -u admin:q1w2e3r4 -X POST -F 'file=@/tmp/demo-docs.zip' http://127.0.0.1:8081/api/demo-renamed/2.0.0
HTTP 201
- 读操作(GET/HEAD)不限:200;
- 写操作无凭据:401;
- 写操作带凭据:201。
该方案与 4.1 的令牌机制叠加生效(Web 层挡"谁都能调接口",应用层挡"认领之外的变更"),生产建议两层都开。
4.3 应用层错误码速查(实测值)
| 场景 | 状态码 | 实测响应体 |
|---|---|---|
| 变更已认领项目未带令牌 | 401 | {"message":"Please provide a header with a valid Docat-Api-Key token for <项目>"} |
| 令牌错误 | 401 | {"message":"Docat-Api-Key token is not valid for <项目>"} |
| 重复认领 | 409 | {"message":"Project <项目> is already claimed!"} |
| 上传覆盖已有标签(符号链接) | 409 | {"message":"Cannot overwrite existing tag with new version."} |
| 标签覆盖已存在的真实版本 | 409 | {"message":"Tag <标签> would overwrite an existing version!"} |
| 保留字项目名(api/doc/upload/claim/delete/help) | 400 | {"message":"Project name \"api\" is forbidden, as it conflicts with pages in docat web."} |
| 查询不存在的项目 | 404 | {"message":"Project <项目> does not exist"} |
| 删除不存在的版本 | 404 | {"message":"Could not find version '<路径>'"} |
| zip 无法解包 | 400 | {"message":"Cannot extract zip file."} |
| 上传成功但 zip 根无 index.html | 201 | {"message":"Documentation uploaded successfully, but no index.html found at root of archive."} |
五、自检与联调接口
| 接口 | 用途 | 实测状态码 |
|---|---|---|
GET /api/stats | 项目数/版本数/存储占用一览(排障首选) | 200 |
GET /api/projects | 项目清单(?include_hidden=true 可见隐藏版本) | 200 |
GET /api/projects/<项目> | 单项目版本与标签明细 | 200 / 404 |
GET /api/docs | 内置 Swagger UI(在线调试全部 API) | 200 |
GET /api/redoc | ReDoc 版 API 文档 | 200 |
GET /api/v1/openapi.json | OpenAPI 规范(可导入 Postman 等工具) | 200 |
六、常用配置变更速查
| 需求 | 改法 | 生效动作 |
|---|---|---|
| 更换数据盘/迁移存储 | systemd 单元改 DOCAT_STORAGE_PATH,同步改 nginx /doc 的 root;迁移时用 tar 打包原目录(保留符号链接) | systemctl restart docat-api && systemctl reload nginx |
| 放宽上传上限 | /api location 内 client_max_body_size 调大 | systemctl reload nginx |
| 内网临时试用无 nginx | API 进程加 DOCAT_SERVE_FILES=1 | 重启 docat-api |
| 公网收紧写权限 | 4.2 的 limit_except + htpasswd | systemctl reload nginx |
| 前端显示正确版本号 | 构建前 export VITE_DOCAT_VERSION=<版本> | 重新 yarn build 并发布产物 |
修改任何 nginx 配置后务必
systemctl reload nginx(或 restart);systemctl enable --now对已运行服务不会重载配置——这是 B 篇排障实录 1 的教训。
七、小结
DOCAT 的配置哲学与它的功能哲学一致:能不做的都不做。两个环境变量、两个 Web 层参数、一个内建认领机制,配合 nginx 官方模板即可支撑从内网到公网的部署。真正需要牢记的三件事:/doc 的 root 必须与 DOCAT_STORAGE_PATH 对齐;client_max_body_size 决定上传上限且仅约束 Web 层;db.json 存着全部认领令牌、必须纳入备份。