Inventaire 5.1.1 配置参数详解

Inventaire 5.1.1 全量配置参数详解:node-config 三层加载机制、/api/config 查看与 local.ts、NODE_CONFIG 修改实测(含优先级验证)、CouchDB/Elasticsearch/邮件/媒体存储/联邦/安全限流逐项说明与两种形态的生产模板,均在 Ubuntu 22.04 与 Compose 双环境实测。

Inventaire自托管配置2026/10/24

Inventaire 5.1.1 配置参数详解

目标:系统梳理 Inventaire 5.1.1 服务端的全部配置参数——配置加载机制、参数查看与修改方法、逐项参数语义与生产推荐模板。本文全部命令与回显均在两套实测环境执行通过:① Ubuntu 22.04 源码安装环境(Node.js 20.20.2 + CouchDB 3.5.3 + Elasticsearch 7.17.29,systemd 托管);② Docker Compose 官方套件部署环境(inventaire/inventaire:5.1.1 + couchdb:3.4.2 + elasticsearch:7.17.28)。实测日期 2026-10-24。

一、环境信息

本文实测覆盖两种部署形态,配置机制相同、参数来源不同,读者按自己的形态对号入座:

环境 ①:源码安装(Ubuntu 22.04)

项目实测值
操作系统Ubuntu 22.04.5 LTS(x86_64)
Inventaire 版本5.1.1(/api/config 返回 serverGitHeadRev: 2263179)
Node.jsv20.20.2(nvm 安装)
CouchDB3.5.3(systemd 服务 couchdb.service,监听 127.0.0.1:5984)
Elasticsearch7.17.29(systemd 服务 elasticsearch.service,监听 127.0.0.1:9200)
服务监听端口3006(默认开发/单机端口)
项目根目录/home/inventaire/inventaire
配置目录/home/inventaire/inventaire/config/

环境 ②:Docker Compose 官方套件

项目实测值
Inventaire 镜像inventaire/inventaire:5.1.1(serverGitHeadRev: 3ba29ff)
CouchDBcouchdb:3.4.2(套件自建层)
Elasticsearch7.17.28(套件 compose 固定)
服务监听端口3016(联邦模式端口),宿主机映射 8138
配置来源容器内 /opt/inventaire/dist/config/local.js(entrypoint 每次启动重新生成)

两种形态的参数语义完全一致(同一套 node-config 配置体系),差异只在「参数从哪来、怎么改」:源码安装直接编辑 config/local.ts;Compose 形态通过 .env 与 NODE_CONFIG 注入。均见第三章实测。

二、前置条件

阅读与操作本文的前提:

  1. 已按 《Inventaire 5.1.1 Docker Compose 部署指南》或源码安装方式完成部署,服务可访问(验证命令 curl -s http://localhost:3006/api/config 或映射端口等价形式,返回 JSON 即可);
  2. 源码安装形态需要对项目目录的写权限(config/local.ts 属于部署用户)与重启服务的能力(本文以 tsx server/server.ts 启动的进程为例,systemd 托管时等价执行 systemctl restart inventaire);
  3. Compose 形态需要能编辑部署目录下的 .env 与 docker-compose.yml 覆盖文件,并执行 docker compose up -d 重建服务;
  4. 了解 JSON 与 JavaScript 对象字面量基本语法(配置文件即 JS 模块)。

三、参数查看与修改方式

Inventaire 服务端配置基于 node-config 机制:全部参数有一个内置默认值(config/default.ts),再按环境叠加覆盖文件,最终形成运行时生效值。本章先讲「怎么看到生效值」,再讲「怎么改」。

3.1 查看生效参数:GET /api/config

服务内置一个公开的配置查看端点 GET /api/config,返回对外可见的生效参数(实例信息、对外地址、地图服务等,不含数据库密码等敏感项):

curl -s http://localhost:3006/api/config | python3 -m json.tool

实测回显(环境 ①,源码安装、默认配置):

{
    "version": "5.1.1",
    "softwareName": "inventaire",
    "serverGitHeadRev": "2263179",
    "env": "default",
    "instanceName": "My Inventaire Instance",
    "orgName": "Example Organization",
    "orgUrl": "https://inventaire.example.org",
    "mapTiles": {
        "tileUrl": "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
        "attribution": "Map data & imagery © <a href=\"http://openstreetmap.org\">OpenStreetMap</a> contributors, <a href=\"http://creativecommons.org/licenses/by-sa/2.0/\">CC-BY-SA</a>"
    },
    "publicHost": "localhost:3006",
    "publicOrigin": "http://localhost:3006",
    "defaultLandingPageMapPosition": [45.744, 4.821],
    "orgFediverseAccount": null,
    "maintenance": {"ongoingReindexation": false},
    "contactAddress": "contact@inventaire.example.org"
}

要点:env 字段显示当前生效的配置环境名(default/production 等),publicOrigin 是客户端实际使用的对外地址,改完配置后先看这个端点确认生效。

3.2 源码安装形态:配置文件体系与加载顺序

源码安装时,配置文件全部在项目根的 config/ 目录,实测该目录内容:

config/
├── default.ts               # 全部参数的默认值(约 440 行,见第四章总览)
├── production.ts            # NODE_ENV=production 时叠加
├── production-federated.ts  # NODE_ENV=production + NODE_APP_INSTANCE=federated 时叠加
├── production-primary.ts    # NODE_APP_INSTANCE=primary 时叠加(实体主库实例用)
├── production-alt.ts        # NODE_APP_INSTANCE=alt 时叠加
├── dev.ts                   # NODE_ENV=dev 时叠加
├── dev-federated.ts         # NODE_ENV=dev + NODE_APP_INSTANCE=federated 时叠加
├── tests-*.ts               # 自动化测试环境用
└── local.ts                 # ★ 运维者自己的覆盖文件,加载顺序最后、优先级最高(本地文件)

加载顺序遵循 node-config 的规则:default.ts → <环境>.ts → <环境>-<实例>.ts → local.ts → NODE_CONFIG 环境变量,后者逐层覆盖前者。local.ts 不在官方仓库中(.gitignore 排除),首次部署由运维者自己创建,是源码安装形态下改参数的标准位置。实测环境 ① 的 config/local.ts:

// Override settings from ./default.ts in this file

const config = {
  db: {
    username: "admin",
    password: "q1w2e3r4",
  },
  publicHostname: "inventaire.example.com",
}

export default config

production.ts 的关键覆盖项(与默认值的差异,实测读源文件):

const config: PartialDeep<Config> = {
  env: 'production',
  publicHostname: 'OVERRIDE',        // 强制要求部署者覆盖
  publicProtocol: 'https',
  publicPort: null,
  serveStaticFiles: false,           // 生产环境静态文件交给 Nginx
  db: {
    username: 'OVERRIDE',
    password: 'OVERRIDE',
    suffix: 'prod',                  // 业务库名加 -prod 后缀
  },
  mailer: { disabled: false, nodemailer: { requireTLS: true } },
  activitySummary: { disabled: false },
  wikidataEdit: { dry: false },
  // ...
}

production-federated.ts(联邦实例,Compose 官方镜像即以此模式运行)的关键覆盖项:

const config: PartialDeep<Config> = {
  port: 3016,                                        // 联邦模式固定端口
  federation: { remoteEntitiesOrigin: 'https://inventaire.io' },
  activitySummary: { disabled: false, maxEmailsPerHour: 20 },
  debouncedEmail: { disabled: true },
  jobs: {                                            // 实体主库专属任务,联邦实例关闭
    'inv:deduplicate': { run: false },
    'entity:auto-enrich-editions': { run: false },
    'entity:popularity': { run: false },
    'wd:entity:indexation': { run: false },
  },
  dataseed: { enabled: false },
  // ...
}

3.3 修改参数并重启验证(源码安装实测)

以「把实例名称从默认值改为自己的名称」为例,完整走一遍修改闭环。第一步编辑 config/local.ts,追加 instanceName 与 contactAddress 两个参数:

const config = {
  db: {
    username: "admin",
    password: "q1w2e3r4",
  },
  publicHostname: "inventaire.example.com",
  instanceName: "My Home Library",
  contactAddress: "admin@inventaire.example.com",
}

export default config

第二步重启服务(本文实测环境以前台进程方式运行,实际服务器用 systemd 时执行 systemctl restart inventaire):

pkill -f 'tsx server/server.ts'
nohup node_modules/.bin/tsx server/server.ts > logs/server.log 2>&1 &

第三步观察启动日志确认就绪(实测回显):

inventaire server is listening on port 3006...
startup: 1.459s
mailer disabled
activity summary disabled
GET http://localhost:5984/items/_changes?limit=0&descending=true [r18] 🗝 200 3ms
GET http://localhost:5984/entities/_changes?limit=0&descending=true [r22] 🗝 200 6ms

日志同时给出了两项关键佐证:CouchDB 连接走 http://localhost:5984(db.* 参数生效值),Elasticsearch 索引初始化正常。第四步用 GET /api/config 验证参数已生效(实测回显,与 3.1 节基线对比):

{
    "instanceName": "My Home Library",
    "publicHost": "inventaire.example.com:3006",
    "publicOrigin": "http://inventaire.example.com:3006",
    "contactAddress": "admin@inventaire.example.com"
}

三个参数全部按预期变化:instanceName 变为 My Home Library;publicHostname 的修改连带 publicHost/publicOrigin 一起更新(这两个是派生值);contactAddress 从默认值变为新地址。

⚠️ 注意:Inventaire 只在启动时读取配置,改完必须重启进程才会生效;配置文件是 TS 模块,语法错误会导致启动失败,启动日志会给出具体解析报错行。

3.4 NODE_CONFIG 环境变量覆盖(优先级实测)

NODE_CONFIG 是 node-config 官方的环境变量覆盖机制:值为一个 JSON 字符串,运行时直接覆盖任意配置项,优先级高于包括 local.ts 在内的所有配置文件,适合不改文件、临时验证或 secrets 注入场景。实测:带着 NODE_CONFIG='{"instanceName":"Env Override Instance"}' 重启服务(local.ts 中仍是 My Home Library):

NODE_CONFIG='{"instanceName":"Env Override Instance"}' node_modules/.bin/tsx server/server.ts

验证生效值(实测回显):

Env Override Instance | http://inventaire.example.com:3006

instanceName 取了环境变量里的 Env Override Instance,而 publicOrigin 仍是 local.ts 定义的 http://inventaire.example.com:3006——证明覆盖只发生在 NODE_CONFIG 里声明的键,其余键继续沿文件链取值。

💡 说明:官方 Docker 镜像内部正是同时使用这两种机制——entrypoint 脚本每次启动用 .env 变量重新生成 dist/config/local.js(文件层),再由部署者按需注入 NODE_CONFIG(环境变量层),见 3.5 节。

3.5 Docker Compose 形态:.env 变量到配置键的映射

Compose 部署时不需要(也不应该)手工编辑容器内配置文件:官方镜像的 /opt/docker-entrypoint.sh 在每次启动时用 24 个环境变量重新生成 dist/config/local.js。实测环境 ② 容器内生成的 local.js 全文如下(即当前生效的文件层配置):

// Built by docker-inventaire/inventaire/docker-entrypoint.sh
// The built server will only care for dist/config/local.js, but config/local.ts is kept in sync for consistency

export default {
  hostname: 'inventaire',
  port: '3016',
  publicHostname: 'localhost',
  instanceName: 'My Inventaire Instance',
  orgName: 'Example Organization',
  orgUrl: 'https://inventaire.example.org',
  contactAddress: 'contact@inventaire.example.org',
  db: {
    username: 'admin',
    password: 'q1w2e3r4',
    hostname: 'couchdb',
    backupDirectory: '/opt/inventaire_backups',
  },
  leveldb: {
    directory: '/opt/inventaire_leveldb',
    suffixDirectoryPathWithDbSuffix: false,
  },
  elasticsearch: {
    origin: 'http://elasticsearch:9200',
  },
  mailer: {
    disabled: false,
    nodemailer: {
      host: 'smtp.example.org',
      port: 587,
      auth: {
        user: 'your_username_on_smtp.example.org',
        pass: 'your_password_on_smtp.example.org'
      },
    },
  },
  mapTilesAccessToken: 'youraccesstoken',
  matomo: {
    enabled: false,
    endpoint: 'https://yourmatomoendpoint/matomo.php',
    idsite: 1,
    rec: 1,
  },
  mediaStorage: {
    mode: 'local',
    swift: {
      username: 'customizedInLocalConfig',
      password: 'customizedInLocalConfig',
      authUrl: 'https://openstackEndpointToCustomize',
      publicURL: 'https://swiftPublicURL',
      tenantName: '12345678',
      region: 'SBG-1',
    },
    local: {
      directory: '/opt/inventaire_local_media_storage'
    },
  },
}

24 个环境变量与配置键的对应关系(entrypoint 脚本逐字提取核对):

.env 变量生成的配置键备注
PUBLIC_HOSTNAMEpublicHostname对外域名
INSTANCE_NAMEinstanceName实例名称
ORG_NAME / ORG_URLorgName / orgUrl组织信息
CONTACT_ADDRESScontactAddress联系邮箱
COUCHDB_USER / COUCHDB_PASSWORDdb.username / db.password主库凭据
INVENTAIRE_PORTport监听端口
MAILER_SMTP_HOST / MAILER_SMTP_PORTmailer.nodemailer.host / .portSMTP 服务器
MAILER_SMTP_USERNAME / MAILER_SMTP_PASSWORDmailer.nodemailer.auth.user / .passSMTP 认证
MAP_TILES_ACCESS_TOKENmapTilesAccessToken地图瓦片令牌
MOTOMO_ENABLED / MOTOMO_ENDPOINT / MOTOMO_IDSITE / MOTOMO_RECmatomo.enabled / .endpoint / .idsite / .recMatomo 统计
MEDIA_STORAGE_MODEmediaStorage.modelocal 或 swift
SWIFT_USERNAME / SWIFT_PASSWORD / SWIFT_AUTH_URL / SWIFT_PUBLIC_URL / SWIFT_TENANT_NAME / SWIFT_REGIONmediaStorage.swift.*Swift 对象存储六项

另有 3 个环境变量不进 local.js、直接由 node-config 运行时消费:NODE_ENV(固定 production,选择 production.ts)、NODE_APP_INSTANCE(官方默认 federated,选择 production-federated.ts)、NODE_CONFIG(JSON 直接覆盖任意键)。生成的 local.js 中 CouchDB 主机名 couchdb、ES 地址 http://elasticsearch:9200、LevelDB 目录 /opt/inventaire_leveldb 等是 entrypoint 模板硬编码的,.env 没有对应变量——单容器连接外部数据库/搜索服务时,必须用 NODE_CONFIG 覆盖 db.hostname 与 elasticsearch.origin(实测可行,见 《Inventaire 5.1.1 Docker Compose 部署指南》第九章)。

Compose 形态改参数的完整闭环:改 .env(或 compose 覆盖文件的 NODE_CONFIG)→ docker compose up -d 重建 inventaire 服务 → 观察启动日志出现 server started → curl -s http://localhost:8138/api/config 验证。实测环境 ② 的生效值:

{
    "version": "5.1.1",
    "env": "production",
    "instanceName": "My Inventaire Instance",
    "publicHost": "localhost:8138",
    "publicOrigin": "http://localhost:8138",
    "remoteEntitiesOrigin": "https://inventaire.io"
}

env: production 与 remoteEntitiesOrigin 正是 production.ts + production-federated.ts 两层叠加的结果,与 3.2 节的文件加载顺序吻合。

四、参数总览

config/default.ts 按功能分区给出全部默认值,下表为全景速查(默认值均摘自实测环境 ① 的源文件,标注 ★ 的为核心运维参数,第五章逐项详解):

分区参数默认值说明
实例信息instanceName'My Inventaire Instance'★ 实例名称,大屏菜单标题
实例信息orgName / orgUrl'Example Organization' / 'https://inventaire.example.org'★ 落地页组织信息
实例信息contactAddress'contact@inventaire.example.org'★ 实例邮件的回复地址
实例信息defaultLandingPageMapPosition[45.744, 4.821]落地页地图默认经纬度
实例信息orgFediverseAccountnull链接分享到联邦宇宙时的署名账号
监听与对外protocol / hostname / port'http' / 'localhost' / 3006★ 服务自身监听(生产 TLS 交给 Nginx)
监听与对外publicProtocol / publicHostname / publicPort'http' / 'localhost' / undefined★ 对外地址三元组(生产应为 https/域名/null)
监听与对外trustProxy'loopback, uniquelocal'Express 反代信任网段
监听与对外serveStaticFilestrue(生产强制 false)★ 是否由 Node 托管前端静态文件
监听与对外autoRotateKeystrue会话签名密钥自动轮换
监听与对外cookieMaxAge180*24*3600*1000(180 天)会话 Cookie 最长有效期
CouchDBdb.protocol / hostname / port'http' / 'localhost' / 5984★ 主库地址
CouchDBdb.username / password占位值★ 主库凭据(必须覆盖)
CouchDBdb.suffixnull(生产 'prod')业务库名后缀
CouchDBdb.follow.reset / freeze / delayfalse / false / 5000库事件跟随行为
CouchDBdb.debugfalse打印 CouchDB 请求参数
CouchDBdb.backupDirectory'backups/couchdb'★ 备份产物目录
CouchDBdb.runMigrationstrue启动时执行数据迁移
LevelDBleveldb.directory'./db/leveldb'LevelDB 目录(缓存+任务队列)
LevelDBleveldb.inMemoryLRUCacheSize64 MB内存 LRU 缓存大小
LevelDBleveldb.ttlCheckFrequency60000缓存 TTL 巡检周期(ms)
缓存cache.defaultTTL / debug30 天 / false通用缓存 TTL
Elasticsearchelasticsearch.origin'http://localhost:9200'★ ES 地址
Elasticsearchelasticsearch.updateDelay1000写入后索引可见延迟(ms)
Elasticsearchelasticsearch.minReindexationInterval60000重索引最小间隔(ms)
Elasticsearchelasticsearch.logResponses / selfSignedCertificatefalse / falseES 调试与自签证书
日志verbosetrue详细日志开关
日志requestsLogger.mutedRootSections / mutedPath[] / ['/api/reports/online']降噪过滤
邮件mailer.disabledtrue(生产 false)★ 邮件总开关
邮件mailer.nodemailer.host / port / authsmtp.ethereal.email / 587 / 占位★ SMTP 参数
邮件mailer.initDelay / deniedAddressesPatterns10000 / []启动延迟 / 拒发地址模式
邮件debouncedEmail.crawlPeriod / debounceDelay / disabled60s / 10min / false通知合并邮件
邮件activitySummary.disabled / maxEmailsPerHourtrue(生产 false)/ 5周期活动摘要
邮件tokenDaysToLive3邮箱验证令牌有效期(天)
媒体存储mediaStorage.mode'local'★ local 或 swift
媒体存储mediaStorage.local.directory'./storage'★ 本地图片目录
媒体存储mediaStorage.images.maxSize1600上传图片最大边长(px)
媒体存储mediaStorage.swift.*占位值Swift 六项(mode=swift 时必填)
媒体存储remoteImages.useEntitiesHostCachedImagesfalse远端图片走实体主库缓存
地图mapTiles.tileUrl / attributionOpenStreetMap 官方瓦片 / OSM 署名地图瓦片源
地图mapTilesAccessToken(Compose 形态)占位值MapBox 令牌
统计matomo.enabled / endpoint / idsite / recfalse / 占位 / 1 / 1Matomo 埋点
联邦federation.remoteEntitiesOriginundefined(联邦模式 https://inventaire.io)★ 远端实体来源
安全rateLimiting.disabled / timeWindowfalse / 30 分钟★ 接口限流
安全rateLimiting.exemptedUserAgents / restrictedUserAgents[] / []UA 白/黑名单
安全incomingRequests.logBodyfalse入站请求体日志
安全outgoingRequests.baseBanTime / banTimeIncreaseFactor / maxBanTime5000 / 4 / 24h出站请求封禁梯度
安全outgoingRequests.rejectPrivateUrlstrue禁止请求内网地址(SSRF 防护)
安全spam.suspectKeywords['agent','SEO','marketing','shopping']垃圾内容触发词
安全waitFactor1请求等待时间全局倍率
后台任务jobs.'inv:deduplicate' 等 5 项run: true(联邦模式 4 项 false)★ LevelDB 任务开关
WikidatawikidataOAuth.consumer_key / consumer_secret占位值用户 Wikidata OAuth 接入
WikidatawikidataBotAccount.* / wikidataEdit.dry占位 / true(生产 false)机器人账号与 dry-run
开发开关i18n.autofix / offline / useSlowPasswordHashFunction / deduplicateRequestsfalse / false / true / true仅测试开发环境调整

表格较长,选型解读一句话概括:单机自托管最小必改集 = db 凭据 + publicHostname + instanceName/orgName/orgUrl/contactAddress + mailer;其余参数保持默认即可稳定运行。

五、核心参数分类详解

本章对标注 ★ 的核心参数逐组展开:语义、默认值、修改影响与实测依据。

5.1 实例信息参数(instanceName / orgName / orgUrl / contactAddress)

这四个参数决定实例的「身份展示」:instanceName 显示在大屏菜单标题与 GET /api/config 响应中;orgName/orgUrl 展示在落地页;contactAddress 是实例发出邮件(验证信、通知)里用户可以直接回复的地址。修改方法与生效验证见 3.3 节实测(instanceName 与 contactAddress 修改后均从 /api/config 读到新值)。

5.2 服务监听与对外地址(protocol / hostname / port / public* 三元组 / serveStaticFiles)

两组地址参数的分工:protocol/hostname/port 是服务进程自己监听的地址;publicProtocol/publicHostname/publicPort 拼成 publicOrigin,是对外宣称的访问入口(生成邮件里的链接、Cookie 域、客户端 API 基址)。

  • publicProtocol 影响会话 Cookie 的 Secure 标记:服务端按 secure: publicProtocol === 'https' 设置,HTTP 直连部署必须改为 'http',否则登录成功但 Cookie 不下发、所有需认证接口 401(实测排障结论,见部署指南 FAQ);
  • publicPort 默认取 port 的值,置 null 表示对外 URL 不带端口(生产由 Nginx 占 80/443 时的标准写法);
  • serveStaticFiles 默认 true(Node 直接托管前端),生产形态官方置 false 交给 Nginx;本地无 Nginx 直连部署时应改回 true,否则只有 API 没有页面;
  • trustProxy 默认 'loopback, uniquelocal',Nginx 反代部署时保持默认即可正确识别 X-Forwarded-* 头。

实测佐证:环境 ② 以 NODE_CONFIG 注入 {"serveStaticFiles":true,"publicProtocol":"http","publicHostname":"localhost","publicPort":null} 后,HTTP 直连形态登录与页面托管均正常,/api/config 的 publicOrigin 精确反映为 http://localhost:8138。

5.3 CouchDB 连接参数(db.*)

主库连接七件套:db.protocol(默认 http)、db.hostname、db.port、db.username、db.password、db.suffix、db.getOrigin()(派生)。suffix 决定业务库名是否带后缀:生产模式自动 'prod',实际库名形如 items-prod、users-prod(启动日志可见 CouchDB: initializing design docs in users-prod)。

辅助参数:

  • db.follow.reset(默认 false):置 true 时外部索引从第一个序列号重建;
  • db.follow.freeze(默认 false):冻结库事件钩子,仅调试用;
  • db.follow.delay(默认 5000 ms):事件跟随延迟;
  • db.debug(默认 false):打印 CouchDB 请求参数,排障时临时开启;
  • db.backupDirectory(默认 backups/couchdb):内置备份脚本产物目录,Compose 形态固定为 /opt/inventaire_backups(对应独立的备份卷);
  • db.runMigrations(默认 true):启动时自动执行 CouchDB 数据迁移,升级版本时保持开启。

实测佐证:环境 ① 启动日志中全部业务库请求都指向 http://localhost:5984(db.hostname=localhost、db.port=5984 生效);环境 ② 生成的 local.js 中 db.hostname: 'couchdb' 与 compose 服务名一致。

5.4 LevelDB 与缓存参数(leveldb.* / cache.*)

Inventaire 用 LevelDB 承载两类数据:处理结果缓存与任务队列(level-jobs)。关键参数:

  • leveldb.directory(默认 ./db/leveldb):数据目录。leveldb.suffixDirectoryPathWithDbSuffix 为 true 时目录名带环境后缀;Compose 形态固定为 /opt/inventaire_leveldb 并挂独立卷持久化;
  • leveldb.inMemoryLRUCacheSize(默认 64 MB):内存 LRU 缓存上限,内存充裕的小型实例可维持默认,内存紧张时下调;
  • leveldb.ttlCheckFrequency(默认 60000 ms):过期巡检周期;
  • cache.defaultTTL(默认 30 天):通用缓存存活时间。

💡 说明:db.backupDirectory(备份目录)与 leveldb.directory(运行数据目录)都支持相对路径——相对路径以 Inventaire 项目根为基准解析;生产建议一律写绝对路径,避免随启动目录漂移。

5.5 Elasticsearch 参数(elasticsearch.*)

  • elasticsearch.origin(默认 http://localhost:9200):ES 集群地址,分体式部署必改;
  • elasticsearch.updateDelay(默认 1000 ms):写入后检索可见的延迟,与 ES refresh 机制对应;
  • elasticsearch.minReindexationInterval(默认 60000 ms):两次重索引的最小间隔,防止索引风暴;
  • elasticsearch.selfSignedCertificate(默认 false):ES 启用 HTTPS 自签证书时置 true;
  • elasticsearch.logResponses(默认 false):打印 ES 响应,排障用;
  • maintenance.ongoingReindexation(默认 false):重索引进行中的标记位,一般不动。

实测佐证:环境 ① 启动日志显示 elasticsearch index created: http://localhost:9200/groups(origin 生效且自动建索引);检索接口 GET /api/search 返回结果带 _score 评分,索引链路正常。

5.6 邮件参数(mailer.* / debouncedEmail / activitySummary / tokenDaysToLive)

邮件是注册闭环的硬依赖(账号验证邮件必须能发出),默认却是关闭的(mailer.disabled: true,开发友好),生产必须改:

mailer: {
  disabled: false,
  nodemailer: {
    host: 'smtp.provider-domain.org',
    port: 587,
    auth: {
      user: 'your-provider-username',
      pass: 'your-provider-password',
    },
  },
},

逐项说明:mailer.nodemailer.* 即 Nodemailer 的 SMTP 传输参数,port 常用 587(STARTTLS);生产模式自动追加 requireTLS: true;mailer.initDelay(默认 10000 ms)延迟初始化 SMTP 连接,避免拖慢启动;mailer.deniedAddressesPatterns 可屏蔽特定收件域。Compose 形态由 MAILER_SMTP_* 四个变量生成(见 3.5 节映射表)。

配套三个参数组:debouncedEmail(把短时间多条通知合并成一封摘要邮件,crawlPeriod 60s 扫描一次、debounceDelay 10min 合并窗口,联邦模式自动禁用);activitySummary(周期性活动摘要邮件,生产默认开启、每小时最多 maxEmailsPerHour 封);tokenDaysToLive(邮箱验证令牌有效期,默认 3 天)。

官方文档提供 npm run send-test-email your@email.org 一键发测试邮件验证 SMTP 配置(出处:仓库 docs/configuration/email_provider.md)。

5.7 媒体存储参数(mediaStorage.*)

用户头像、小组封面(实体主库还有书封)的上传图片存储位置:

💡 技术拓展:自托管应用的图片存储后端不止本地磁盘一种,常见选项对比——

存储模式原理/特点适用场景
local 本地目录直接写服务器文件系统,备份即拷目录单机部署、中小规模(最常用)
OpenStack Swift 对象存储经 Swift API 上传,容量弹性、天然多副本多实例共享存储、大图片量
S3 兼容对象存储AWS S3/MinIO 等通用协议主流云环境(Inventaire 未支持,仅 Swift)

Inventaire 实际支持 local 与 swift 两种(mediaStorage.mode),未提供 S3 协议接入;单机自托管选 local 即可。

  • mediaStorage.mode:'local'(默认)或 'swift';
  • mediaStorage.local.directory(默认 ./storage):本地存储目录,Compose 形态固定 /opt/inventaire_local_media_storage 并挂独立卷;
  • mediaStorage.images.maxSize(默认 1600 px):上传图最大边长,超限自动缩放;
  • mediaStorage.swift.* 六项(username/password/authUrl/publicURL/tenantName/region):仅 mode=swift 时生效,对应 OpenStack 凭据;
  • remoteImages.useEntitiesHostCachedImages(默认 false):联邦实例展示远端实体图片时改 true 可直接取用实体主库(inventaire.io)的缩放缓存,显著加速图片加载。

5.8 地图与统计参数(mapTiles / mapTilesAccessToken / matomo.*)

  • mapTiles.tileUrl(默认 OpenStreetMap 官方瓦片):服务端地图接口使用的瓦片源,可自建瓦片服务替换;
  • mapTilesAccessToken:MapBox 令牌,客户端地图功能使用;不填实例可运行,但地图相关页面不可用(官方 .env 模板将其列为必填占位);
  • matomo.enabled/endpoint/idsite/rec:Matomo(ex-Piwik)隐私友好统计埋点,默认关闭;开启后访问统计上报到 endpoint 指定的 Matomo 实例(Compose 形态对应 MOTOMO_* 四个变量,官方变量名沿用旧拼写 Motomo)。

5.9 联邦模式参数(federation.remoteEntitiesOrigin / NODE_APP_INSTANCE / jobs.*)

Inventaire 的实例分两类角色:实体主库(primary,自建图书/著作/人物实体数据库并承担 Wikidata 同步任务)与联邦实例(federated,复用旗舰实例 inventaire.io 的实体数据,专注本地用户/物品/小组)。角色由 NODE_APP_INSTANCE 环境变量选择:不设 = 单机默认模式,federated = 联邦实例,primary = 实体主库。

  • federation.remoteEntitiesOrigin:远端实体来源域名,联邦模式由 production-federated.ts 固定为 https://inventaire.io;开发环境想用旗舰站实体可同样设置。实测佐证:环境 ②(federated)的 /api/config 返回 remoteEntitiesOrigin: https://inventaire.io,且实体检索接口返回 entities should be searched on remote instance 提示(本地只搜用户与小组);
  • jobs.*:基于 LevelDB 队列的 5 个后台任务开关(inv:deduplicate 实体去重、entity:auto-enrich-editions 版本信息自动补全、entity:popularity 热度计算、wd:entity:indexation Wikidata 双库索引同步、post:activity 联邦宇宙动态发布),主库实例默认全开,联邦实例自动关闭前 4 个(见 3.2 节 production-federated.ts 实录)。

5.10 安全与限流参数(rateLimiting / outgoingRequests / spam / waitFactor)

  • rateLimiting.disabled(默认 false):接口限流总开关,生产保持开启;timeWindow(默认 30 分钟)为限流统计窗口;exemptedUserAgents/restrictedUserAgents 可对爬虫类 UA 放行或限制;
  • outgoingRequests.rejectPrivateUrls(默认 true):SSRF 防护——服务端代替用户抓取外部图片/数据时拒绝内网地址,生产严禁关闭;
  • outgoingRequests.baseBanTime(默认 5000 ms)/ banTimeIncreaseFactor(默认 4)/ maxBanTime(默认 24 h):对外请求失败后的自动封禁梯度(5s 起步、每次 ×4、封顶 1 天),防止故障外部服务拖垮自身;
  • spam.suspectKeywords(默认含 'agent'/'SEO'/'marketing'/'shopping'):用户简介、物品评论等文本中出现这些词时在用户文档打可疑标记,管理员可在用户管理页复核;
  • cookieMaxAge(默认 180 天)与 autoRotateKeys(默认 true):会话有效期与会话签名密钥轮换;多实例负载均衡时共享会话需固定一套密钥——一台 autoRotateKeys: true、其余 false;
  • waitFactor(默认 1):全局请求等待时间倍率,机器负载高时可调大让重操作更耐心。

5.11 Wikidata 接入参数(wikidataOAuth / wikidataBotAccount / wikidataEdit)

仅实体主库实例需要关注:wikidataOAuth.consumer_key/consumer_secret(用户用 Wikidata 账号登录本实例的 OAuth 接入,在 Wikimedia 官方申请);wikidataBotAccount.*(服务器自身对 Wikidata 做自动化编辑的机器人 OAuth 四件套);wikidataEdit.dry(默认 true 的 dry-run 保护,生产置 false 才会真正写 wikidata.org)。联邦实例无需配置这些参数——Wikidata 同步任务已自动关闭。

六、生产配置模板

以下模板按「4 GB 内存、40 GB 磁盘、50 人以内团队的单机自托管藏书库」场景给出(与本文实测环境同规格),两个形态二选一。

形态 A:源码安装(Ubuntu 22.04,Nginx TLS 反代) —— config/local.ts:

const config = {
  // 主库凭据(与 CouchDB 管理员一致)
  db: {
    username: "admin",
    password: "q1w2e3r4",
    // 分体式部署时改为 CouchDB 实际地址
    // hostname: "192.168.1.10",
  },
  // 对外地址(Nginx 占 443,Node 只听本机)
  publicProtocol: "https",
  publicHostname: "books.example.org",
  publicPort: null,
  serveStaticFiles: false,
  // 实例身份
  instanceName: "My Home Library",
  orgName: "Example Organization",
  orgUrl: "https://books.example.org",
  contactAddress: "admin@books.example.org",
  // 邮件(生产必须,否则注册闭环断裂)
  mailer: {
    disabled: false,
    nodemailer: {
      host: "smtp.example.org",
      port: 587,
      auth: { user: "books", pass: "q1w2e3r4" },
    },
  },
  // 图片本地存储(绝对路径,纳入备份计划)
  mediaStorage: {
    mode: "local",
    local: { directory: "/var/lib/inventaire/storage" },
  },
}

export default config

启动方式(以生产环境变量运行,联邦实例):

cd /home/inventaire/inventaire
NODE_ENV=production NODE_APP_INSTANCE=federated node_modules/.bin/tsx server/server.ts

形态 B:Docker Compose 部署 —— 部署目录 .env(完整 24 变量中与上述场景相关的关键项):

PUBLIC_HOSTNAME=books.example.org
COUCHDB_USER=admin
COUCHDB_PASSWORD=q1w2e3r4
INSTANCE_NAME=My Home Library
ORG_NAME=Example Organization
ORG_URL=https://books.example.org
CONTACT_ADDRESS=admin@books.example.org
MAILER_SMTP_HOST=smtp.example.org
MAILER_SMTP_PORT=587
MAILER_SMTP_USERNAME=books
MAILER_SMTP_PASSWORD=q1w2e3r4
MAP_TILES_ACCESS_TOKEN=your-mapbox-token
MOTOMO_ENABLED=false
MOTOMO_ENDPOINT=https://matomo.example.org/matomo.php
MOTOMO_IDSITE=1
MOTOMO_REC=1
MEDIA_STORAGE_MODE=local
SWIFT_USERNAME=placeholder
SWIFT_PASSWORD=placeholder
SWIFT_AUTH_URL=https://swift.example.org/auth
SWIFT_PUBLIC_URL=https://swift.example.org/public
SWIFT_TENANT_NAME=12345678
SWIFT_REGION=SBG-1

要点回顾:.env 管不到的连接类参数(CouchDB/ES 地址、LevelDB 目录)由 entrypoint 硬编码为套件内部服务名,单机套件部署无需关心;HTTP 直连测试形态另需 NODE_CONFIG 注入四项(见 3.5 节实测)。磁盘规划:主库卷 + LevelDB 卷 + 图片卷各按 10 GB 起步,备份卷按主库体积 2 倍预留。

七、常见问题

Q1:改了配置文件重启后参数没生效?

三个常见原因逐一排查:① 改错了文件——源码安装改的是项目根 config/local.ts,不是 dist/config/local.js(后者是编译产物,TS 源文件才是运行时真源,npm run build 时从 config/ 生成);② 没重启进程——配置只在启动时读取;③ 被 NODE_CONFIG 覆盖——环境变量优先级最高,检查启动环境里是否残留(echo $NODE_CONFIG)。

Q2:启动直接失败,日志报 Cannot parse config file ... local.js: SyntaxError?

配置文件是 JS 模块,语法错误会在启动时抛出,报错行号即出错位置。Compose 形态的经典成因是用 docker run --env-file 传入带引号的 .env——单引号字面量进入 entrypoint 模板生成非法 JS(如 instanceName: ''My Inventaire Instance'')。Compose 的 env_file 会剥引号而 docker run --env-file 不会,两者不通用(实测复现结论)。

Q3:NODE_ENV=production 启动后为什么强制要求改一堆 OVERRIDE?

production.ts 把 publicHostname、db.username、db.password 显式设为 'OVERRIDE' 占位,就是逼部署者显式覆盖:不改就带着字面量 OVERRIDE 运行,数据库连不上或对外地址错误。这是官方故意的防呆设计。

Q4:联邦实例和主库实例怎么选,配置上差在哪?

只差 NODE_APP_INSTANCE 一个环境变量:federated 自动获得 remoteEntitiesOrigin=https://inventaire.io、关闭实体类后台任务;primary 需自行配置 Wikidata OAuth 并保证磁盘充裕(自建全量实体库)。单机自托管藏书库用 federated(官方 Compose 镜像默认值)。

Q5:想确认限流、SSRF 防护这些安全参数真的在工作?

rateLimiting 与 outgoingRequests 参数不在 /api/config 暴露(避免向攻击者泄露防护配置),确认方式是看源码 config/default.ts 的生效值与版本一致,并结合行为验证:超频请求被 429 拒绝、构造内网 URL 的图片代理请求被拒绝。默认值即安全值,无特殊需求不要关闭。

八、参考链接