DOCAT 配置参数详解(环境变量 / nginx 反代 / 认证实测)

逐项拆解 DOCAT 1.6.1 配置面:DOCAT_STORAGE_PATH 与 DOCAT_SERVE_FILES 环境变量隔离实测、MAX_UPLOAD_SIZE 上传 413 实测、nginx 反代模板逐段说明、claim 令牌与 Basic Auth 双层认证及全量错误码矩阵,全部配置改动附真机回显。

DOCAT文档托管Ubuntu22.042026/10/28

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_FILESsystemd 单元 Environment= 行,重启 docat-api 服务生效
前端构建期变量VITE_DOCAT_VERSIONyarn 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.html201{"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/redocReDoc 版 API 文档200
GET /api/v1/openapi.jsonOpenAPI 规范(可导入 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
内网临时试用无 nginxAPI 进程加 DOCAT_SERVE_FILES=1重启 docat-api
公网收紧写权限4.2 的 limit_except + htpasswdsystemctl 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 存着全部认领令牌、必须纳入备份。