WikiDocs 2.0 配置参数详解

目标:讲清 WikiDocs(Wiki|Docs)2.0.2 的两层参数体系——进程级环境变量(MODE/SECRET/PORT/DATASETS/FRONTEND)与站点级 settings.json(9 字段)及 accounts.json、pinned.json 数据文件,逐项给出约束、默认值、修改方式与实测校验回显,并实测沉淀 SECRET 轮换导致全员登录失败等 5 个高频配置故障的处理

WikiDocsWikiDocs知识库自托管2026/10/30

WikiDocs 2.0 配置参数详解

目标:讲清 WikiDocs(Wiki|Docs)2.0.2 的两层参数体系——进程级环境变量(MODE/SECRET/PORT/DATASETS/FRONTEND)与站点级 settings.json(9 字段)及 accounts.json、pinned.json 数据文件,逐项给出约束、默认值、修改方式与实测校验回显,并实测沉淀 SECRET 轮换导致全员登录失败等 5 个高频配置故障的处理方案。已在 Ubuntu 26.04.1 LTS + WikiDocs 2.0.2 环境逐条实测通过。

一、环境信息

本文所有命令与回显均在以下真实环境逐条实测,读者在相近环境可按原文复现。

  • 操作系统:Ubuntu 26.04.1 LTS(内核 7.0.0-38-generic)
  • 应用版本:WikiDocs 2.0.2(官方镜像 zavy86/wikidocs:2,后端运行时 Node.js v22.23.4)
  • 部署形态:按 《WikiDocs 2.0 Docker Compose 部署指南》第四章完成的单实例部署
  • 服务端口:宿主机 8132 → 服务内 3210
  • 数据目录:/opt/wikidocs/datasets(挂载到服务内 /var/lib/wikidocs/datasets)
  • 演示账号:管理员 admin@lab.local / q1w2e3r4(另含一个 author@lab.local 演示作者号)
  • 参数取证口径:运行实例实测(curl 回显)+ 2.0.2 官方源码(backend/src/services/environment.service.ts、backend/src/schemas/settings.schema.ts、backend/src/services/accounts.service.ts)

二、前置条件

  1. 已按部署指南完成 WikiDocs 2.0.2 部署并完成初始化(存在管理员账号)。部署与环境变量注入方式见 《WikiDocs 2.0 Docker Compose 部署指南》第四、六章。

  2. 健康检查通过(后续所有实测都在该状态下进行):

curl -s -o /dev/null -w 'GET /api/health -> %{http_code}\n' http://127.0.0.1:8132/api/health
GET /api/health -> 204
  1. 准备一个管理员访问令牌(有效期 900 秒,过期重新获取即可):
curl -s -X POST http://127.0.0.1:8132/api/authenticate \
  -H 'Content-Type: application/json' \
  -d '{"account":"admin@lab.local","password":"q1w2e3r4"}'

输出节选(实测首尾):

{"jwt":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkdXJhdGlvbiI6OTAwLCJnZW5lcmF0aW9uIjoiMjAyNi0xMC0zMFQwNjo1MTowOC40MTZaIiwiZXhwaXJhdGlvbiI6IjIwMjYtMTAtMzBUMDc6MDY6MDguNDE2WiIsImFjY291bnQiOiJhZG1pbkBsYWIubG9jYWwiLCJyb2xlIjoiYWRtaW5pc3RyYXRvciIsImF1dGhvcml6YXRpb25zIjpbInJlYWQiLCJ3cml0ZSIsImRlbGV0ZSIsInN5bmMiLCJtYW5hZ2UiXSwiaWF0IjoxNzkzMzQzMDY4fQ.rDzLBd…"}
JWT=$(curl -s -X POST http://127.0.0.1:8132/api/authenticate \
  -H 'Content-Type: application/json' \
  -d '{"account":"admin@lab.local","password":"q1w2e3r4"}' \
  | sed 's/.*"jwt":"\([^"]*\)".*/\1/')
echo "$JWT" | head -c 40; echo '   # 后文统一用 $JWT 指代该令牌'
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkdXJhdGlvbiI6OTAwLCJnZW5lcmF0aW9u   # 后文统一用 $JWT 指代该令牌

三、参数查看与修改方式

WikiDocs 2.0 的参数分两层,修改入口与生效方式完全不同,先分清再动手:

  • 进程级环境变量(MODE、SECRET、PORT、DATASETS、FRONTEND):服务启动时读取一次,运行期不可通过接口修改,改完必须重建服务进程才生效;
  • 站点级数据文件(settings.json、accounts.json、pinned.json):存放在数据集目录,服务对它们按请求实时读盘,既可以通过 REST 接口改,也可以直接编辑文件,改完立即生效、无需重启。

3.1 环境变量的查看方式

部署时注入的环境变量以进程配置为准,直接查运行实例:

docker inspect wikidocs-2.0.2-web --format '{{range .Config.Env}}{{println .}}{{end}}' \
  | grep -vE '^(PATH|NODE_VERSION|YARN_VERSION|NODE_ENV)='
MODE=public
SECRET=q1w2e3r4-instance-secret-key
PORT=3210
FRONTEND=/app/frontend
DATASETS=/var/lib/wikidocs/datasets

💡 说明:grep -vE 过滤掉的是 Node.js 基础镜像自带变量,与 WikiDocs 无关。MODE、SECRET、PORT、DATASETS、FRONTEND 这 5 个就是应用读取的全部业务环境变量(与源码 environment.service.ts 的 getMode/getSecret/getDatasetsRoot 一一对应),其中 FRONTEND 指向前端静态资源目录、镜像内已固定,实际需要运维关注的只有前 4 个。

3.2 settings.json 的两种修改入口

入口一:REST 接口。读取接口 GET /api/settings 匿名即可访问(实测未带令牌返回 200 与完整配置);写入接口 PUT /api/settings 必须携带令牌:

curl -s -w '\nHTTP %{http_code}\n' http://127.0.0.1:8132/api/settings
{"title":"Lab Wiki","subtitle":"实验知识库","owner":"Admin User","notice":"Copyright © 2026 Admin","privacy":null,"localization":"zh","timezone":"Asia/Shanghai","template":"dark","color":"#1e88e5"}
HTTP 200
curl -s -w '\nHTTP %{http_code}\n' -X PUT http://127.0.0.1:8132/api/settings \
  -H 'Content-Type: application/json' -d '{"title":"T"}'
{"message":"You must provide an authentication token..","error":"Unauthorized","statusCode":401}
HTTP 401

带令牌整体提交 9 个字段(PUT 是全量覆盖语义,漏掉的字段会被校验拒绝):

curl -s -w 'HTTP %{http_code}\n' -X PUT http://127.0.0.1:8132/api/settings \
  -H "Authorization: Bearer $JWT" -H 'Content-Type: application/json' \
  -d '{"title":"Lab Wiki","subtitle":"实验知识库","owner":"Admin User","notice":"Copyright © 2026 Admin","privacy":null,"localization":"zh","timezone":"Asia/Shanghai","template":"light","color":"#1e88e5"}'
HTTP 204

入口二:直接编辑数据文件。settings.json 就在数据集目录下,服务每次处理请求都实时读盘,手工编辑保存后立即生效,无需重启。实测:把 "owner" 从 Admin User 改成 Admin User (file-edit),下一次 GET /api/settings 就返回新值:

sed -i 's/"owner": "Admin User"/"owner": "Admin User (file-edit)"/' /opt/wikidocs/datasets/settings.json
curl -s http://127.0.0.1:8132/api/settings | grep -o '"owner":"[^"]*"'
"owner":"Admin User (file-edit)"

两种入口的取舍:接口方式有完整校验(非法值直接 400 并给出原因),文件方式无任何校验(写错了要到页面异常才发现),日常优先走接口,批量修正或救援场景才直接改文件。

3.3 accounts.json 与 pinned.json 的修改方式

账号与置顶同样支持接口/文件两种入口。账号列表接口 GET /api/accounts(需管理员令牌)返回时会自动剥掉口令摘要字段:

curl -s -w '\nHTTP %{http_code}\n' http://127.0.0.1:8132/api/accounts -H "Authorization: Bearer $JWT"
{"accounts":[{"account":"admin@lab.local","firstname":"Admin","lastname":"User","role":"administrator"},{"account":"author@lab.local","firstname":"Au","lastname":"Thor","role":"author"}]}
HTTP 200

账号的增改走 PUT /api/accounts(详见 5.8),置顶走 PUT /api/pinned(详见 5.10);两者的落盘文件可以直接查看:

cat /opt/wikidocs/datasets/pinned.json
[
  {
    "path": "guides/quick-start"
  }
]

四、参数总览

4.1 环境变量总表(5 项)

变量默认值取值与作用生效方式
MODEpubliclocal / private / public 三态,决定访问与游客令牌行为重建服务进程
SECRETwikidocs-insecure-secret-to-be-changed双用途密钥:登录令牌(JWT)签名 + 全部账号口令摘要的 HMAC 密钥重建服务进程
PORT3210服务内监听端口重建服务进程
DATASETS/var/lib/wikidocs/datasets数据集根目录重建服务进程
FRONTEND/app/frontend前端静态资源目录(镜像内固定,无需改)重建服务进程

💡 说明:「重建服务进程」指修改部署编排文件中的 environment: 后执行 docker compose up -d(会触发 Recreate);Shell 命令行注入的环境变量覆盖不了编排文件里的同名值(实测实录见部署指南 7.3)。SECRET 的双用途是本文最重要的结论,轮换影响见 5.2 与 7.1。

4.2 settings.json 字段总表(9 字段)

字段类型约束初始化默认值作用
titlestring非空,≤32 字符初始化向导传入值站点标题(浏览器标题与页头主标题)
subtitlestring非空,≤64 字符flat-file markdown wiki页头副标题
ownerstring非空,≤64 字符初始化向导传入值站点归属方名称
noticestring非空,≤128 字符Copyright © <owner>页脚版权/声明文字
privacystring | null可空;非空时 ≤1024 字符null隐私政策横幅内容,null 表示不展示
localizationstring15 个枚举值之一en界面语言
timezonestring非空,≤32 字符UTC时区(如 Asia/Shanghai),影响时间展示
templatestringlight / darklight明暗主题
colorstring正则 ^#(?:[0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$#4caf50主题强调色

localization 的 15 个合法枚举值(源码 settings.schema.ts @IsIn 列表,含简体中文 zh):ar、cs、de、en、es、fa、fr、hi、it、ja、nl、pl、pt、ru、zh。

4.3 数据集文件总表

路径(DATASETS 下)内容修改接口
settings.json站点设置 9 字段GET/PUT /api/settings
accounts.json账号数组(含 HMAC 口令摘要)GET/PUT /api/accounts、GET/PUT /api/profile
pinned.json置顶文档路径数组GET/PUT /api/pinned
documents/文档树(每页一个目录,content.md 正文、_versions/ 版本快照、附件同目录)/api/document、/api/attachment 等
trash/回收站(删除的页带毫秒时间戳前缀暂存)/api/trash

五、核心参数分类详解

5.1 MODE:访问模式

MODE 决定整个实例的访问边界,三个取值的实测语义:

  • local:单机桌面形态(配合官方桌面客户端使用),账号管理接口被禁用——源码 accounts.service.ts 在该模式下直接抛 BadRequestException('Accounts management is disabled in local mode'),服务器部署不使用该值;
  • private:所有页面必须登录,游客令牌端点返回 401(实测回显):
curl -s http://127.0.0.1:8132/api/guest
{"message":"Guest access is enabled only in public mode","error":"Unauthorized","statusCode":401}
  • public:游客免登录领取只读令牌(86400 秒),匿名浏览全部公开内容,登录后才有写权限:
curl -s http://127.0.0.1:8132/api/guest | head -c 120; echo
{"jwt":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkdXJhdGlvbiI6ODY0MDAsImdlbmVyYXRpb24iOiIyMDI2LTEwLTMwVDA2OjU1OjMyLjMxMloiLCJhY2NvdW50IjoiZ3Vlc3Qi…

当前生效值可用公开端点复核:

curl -s http://127.0.0.1:8132/api/information | head -c 90; echo
{"mode":"public","initialized":true,"service":"wikidocs-backend","version":"2.0.2"

5.2 SECRET:一钥两用的核心密钥

SECRET 在 2.0.2 中承担两个职责(源码实证):

  1. 登录令牌签名:authenticate/guest 颁发的 JWT 用它签发与校验,换钥后所有已发令牌立即失效;
  2. 口令摘要密钥:账号口令不是裸哈希存储,而是 HMAC-SHA256(key=SECRET, msg=password)(源码 accounts.service.ts 的 hashPassword() 用 createHmac('sha256', this.environmentService.getSecret()))。也就是说 SECRET 参与了每个账号口令摘要的运算。

用实例数据复算验证(accounts.json 中管理员口令摘要为 86873d8d…6d8):

printf '%s' 'q1w2e3r4' | openssl dgst -sha256 -hmac 'q1w2e3r4-instance-secret-key'
SHA2-256(stdin)= 86873d8d7a8e8dbd33a509af2e0f35c1197aeece7b52bf324e1a5491cf7556d8

与 accounts.json 存储值逐位一致,算法结论成立。因此 SECRET 的轮换规程必须是「换钥 + 重置全部口令」两步:只换钥不重置口令,所有账号(含管理员)都会登录失败(破坏性实测见 7.1)。镜像默认值 wikidocs-insecure-secret-to-be-changed 明确提示必须替换,首次部署时就要定好。

5.3 PORT 与 DATASETS、FRONTEND

  • PORT:服务内监听端口,默认 3210。2.0 系列从 1.x 的 80 端口迁移过来,官方样例固定 3210;对外暴露端口由部署层的端口映射决定(本文实例为宿主 8132 → 服务内 3210),一般不动 PORT 本身。启动日志显示的监听地址格式有误导性,判断依据见部署指南 7.4;
  • DATASETS:数据集根目录,全部状态(设置/账号/文档/回收站)都落在它下面。默认 /var/lib/wikidocs/datasets,部署层把这个位置挂载到宿主持久化目录即可,无需改值;注意挂载的宿主目录必须预置属主 uid=1000(属主错误导致的 FATAL 重启实录见部署指南 7.1);
  • FRONTEND:前端静态资源在镜像内的位置,/app/frontend,仅源码定制前端时才涉及,运维保持默认。

5.4 站点标识四件套:title / subtitle / owner / notice

四个纯展示字段的约束一致(非空 + 长度上限),超限即 400。以 title(≤32 字符)为例提交 33 个字符:

curl -s -w '\nHTTP %{http_code}\n' -X PUT http://127.0.0.1:8132/api/settings \
  -H "Authorization: Bearer $JWT" -H 'Content-Type: application/json' \
  -d '{"title":"123456789012345678901234567890123","subtitle":"s","owner":"o","notice":"n","privacy":null,"localization":"zh","timezone":"Asia/Shanghai","template":"light","color":"#4caf50"}'
{"message":["title must be shorter than or equal to 32 characters"],"error":"Bad Request","statusCode":400}
HTTP 400
  • title ≤32:站点主标题,建议短词(如「Lab Wiki」),过长会挤压页头布局;
  • subtitle ≤64:副标题,一句话说明站点用途(本文实例用「实验知识库」);
  • owner ≤64:归属方名称,初始化时默认取管理员姓名;
  • notice ≤128:页脚声明,初始化默认生成 Copyright © <owner> 格式(本文实例为 Copyright © 2026 Admin)。

privacy 是其中唯一的可空字段:null 表示不展示隐私横幅;赋非空字符串(≤1024 字符)后客户端会弹出隐私政策横幅,适合对外 public 站点做合规提示。

5.5 localization:界面语言

15 个枚举值之外的任何取值都被拒绝,实测提交 xx:

curl -s -w '\nHTTP %{http_code}\n' -X PUT http://127.0.0.1:8132/api/settings \
  -H "Authorization: Bearer $JWT" -H 'Content-Type: application/json' \
  -d '{"title":"T","subtitle":"s","owner":"o","notice":"n","privacy":null,"localization":"xx","timezone":"Asia/Shanghai","template":"light","color":"#4caf50"}'
{"message":["localization must be one of the following values: ar, cs, de, en, es, fa, fr, hi, it, ja, nl, pl, pt, ru, zh"],"error":"Bad Request","statusCode":400}
HTTP 400

中文界面直接设 "localization": "zh"(本文实例已实测切换,切换后 Web 界面菜单、按钮即时中文化)。注意该值只影响客户端 UI 文案,不翻译文档内容本身。

5.6 timezone:时区

字符串约束(非空、≤32 字符),采用 IANA 时区名。中文环境推荐 Asia/Shanghai(本文实例实测值);留 UTC 时文档时间戳与版本快照时间会与本地时间差 8 小时,容易误判「时间不对」,实为时区展示差异。

5.7 template 与 color:主题外观

  • template:只有 light / dark 两个合法值(枚举校验),控制全局明暗主题;
  • color:主题强调色,必须匹配正则 ^#(?:[0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$,支持 3 位或 6 位十六进制。实测提交颜色名 blue 被拒:
curl -s -w '\nHTTP %{http_code}\n' -X PUT http://127.0.0.1:8132/api/settings \
  -H "Authorization: Bearer $JWT" -H 'Content-Type: application/json' \
  -d '{"title":"Lab Wiki","subtitle":"s","owner":"o","notice":"n","privacy":null,"localization":"zh","timezone":"Asia/Shanghai","template":"light","color":"blue"}'
{"message":["color must match /^#(?:[0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/ regular expression"],"error":"Bad Request","statusCode":400}
HTTP 400

合法写法如 #1e88e5(本文实例采用)或简写 #1e8。本文实例曾将 template 切为 dark、color 设为 #1e88e5 并 PUT 成功(204),确认两值可独立组合。

5.8 账号参数:accounts.json 字段与角色枚举

账号文件是对象数组,每条账号 5 个字段:

字段约束说明
account邮箱格式(@IsEmail),≤256 字符登录名,本文实例用 admin@lab.local
firstname / lastname非空,各 ≤32 字符姓与名,展示用
roleadministrator / author / user 三枚举角色决定授权范围
password可选(null 表示禁用登录);存储值为 HMAC 摘要新建账号必填(接口侧校验)

实际落盘内容(口令摘要为 HMAC-SHA256,见 5.2):

cat /opt/wikidocs/datasets/accounts.json
[
  {
    "account": "admin@lab.local",
    "firstname": "Admin",
    "lastname": "User",
    "role": "administrator",
    "password": "86873d8d7a8e8dbd33a509af2e0f35c1197aeece7b52bf324e1a5491cf7556d8"
  },
  {
    "account": "author@lab.local",
    "firstname": "Au",
    "lastname": "Thor",
    "role": "author",
    "password": "86873d8d7a8e8dbd33a509af2e0f35c1197aeece7b52bf324e1a5491cf7556d8"
  }
]

角色语义:administrator 拿到 read/write/delete/sync/manage 全量授权(JWT authorizations 字段实测可见),author 与 user 按角色收敛写与管理权限;账号增改统一走 PUT /api/accounts(接口会自动完成摘要运算,不要手工编辑 accounts.json 写口令)。

5.9 文档路径规则:sanitizeDocumentPath

文档路径不是自由文本,接口层有一套确定性清洗规则(源码 environment.service.ts),决定 documents/ 下的目录命名。规则逐条如下:

  1. 去除首尾空白后整体转小写;
  2. 反斜杠 \ 统一替换为 /;
  3. 去掉首尾斜杠,连续斜杠合并为一个;
  4. 合法字符仅 a-z 0-9 _ . - /,其余字符(含中文、空格、大写字母)一律替换为 -;
  5. 连续 - 合并为一个;
  6. 含 .. 直接拒绝:Invalid path <…>, '..' is not allowed(防目录穿越);
  7. 路径中含 _versions 直接拒绝:Invalid path <…>, '_versions' is not allowed(保护版本快照目录)。

对中文标题的实际影响:指南/部署说明 这类路径清洗后变成 ---/----,可读性差且易撞名。建议建页时直接用英文小写路径(如 guides/quick-start),把中文放在文档 title 元数据里——本文实例的示例页就用这种姿势落盘:

find /opt/wikidocs/datasets/documents -name content.md | sort
/opt/wikidocs/datasets/documents/content.md
/opt/wikidocs/datasets/documents/guides/quick-start/content.md

5.10 pinned.json:置顶列表

置顶是轻量的路径数组,每项只有 path 一个字段,值必须是清洗后的合法文档路径(见 5.9)。本文实例把示例页置顶后的落盘内容见 3.3([{"path": "guides/quick-start"}])。接口侧对应 PUT /api/pinned;手工编辑该文件同样即时生效,但路径写错(未清洗或不存在)会导致置顶项在客户端无法跳转。

六、生产配置模板

适用场景:小团队私有知识库——20 人以内、日均编辑几十次、文档量数千页以内。资源口径:2 vCPU / 4GB 内存 / 20GB+ 数据盘(WikiDocs 进程为 Node.js 单进程、无数据库,空闲内存占用约 38MB(/api/information 实测 memory:38514688),资源瓶颈主要在磁盘 IO 与附件体积,4GB 内存余量充足)。

部署层模板(生产环境 MODE 用 private,SECRET 必须换成随机长串):

name: wikidocs-2.0.2-stack

services:
  wikidocs:
    image: zavy86/wikidocs:2
    container_name: wikidocs-2.0.2-web
    environment:
      MODE: private
      SECRET: ${WIKIDOCS_SECRET}
      PORT: "3210"
    volumes:
      - /opt/wikidocs/datasets:/var/lib/wikidocs/datasets
    ports:
      - "8132:3210"
    restart: unless-stopped

settings.json 模板(中文团队推荐值):

{
  "title": "团队知识库",
  "subtitle": "研发与运维知识沉淀",
  "owner": "IT 部",
  "notice": "Copyright © 2026 IT 部 - 内部资料",
  "privacy": null,
  "localization": "zh",
  "timezone": "Asia/Shanghai",
  "template": "light",
  "color": "#1e88e5"
}

逐项说明:

  • MODE: private:内部知识库默认全站登录;确需对外公开再切 public 并配合 privacy 横幅(5.4);
  • SECRET:${WIKIDOCS_SECRET} 从部署环境的同名 Shell 变量填充(先 export WIKIDOCS_SECRET=$(openssl rand -hex 32) 再执行 docker compose up -d),一旦投入生产就不再轮换(轮换代价见 7.1),密钥本身不要提交到代码仓库;
  • localization: zh + timezone: Asia/Shanghai:中文团队的界面与时区基线(5.5、5.6);
  • template/color 按团队偏好,light + #1e88e5 是低刺激的默认组合(5.7)。

七、常见问题(FAQ)

以下 5 个问题均为本次配置实测踩到或验证过的真实场景,处理方案附原始回显。

7.1 更换 SECRET 后所有账号登录失败

现象:把 SECRET 从 q1w2e3r4-instance-secret-key 改为 rotated-secret-for-test 并重建服务后,口令完全正确的管理员登录也报错:

curl -s -w '\nHTTP %{http_code}\n' -X POST http://127.0.0.1:8132/api/authenticate \
  -H 'Content-Type: application/json' \
  -d '{"account":"admin@lab.local","password":"q1w2e3r4"}'
{"message":"Authentication failed","error":"Unauthorized","statusCode":401}
HTTP 401

定位:SECRET 是口令摘要的 HMAC 密钥(5.2),换钥后 accounts.json 里存量摘要全部失配,校验必然失败——这是机制性结果,不是密码记错。

修复:二选一。

  • 实验环境直接把 SECRET 改回原值重建,登录即刻恢复(本文实测恢复后 HTTP 200);
  • 生产确需轮换:改 SECRET 重建后,用管理员之外的手段(如 PUT /api/accounts)重置全部账号口令,让摘要按新钥重算。所以生产环境应「一次定好、长期不动」。

7.2 PUT /api/settings 返回 400

现象:提交设置被拒。三类高频 400 的实测回显(均在 5.4–5.7 出现过):

{"message":["title must be shorter than or equal to 32 characters"],"error":"Bad Request","statusCode":400}
{"message":["localization must be one of the following values: ar, cs, de, en, es, fa, fr, hi, it, ja, nl, pl, pt, ru, zh"],"error":"Bad Request","statusCode":400}
{"message":["color must match /^#(?:[0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/ regular expression"],"error":"Bad Request","statusCode":400}

修复:按 message 数组逐条对号——长度超限就截短、语言用 15 枚举内的值、颜色必须 # 开头的 3/6 位十六进制。另注意 PUT 是全量提交:只提交改动字段(如 {"title":"T"})会因缺字段被 400,正确姿势是先 GET 取全量、改完整体提交(3.2 实测的 204 流程)。

7.3 手工编辑 settings.json 后不生效?

结论:会生效,且无需重启——服务对 settings.json 是按请求实时读盘(3.3 实测:改完 owner 立即在下一次 GET /api/settings 返回新值)。如果「没生效」,先检查三件事:改的是不是挂载进数据集的那个文件(DATASETS 指向的真实路径,而非编排目录里的散文件)、JSON 是否被改坏(python3 -m json.tool settings.json 可校验语法)、浏览器端是否有界面缓存(强制刷新)。

7.4 数据目录属主不对导致 FATAL 无限重启

现象:改动 DATASETS 挂载后服务反复重启,日志刷 FATAL [Bootstrap] DATASETS directory </var/lib/wikidocs/datasets> is not writable。

修复:宿主数据目录预置属主 uid=1000(服务以 node 用户运行):

chown -R 1000:1000 /opt/wikidocs/datasets

完整的定位链路(含 User: [node] 与 uid=1000 的取证命令)见 《WikiDocs 2.0 Docker Compose 部署指南》7.1。

7.5 忘记管理员口令怎么办

现象:管理员口令遗失,无法登录。

修复:直接重算 accounts.json 中该账号的 password 摘要。摘要算法为 HMAC-SHA256(key=SECRET, msg=新口令)(5.2 实测验证过的命令):

printf '%s' 'q1w2e3r4' | openssl dgst -sha256 -hmac 'q1w2e3r4-instance-secret-key'
SHA2-256(stdin)= 86873d8d7a8e8dbd33a509af2e0f35c1197aeece7b52bf324e1a5491cf7556d8

把输出值替换进 accounts.json 对应账号的 password 字段即可(文件实时生效,无需重启)。更省事的路径:若还有另一个管理员账号,用 PUT /api/accounts 重置口令(接口自动算摘要),不必碰文件。

八、参考链接