Habitica v5.52.0 配置参数详解

Habitica v5.52.0 配置体系逐项实测详解:nconf 三级加载优先级、28 键最小可运行集、BASE_URL 301 重定向、WEB_CONCURRENCY 集群、限流 429、SESSION_SECRET_KEY hex 硬约束、BullMQ 邮件队列与 Cron 保护开关,含最小配置三连崩排障。

HabiticaHabitica自托管2026/10/28

Habitica v5.52.0 配置参数详解

目标:逐项讲清 Habitica v5.52.0 的配置体系——config.json 的加载机制与优先级、最小可运行参数集(含缺失即崩溃的加载期硬读取键)、数据库/缓存/Web 行为/进程集群/限流/邮件队列/Cron 各域参数的语义与实测行为。全部结论基于 Ubuntu 22.04 服务器上的 v5.52.0 实测(源码交叉印证,来源与查询日期在各节标注),安装前置步骤见本专栏安装指南篇。

一、环境信息

本篇实测环境与取证口径:

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64);Habitica v5.52.0(git tag 源码,commit 12091ca)
  • 服务形态:systemd 托管,入口 /opt/habitica/website/transpiled-babel/index.js(NODE_ENV=production)
  • 依赖组件:Node.js v20.20.2 / MongoDB v7.0.45(单节点副本集 rs)/ Redis v8.10.3
  • 配置文件:/opt/habitica/config.json(由 config.json.example 改写)
  • 服务地址:http://10.89.71.10:3000(本机内网 IP,读者替换)
  • 演示账号:habadmin / q1w2e3r4
  • 参数行为取证:v5.52.0 源码实查(website/server/libs/setupNconf.js、libs/config.js、middlewares/redirects.js、middlewares/rateLimiter.js、middlewares/maintenanceMode.js、libs/worker.js、libs/email.js、libs/cron.js、libs/encryption.js,查询日期 2026-10-28)+ 服务器实测回显

💡 说明:文中标注「实测」的行为均为上述环境在 2026-10-28 的可复现结果;源码行号为 v5.52.0 tag 实查位置,升级后可能漂移。

二、配置加载机制与优先级

2.1 加载入口与文件定位

Habitica 的全部运行参数收敛于一个 JSON 文件:仓库根的 config.json。加载逻辑在 website/server/libs/setupNconf.js(v5.52.0 源码实查):

const PATH_TO_CONFIG = join(resolve(__dirname, '../../../config.json'));

export default function setupNconf (file, nconfInstance = nconfDefault) {
  const configFile = file || PATH_TO_CONFIG;
  nconfInstance
    .argv()
    .env()
    .file('user', configFile);
  nconfInstance.set('IS_PROD', nconfInstance.get('NODE_ENV') === 'production');
  nconfInstance.set('IS_DEV', nconfInstance.get('NODE_ENV') === 'development');
  nconfInstance.set('IS_TEST', nconfInstance.get('NODE_ENV') === 'test');
  process.env.CONTENT_SWITCHOVER_TIME_OFFSET = nconfInstance.get('CONTENT_SWITCHOVER_TIME_OFFSET') || 0;
}

三个要点:

  1. 优先级为 命令行参数 > 环境变量 > config.json(nconf 的 .argv().env().file() 链式注册顺序即优先级从高到低);
  2. IS_PROD/IS_DEV/IS_TEST 是派生键,由 NODE_ENV 计算后写回 nconf,全服务端统一读取——NODE_ENV 同时还控制入口文件是否挂载 @babel/register(index.js 首行判断 process.env.NODE_ENV !== 'production'),因此生产部署必须双通道生效:config.json 写 "NODE_ENV": "production" 且 systemd 单元带 Environment=NODE_ENV=production;
  3. 配置文件路径按入口文件位置回溯三级定位,从 website/transpiled-babel/libs/ 回溯到仓库根的 config.json——替换入口位置会导致找不到配置。

2.2 优先级实测:环境变量覆盖 config.json

实测方法:config.json 写 "MAINTENANCE_MODE": "false"、"IGNORE_REDIRECT": "false",再以环境变量方式同时注入两个键的 true 值启动:

sudo systemctl stop habitica-web
su habitica -c 'cd /opt/habitica && NODE_ENV=production MAINTENANCE_MODE=true IGNORE_REDIRECT=true nohup node website/transpiled-babel/index.js > /tmp/maint-test.log 2>&1 &'
sleep 15
curl -s -o /dev/null -w 'GET / HTTP:%{http_code}\n' http://127.0.0.1:3000/
curl -s http://127.0.0.1:3000/api/v3/status
grep -c 'Connected with Mongoose' /tmp/maint-test.log

实测回显:

GET / HTTP:503
{"error":"Maintenance","message":"Server offline for maintenance."}
0

config.json 明明写 false,运行行为却按 true 执行(首页 503 维护页、API 返回维护 JSON),证明环境变量优先于 config.json。同一组回显还顺带证明了 MAINTENANCE_MODE=true 的两个附带行为:全站 503 与跳过 MongoDB 连接(日志中 Connected with Mongoose 计数为 0)。这一机制是 systemd 托管场景做临时开关的标准手法——不改配置文件,直接在单元文件加 Environment= 覆盖。

三、最小可运行配置集

v5.52.0 的 config.json.example 有 90+ 个键,但多数是第三方集成的占位值。实测逐项验证出「缺一个就起不来」的加载期硬读取键与核心运行键共 28 个,以下为实测通过的最小集(分类说明见后续各节):

分类键实测值作用
基础PORT3000HTTP 监听端口(server.js 中 app.set('port', nconf.get('PORT')))
基础NODE_ENVproduction运行模式,派生 IS_PROD/IS_DEV/IS_TEST
基础BASE_URLhttp://10.89.71.10:3000站点规范地址,重定向/邮件链接均基于它
数据库NODE_DB_URImongodb://localhost:27017/habitica?replicaSet=rs&directConnection=true主库连接串
数据库TEST_DB_URImongodb://localhost:27017/habitica-test?replicaSet=rs&directConnection=true测试库连接串(IS_TEST 时替代主库)
数据库MONGODB_POOL_SIZE"10"连接池上限(maxPoolSize)
缓存REDIS_HOST / REDIS_PORT / REDIS_PASSWORD127.0.0.1 / "6379" / ""Redis 连接参数(限流、会话、队列共用)
队列WORKER_REDIS_URLredis://127.0.0.1:6379BullMQ 任务队列连接串,缺失邮件类接口直接 500
会话SESSION_SECRET任意强随机串会话签名密钥
会话SESSION_SECRET_KEY64 位十六进制AES-256-GCM 加密密钥(密码重置码等),必须 hex
进程WEB_CONCURRENCY1cluster worker 数(0 或缺省 = 单进程不启用 cluster)
邮件EMAIL_SERVER_URL / EMAIL_SERVER_AUTH_USER / EMAIL_SERVER_AUTH_PASSWORD占位即可邮件投递 worker 调用的上游接口
邮件ADMIN_EMAIL / FLAG_REPORT_EMAIL有效邮箱管理员邮箱;后者缺失启动崩溃(split 硬读取)
CronMAINTENANCE_MODE / CRON_SAFE_MODE / CRON_SEMI_SAFE_MODE"false"维护模式与 Cron 保护开关
限流RATE_LIMITER_ENABLED"false"(生产建议 true)API 限流总开关
日志DISABLE_REQUEST_LOGGING"true"关闭请求级日志
WebIGNORE_REDIRECT"false"生产 BASE_URL 强制重定向开关
支付PAYPAL_MODE / PAYPAL_CLIENT_ID / PAYPAL_CLIENT_SECRETsandbox + 占位缺失启动崩溃(paypal-sdk configure 校验)
登录GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET占位缺失启动崩溃(passport 策略注册期)

⚠️ 排障实录(缺失即崩溃的三个键,安装篇首次启动实测命中):

  1. PAYPAL_MODE 缺失 → worker 启动即抛 Error: Mode must be "sandbox" or "live"(node_modules/paypal-rest-sdk/lib/api.js:26),cluster 主进程不断重拉 worker,日志循环出现 worker:xxxx disconnect! new worker:yyyy fork;
  2. FLAG_REPORT_EMAIL 缺失 → TypeError: Cannot read properties of undefined (reading 'split'),出错点 libs/challenges/reporting.js:16——模块加载期对 nconf.get('FLAG_REPORT_EMAIL') 直接 .split(',');
  3. GOOGLE_CLIENT_ID/SECRET 缺失 → passport 构造异常(passport-google-oauth20/lib/strategy.js:52,注册点 libs/setupPassport.js:16)。

三处都是 require 阶段的硬读取,任何一个未命中都会让服务陷入「worker 反复崩溃重启」,且生产默认无控制台日志、极易误判。自托管部署建议先照抄安装篇的最小集,再逐域加参数。

四、数据库与缓存参数

4.1 NODE_DB_URI:副本集是硬要求

官方示例连接串为 mongodb://localhost:27017/habitica-dev?replicaSet=rs&directConnection=true&readPreference=secondary(config.json.example 实查),官方编排文件中数据库以 --replSet rs 参数启动。自托管单机照抄该形态即可,三个参数的作用:

  • replicaSet=rs:声明副本集名,Habitica 依赖副本集特性的写入路径(事务与变更流);
  • directConnection=true:跳过驱动对副本集成员的自动发现,直连指定节点(单机部署必需,多副本时可去掉);
  • readPreference=secondary:官方开发配置带了从节点读偏好,单节点副本集上主节点即唯一成员,实测可省略(本篇实测连接串未携带,服务正常)。

数据库名(连接串 path 部分)按需自定义,首次启动时 mongoose 自动建库建集合。

4.2 MONGODB_POOL_SIZE:连接池上限实测

源码映射(libs/mongoose.js:18-19 实查):

if (MIN_POOL_SIZE) mongooseOptions.minPoolSize = Number(MONGODB_MIN_POOL_SIZE);
if (POOL_SIZE) mongooseOptions.maxPoolSize = Number(POOL_SIZE);

即 MONGODB_POOL_SIZE 直接映射 mongoose maxPoolSize(另有 MONGODB_MIN_POOL_SIZE 映射 minPoolSize、MONGODB_SOCKET_TIMEOUT 映射 socketTimeoutMS,均非必填)。连接按需惰性建立,池上限决定「最忙时最多建多少条」。实测对比(同流程:改配置重启 → 首页与 API 各请求一次 → mongosh 查 serverStatus):

MONGODB_POOL_SIZEmongosh db.serverStatus().connections.current
"2"13
"10"19

可见池上限放大后服务端可见连接数随之上升(数字含 mongosh 自身 1 条与监控连接,取同流程差值看趋势:上限从 2 提到 10,实际建立的连接明显增多)。单机自托管默认 10 足够;小内存机器可降到 2-4 限制内存占用。

4.3 REDIS_HOST / REDIS_PORT / REDIS_PASSWORD

三键供两个客户端使用:限流中间件(middlewares/rateLimiter.js:22-24 实查读取三键建立 ioredis 连接)与开发态会话等场景。Redis 无密码时 REDIS_PASSWORD 留空字符串即可——客户端仅在密码真值时发送 AUTH(libs/redis.js:10-12 实查:if (connectionOptions.password) 才挂 password)。生产建议开启 Redis requirepass 并同步三键。

五、Web 行为与重定向参数

5.1 BASE_URL 与生产强制重定向(实测)

middlewares/redirects.js 的 forceHabitica 中间件(v5.52.0 源码实查):

function forceHabitica (req, res, next) {
  if (IS_PROD && !IGNORE_REDIRECT && req.hostname !== BASE_URL_HOST && nonApiUrl(req) && req.method === 'GET') {
    return res.redirect(301, BASE_URL + req.url);
  }
  return next();
}

触发条件五个全部满足才重定向:生产模式(IS_PROD)、未关闭重定向(!IGNORE_REDIRECT)、请求主机名与 BASE_URL 主机不一致、非 API 路径(URL 不含 /api/)、GET 方法。实测全链路(BASE_URL=http://10.89.71.10:3000):

curl -s -D- -o /dev/null http://127.0.0.1:3000/ | head -1
curl -s -D- -o /dev/null http://127.0.0.1:3000/ | grep -i location
curl -s -o /dev/null -w 'api GET HTTP:%{http_code}\n' http://127.0.0.1:3000/api/v3/status
curl -s -I -o /dev/null -w 'HEAD HTTP:%{http_code}\n' http://127.0.0.1:3000/
curl -s -o /dev/null -w 'GET BASE_URL HTTP:%{http_code}\n' http://10.89.71.10:3000/
HTTP/1.1 301 Moved Permanently
Location: http://10.89.71.10:3000/
api GET HTTP:200
HEAD HTTP:200
GET BASE_URL HTTP:200

结论与使用建议:主机名不一致的首页 GET 请求 301 到 BASE_URL(Location 头实测指向配置地址);API 请求与 HEAD 请求豁免;用 BASE_URL 本身访问则 200 直达。BASE_URL 必须与用户实际访问地址一致(协议/域名/IP/端口),反代场景填对外域名。内网调试想免重定向,把 IGNORE_REDIRECT 设 "true"。

5.2 IGNORE_REDIRECT:关闭强制重定向(实测)

config.json 保持 "IGNORE_REDIRECT": "false" 不动,仅以环境变量 IGNORE_REDIRECT=true 重启后实测:

curl -s -o /dev/null -w 'GET 127.0.0.1 HTTP:%{http_code}\n' http://127.0.0.1:3000/
curl -s -o /dev/null -w 'GET BASE_URL HTTP:%{http_code}\n' http://10.89.71.10:3000/
GET 127.0.0.1 HTTP:200
GET BASE_URL HTTP:200

与 5.1 节的 301 形成对照组:同一请求,开关打开后 127.0.0.1:3000 直接 200。相关键 TRUSTED_DOMAINS(逗号分隔的可信域名清单,供跨域信任判断)、SKIP_SSL_CHECK_KEY(携带该 key 的请求跳过 HTTPS 强制检查,用于反代终止 TLS 的调试场景)按需保留默认。

5.3 请求日志参数

DISABLE_REQUEST_LOGGING(示例默认 true):关闭每个请求的访问日志行;LOG_REQUESTS_EXCESSIVE_MODE 与 SLOW_REQUEST_THRESHOLD(毫秒)用于超时请求的额外记录。自托管单机建议保持 DISABLE_REQUEST_LOGGING: "true"(配合第六节的生产日志策略,避免噪音)。

六、进程、集群与维护模式参数

6.1 WEB_CONCURRENCY:cluster worker 数(实测)

进程模型(index.js + libs/config.js 源码实查):

const CORES = Number(nconf.get('WEB_CONCURRENCY')) || 0;
const ENABLE_CLUSTER = CORES !== 0 && cluster.isMaster && (IS_DEV || IS_PROD);

WEB_CONCURRENCY 为正整数时主进程 fork 对应数量 worker(worker 崩溃自动重拉),为 0 或缺省时单进程直跑。实测 WEB_CONCURRENCY=3(systemd 单元 drop-in 注入 Environment=WEB_CONCURRENCY=3):

ps -ef | grep 'transpiled-babel' | grep -v grep
habitica    8915       1  0 07:45 ?  /usr/bin/node /opt/habitica/website/transpiled-babel/index.js
habitica    8923    8915  5 07:45 ?  /usr/bin/node /opt/habitica/website/transpiled-babel/index.js
habitica    8924    8915  5 07:45 ?  /usr/bin/node /opt/habitica/website/transpiled-babel/index.js
habitica    8925    8915  5 07:45 ?  /usr/bin/node /opt/habitica/website/transpiled-babel/index.js
curl -s http://10.89.71.10:3000/api/v3/status
{"success":true,"data":{"status":"up"},"appVersion":"5.52.0"}

1 个主进程(PPID 1)+ 3 个 worker(PPID 8915),多 worker 下 API 正常。单机 2 核以内建议保持 1;CPU 富余且并发高时可提升。注意改该键要配合内存评估——每 worker 常驻 150MB 级。

6.2 MAINTENANCE_MODE:维护模式(实测)

设为 "true" 后(2.2 节同一次实测),行为三件套:

  1. 全站 503:HTML 请求渲染维护页(views/static/maintenance),API 请求返回 JSON——实测 curl http://127.0.0.1:3000/api/v3/status 得 {"error":"Maintenance","message":"Server offline for maintenance."};
  2. 启动不连库:libs/mongoose.js:27 实查 if (MAINTENANCE_MODE !== 'true') 才执行 mongoose.connect,实测日志中 Connected with Mongoose 计数为 0;
  3. 可选配 MAINTENANCE_START/MAINTENANCE_END 展示维护时间窗。

适用场景:数据库迁移、大版本升级时把应用置为维护态,用户侧得到明确维护页而非报错。

6.3 ENABLE_CONSOLE_LOGS_IN_PROD:生产控制台日志

生产模式 winston 默认无任何 Console transport(libs/logger.js:52 实查 if (IS_PROD) 分支内再判断该键),表现是 journald 里几乎看不到应用日志、且 winston 会打 [winston] Attempt to write logs with no transports 提示。实测:默认配置下 journalctl -u habitica-web 仅有 systemd 生命周期行;在 systemd 单元追加 Environment=ENABLE_CONSOLE_LOGS_IN_PROD=true 并重启后,文本与 JSON 双格式日志进入 journald。该键属临时排障开关,长期开启会产生大量噪音,正式运行建议接 LOGGLY_TOKEN/LOGGLY_SUBDOMAIN 或保持关闭。

七、安全与会话参数

7.1 SESSION_SECRET 与 SESSION_SECRET_KEY(关键差异实测)

两个键极易混淆,语义完全不同:

  • SESSION_SECRET:普通会话签名密钥,任意强随机字符串即可,泄露只影响会话伪造风险;
  • SESSION_SECRET_KEY:AES-256-GCM 对称加密密钥,源码(libs/encryption.js:16-17 实查)按 hex 解码:
const SESSION_SECRET_KEY = nconf.get('SESSION_SECRET_KEY');
const key = Buffer.from(SESSION_SECRET_KEY, 'hex');

密钥算法要求 32 字节,即必须提供 64 位十六进制字符。实测反面案例:把 SESSION_SECRET_KEY 填成普通字符串(非 hex),服务能正常启动、日常 API 全部正常,但触发密码重置时服务端 encrypt() 在 createCipheriv 抛错,接口返回 500:

{"success":false,"error":"InternalServerError","message":"An unexpected error occurred."}

将 SESSION_SECRET_KEY 改为 64 位 hex(如 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef)并重启后复测:

{"success":true,"data":{},"message":"If we have your email or username on file, instructions for setting a new password have been sent..."}

生成方式:openssl rand -hex 32。这是一个「平时无感、用时才炸」的隐蔽配置点,务必在首次部署时写对。

7.2 SITE_HTTP_AUTH_*:站点级 HTTP Basic 认证

SITE_HTTP_AUTH_ENABLED(true 时全站挂 Basic 认证)、SITE_HTTP_AUTH_USERNAMES 与 SITE_HTTP_AUTH_PASSWORDS(逗号分隔多组)。适用于内网试用、演示环境加一层门禁。本篇实测未启用,保持 false。

7.3 限流参数(RATE_LIMITER_*,429 实测)

参数全集与默认值(middlewares/rateLimiter.js:25-29 源码实查):

键默认值作用
RATE_LIMITER_ENABLEDfalse限流总开关(=== 'true' 才启用)
RATE_LIMITER_BASE_POINTS30基础桶容量(次)
RATE_LIMITER_BASE_DURATION60桶周期(秒),按 User ID 或 IP 计
RATE_LIMITER_REGISTRATION_COST10注册接口单次扣点
RATE_LIMITER_LOGIN_COST10登录接口单次扣点
RATE_LIMITER_IP_COST5未认证请求按 IP 单次扣点
RATE_LIMITER_V4_POINTS200v4 API 独立桶容量

实测:设 RATE_LIMITER_ENABLED: "true" 重启后连续请求 /api/v4/status(未认证,按 IP 每次扣 5 点):

前 8 次全部 200,响应头实测:

X-RateLimit-Limit: 200
X-RateLimit-Remaining: 160
X-RateLimit-Reset: Wed Oct 28 2026 08:03:06 GMT+0000 (Coordinated Universal Time)

200 点桶、单请求扣 5 点(200 - 8×5 = 160,严丝合缝)。继续压测至桶耗尽,实测回显:

200 200 200 200 200 200 200 429 429 429

429 响应头实测:

X-RateLimit-Limit: 200
X-RateLimit-Remaining: 0

(桶内余量不足时还携带 Retry-After 头,值为窗口重置秒数。)限流的 Redis 键前缀 api(keyPrefix),可通扫观察。公网部署强烈建议开启;本篇实验完毕已将开关还原为 false。

八、邮件与任务队列参数

8.1 邮件链路架构

Habitica 的邮件是异步队列模型(libs/email.js + libs/worker.js 源码实查):

  1. Web 进程在 IS_PROD 时把邮件任务投递进 BullMQ 队列(email.js:150:if (IS_PROD && mailingInfoArray.length > 0) return worker.sendJob('email', {...}));
  2. 队列建立在 Redis 上,连接串取 WORKER_REDIS_URL(worker.js:11 实查,未配置则根本不创建队列对象);
  3. 真正发送邮件的是独立的邮件 worker(官方 habitica-workers 项目):它消费队列,把任务 POST 到 EMAIL_SERVER_URL(带 EMAIL_SERVER_AUTH_USER/PASSWORD 基本认证)完成投递。

自托管单机若只跑 Web 进程,邮件任务会滞留 Redis 队列——接口层不报错,但邮件不会发出。这是自托管邮件功能「成功却收不到」的架构性原因。

8.2 WORKER_REDIS_URL:缺失即 500(实测)

未配置该键时,sendJob('email') 因队列对象不存在直接 Promise.reject(new Error('Queue email does not exist'))(worker.js:38-40 实查),调用链上的密码重置接口实测 500。补 "WORKER_REDIS_URL": "redis://127.0.0.1:6379" 重启后复测:

curl -s -X POST http://10.89.71.10:3000/api/v4/user/reset-password \
  -H 'Content-Type: application/json' -d '{"email":"admin@example.com"}'
redis-cli llen bull:emails:wait
{"success":true,"data":{},"message":"If we have your email or username on file, instructions for setting a new password have been sent..."}
1

接口 200,队列 bull:emails:wait 出现 1 条待消费任务。任务载荷实测(Redis hash):

name: "reset-password"
data: {"emailType":"reset-password","to":[{"name":"habadmin","email":"admin@example.com",...}],"variables":[{"name":"BASE_URL","content":"http://10.89.71.10:3000"},{"name":"PASSWORD_RESET_LINK","content":"http://..."}]}

链接域名取自 BASE_URL,用户端收到的重置链接可直接打开。另有 WORKER_REDIS_USERNAME/WORKER_REDIS_PASSWORD/WORKER_REDIS_KEY_PREFIX 支持队列 Redis 的认证与前缀隔离。生产部署两条路径:跑官方邮件 worker 消费队列(需自建符合 EMAIL_SERVER_URL 协议的投递端),或接受「队列滞留」并在应用层关闭邮件类入口。本篇实测止于队列层,投递端未搭建,如实说明。

九、Cron 与内容运营参数

9.1 CRON_SAFE_MODE / CRON_SEMI_SAFE_MODE:Cron 保护开关

Habitica 的每日结算(cron)在用户首次访问或定时触发时结算前一日 Dailies:未完成的 Dailies 扣血、累积 Boss 伤害。两个保护开关改变结算行为(libs/cron.js v5.52.0 源码实查,行号为 253 与 277 附近):

  • CRON_SAFE_MODE: "true":未完成 Dailies 不扣血(if (CRON_SAFE_MODE) { dailyChecked += 1 } else { perfect = false; ... scoreTask(direction: 'down', ...) }),Mana 按全额回满,且跳过对已终止订阅用户的清理逻辑(cron.js:179);调试数值系统或迁移数据时使用;
  • CRON_SEMI_SAFE_MODE: "true":照常扣血但不累积 Boss 任务伤害(if (!CRON_SEMI_SAFE_MODE) { user.party.quest.progress.down += delta ... }),用于队伍任务期避免成员因现实原因漏打卡被团灭。

两键均为字符串 "true"/"false"。说明:两键的行为差异取自源码级分析,跨天 cron 结算需真实跨越自然日复测,本篇实验周期内未做跨日实测,特此如实说明。相关键 CRON_SEMI_SAFE_MODE 之外还有 ACCOUNT_MIN_CHAT_AGE(聊天防灌水)等运营键,按需取用。

9.2 CONTENT_SWITCHOVER_TIME_OFFSET 与 TIME_TRAVEL_ENABLED

  • CONTENT_SWITCHOVER_TIME_OFFSET(默认 8):内容更新(新活动、新商店物品)的时区偏移,8 即东八区零点切换;setupNconf 会把它写回 process.env 供公共模块使用(2.1 节源码可见);
  • TIME_TRAVEL_ENABLED(默认 false):允许伪造系统时间调试 Cron 与周期逻辑,仅调试用;
  • DEBUG_ENABLED(默认 false):调试态辅助开关。

十、第三方集成占位键分级说明

config.json.example 中其余键按「缺省后果」分三级管理:

级别键族说明
启动必需(占位即可)PAYPAL_MODE/CLIENT_ID/CLIENT_SECRET、GOOGLE_CLIENT_ID/SECRET加载期硬读取,缺了崩溃,自托管不接支付/谷歌登录可保留占位值
功能开关型FACEBOOK_KEY/SECRET、APPLE_AUTH_*、STRIPE_*、AMAZON_PAYMENTS_*、PAYPAL_BILLING_PLANS_*社交登录与订阅支付,不接则对应功能不可用,不影响启动
可选观测型LOGGLY_*、GA_ID、AMPLITUDE_*、SLACK_URL、S3_*、PUSH_CONFIGS_*日志/统计/通知/推送集成,全部可留默认占位

💡 技术拓展:第三方登录在服务端的形态是 passport 策略注册(如 7.1 节崩溃三的 passport-google-oauth20),占位值能让策略注册成功,但用户实际点击「Google 登录」时会跳到占位 client_id 对应的无效 OAuth 页面——因此「能启动」与「能用」是两回事,需要真实启用时再填真实凭据。这也是许多自托管应用「示例配置必须整份抄」的原因:占位不是装饰,是启动依赖。

十一、常见配置排障实录汇总

Q1:worker 反复崩溃重启(worker:xxxx disconnect! new worker:yyyy fork 循环)? 按第三节「三连崩」逐键核对:PAYPAL_MODE、FLAG_REPORT_EMAIL、GOOGLE_CLIENT_ID/SECRET。临时加 Environment=ENABLE_CONSOLE_LOGS_IN_PROD=true 看完整堆栈。

Q2:首页访问 301 跳到别处? 生产模式 BASE_URL 强制重定向(5.1 节实测),确认访问地址与 BASE_URL 一致,或临时 IGNORE_REDIRECT: "true"。

Q3:密码重置接口 500? 两个已知根因:SESSION_SECRET_KEY 非 64 位 hex(7.1 节实测修复前后对照);WORKER_REDIS_URL 缺失导致队列不存在(8.2 节实测)。按顺序排查。

Q4:邮件「发送成功」却收不到? 架构性原因:任务滞留 bull:emails:wait 队列,无邮件 worker 消费(8.1 节)。redis-cli llen bull:emails:wait 可确认积压。

Q5:改了 config.json 不生效? 优先级问题:环境变量 > config.json(2.2 节实测)。检查 systemd 单元与 drop-in(/etc/systemd/system/habitica-web.service.d/override.conf)中的 Environment= 是否覆盖了同名键,改完 systemctl daemon-reload && systemctl restart habitica-web。

Q6:限流误伤正常使用? 按第七节参数表调大 RATE_LIMITER_V4_POINTS 或 RATE_LIMITER_BASE_POINTS;确认 RATE_LIMITER_ENABLED 当前值(=== 'true' 才生效)。

十二、参考链接