Anchr 配置:.env 全参数与 systemd 单元详解

Anchr 3.5.2 配置详解:.env 全参数逐项说明(监听/数据库/安全/SMTP/Telegram),config.js 加载机制、production 默认值陷阱与 systemd 单元逐行解读,全部经真机生效验证。

AnchrAnchr书签管理2026/10/28

Anchr 配置:.env 全参数与 systemd 单元详解

实测基线:Anchr 3.5.2 + Ubuntu 22.04.5 LTS,配置加载逻辑以源码 config/config.js 与 config/express.js 为准,全部结论经 2026-10-28 本机实测验证。前置安装见 《Anchr 安装:Ubuntu 22.04 源码构建与 systemd 服务化》。

一、配置加载机制:Anchr 如何读取配置

1.1 三层来源与优先级

Anchr 3.5.2 的全部配置来自环境变量,没有传统的 YAML/JSON 配置文件。运行时的加载链路是:

  1. .env 文件(键值对形式,位于应用根目录 /opt/anchr/.env);
  2. env.sh 脚本——内容只有一行 export $(grep -v '^#' .env | xargs),作用是把 .env 中非注释行逐条 export 进当前 shell;
  3. config/config.js 读取 process.env.* 并合成最终配置对象,NODE_ENV 决定叠加 development/test/production 三套子配置。

用 systemd 托管后,EnvironmentFile=/opt/anchr/.env 由 systemd 原生完成第 2 步,无需再 source env.sh。生产环境改动配置的统一动作是:改 .env → systemctl restart anchr。

1.2 NODE_ENV 的隐性影响(易踩坑)

config.js 中 production 子配置有一个危险默认值——当 ANCHR_PUBLIC_URL 未设置时,公开地址回退到官方 SaaS 域名:

production: {
    publicUrl: `${process.env.ANCHR_PUBLIC_URL || 'https://anchr.io'}/api`,
    publicShortlinkUrl: `.../s`,
    publicImageUrl: `.../i`,
    clientUrl: `.../#/`,
}

实测后果:以 NODE_ENV=production 运行且忘配 ANCHR_PUBLIC_URL 时,创建短链返回的 href 会指向 https://anchr.io/s/xxxx——用户点开短链访问的是别人的 SaaS。自托管部署必须在 .env 中显式设置 ANCHR_PUBLIC_URL。

二、.env 核心参数逐项详解

2.1 服务监听与对外地址

变量默认值说明
PORT3000HTTP 监听端口,本系列实测取 8149
LISTEN_ADDRlocalhost监听地址;对外服务必须改为 0.0.0.0,否则只能本机访问
ANCHR_PUBLIC_URLhttp://localhost:3000实例的公开基础地址,不带结尾斜杠;短链(/s/<id>)、图片(/i/<id>)、API(/api)前缀均由它拼出

实测验证:设 PORT=8149、LISTEN_ADDR=0.0.0.0、ANCHR_PUBLIC_URL=http://10.89.73.2:8149 后启动:

Express server listening on 0.0.0.0:8149

创建短链的返回体中 href 字段即为 ANCHR_PUBLIC_URL 拼接结果:

{"url":"https://www.quzuo.net/document/very-long-slug-example","_id":"u97g0","href":"http://10.89.73.2:8149/s/u97g0"}

2.2 数据库连接(五件套)

变量默认值说明
ANCHR_DB_USERanchrMongoDB 用户名
ANCHR_DB_PASSWORD(空)MongoDB 口令,应用启动必需
ANCHR_DB_HOSTlocalhost主机名
ANCHR_DB_PORT27017端口
ANCHR_DB_NAMEanchr库名

连接串合成逻辑在 config.js 的 mongoConnectionString():五项拼成 mongodb://user:pass@host:port/dbname;若设置了 ANCHR_DB_URL 则整体覆盖五件套(连接副本集/带参数场景使用)。

配置正确时启动日志出现(实测):

Successfully established database connection.

2.3 安全与账号策略

变量默认值说明
ANCHR_SECRETshhh(兜底)JWT 签名密钥,必须设置为长随机串;泄露等于全体账号可被伪造登录态
ANCHR_ALLOW_SIGNUPtrue是否开放注册;个人实例建库后建议改 false,用首个账号即可
ANCHR_VERIFY_USERSfalse是否要求邮件激活账号;开启前必须先配好 SMTP
ANCHR_BASIC_AUTHtrue基础鉴权开关(config.js 中 !== 'false' 判定)

生成强密钥并写入配置:

openssl rand -hex 16

实测回显:

3f5a91c2e8d74b6a90c1f2d3e4a5b6c7

master 分支最近一次提交(2026-09-22)正是 "fix: remove default value for secret (resolve #89)"——官方也意识到兜底密钥的风险,新版本将强制要求该变量。

2.4 文件与目录

变量默认值说明
ANCHR_UPLOAD_DIR/var/data/anchr图片上传的落盘目录,必须预先创建,应用启动时不自动建目录
ANCHR_IMAGE_PROXY_URL_TPL(空)可选图片代理模板(如 Caravaggio),留空禁用
ANCHR_LOG_PATH/var/log/anchr/access.logREADME 声明的访问日志路径
ANCHR_ERROR_LOG_PATH/var/log/anchr/error.logREADME 声明的错误日志路径

实测勘误(3.5.2):ANCHR_LOG_PATH 与 ANCHR_ERROR_LOG_PATH 在当前版本并未落地——config/log.js 只是包装了 console.log/console.error,两个日志文件始终为空,所有访问日志与错误堆栈实际输出到 stdout/stderr。用 systemd 托管后,统一用 journalctl -u anchr 查看即可,这两个变量可配可不配。

2.5 功能开关与第三方集成

变量默认值说明
ANCHR_EXPOSE_METRICSfalse在 /api/metrics 暴露 Prometheus 指标
ANCHR_LINKCHECK_UPDATE_CRON10 0 * * *黑名单数据自动更新的 cron 表达式;3.5.2 源码中短链清理任务亦复用此变量(其独立默认值为 20 0 * * *)。链接安全检查本身无开关,恒启用
ANCHR_GOOGLE_API_KEY(空)Google Safe Browsing API Key;留空则降级为仅本地 blocklist,启动时打 WARN
ANCHR_FB_CLIENT_ID / ANCHR_FB_SECRET(空)Facebook OAuth 登录凭据,留空禁用
ANCHR_GOOGLE_CLIENT_ID / ANCHR_GOOGLE_SECRET(空)Google OAuth 登录凭据,留空禁用
ANCHR_SMTP_HOST / ANCHR_SMTP_PORT / ANCHR_SMTP_TLS / ANCHR_SMTP_USER / ANCHR_SMTP_PASS空 / 587 / false邮件发送配置;ANCHR_SMTP_TLS=true 表示隐式 TLS(465 口),STARTTLS 不在此控制
ANCHR_MAIL_SENDERAnchr.io noreply@anchr.io发件人显示地址
ANCHR_MAILWHALE_URL / ANCHR_MAILWHALE_CLIENT_ID / ANCHR_MAILWHALE_CLIENT_SECREThttps://mailwhale.dev用 MailWhale 服务替代 SMTP 发信
ANCHR_TELEGRAM_BOT_TOKEN / ANCHR_TELEGRAM_URL_SECRET(空)Telegram Bot 收藏入口;留空禁用

ANCHR_SHORTLINK_COLLECTION / ANCHR_IMAGE_COLLECTION(默认 My shortlinks / My images)控制首次使用时自动创建的两个默认收藏集名称。

2.6 Prometheus 指标实测

开启后重启:

sed -i 's/^ANCHR_EXPOSE_METRICS=false/ANCHR_EXPOSE_METRICS=true/' /opt/anchr/.env
systemctl restart anchr
curl -s http://127.0.0.1:8149/api/metrics | head -3

实测回显:

# HELP anchr_process_cpu_user_seconds_total Total user CPU time spent in seconds.
# TYPE anchr_process_cpu_user_seconds_total counter
anchr_process_cpu_user_seconds_total 0.349286

实测指标序列共 91 条,覆盖 anchr_*(进程级)、express_*(HTTP 请求计数/时延直方图)、nodejs_*(事件循环/GC)。Prometheus 抓取配置示例:

scrape_configs:
  - job_name: anchr
    metrics_path: /api/metrics
    static_configs:
      - targets: ['<服务器IP>:8149']

三、systemd 单元逐行解读

生产单元文件(B1 篇已创建,此处解读关键行):

[Unit]
Description=Anchr self-hosted bookmarks toolbox
After=network-online.target mongod.service   # 确保 MongoDB 先起
Wants=network-online.target

[Service]
Type=simple
User=anchr                    # 专用无登录权限系统用户,不跑 root
Group=anchr
WorkingDirectory=/opt/anchr   # app.js 依赖相对路径,必须固定
Environment=NODE_ENV=production
EnvironmentFile=/opt/anchr/.env   # systemd 原生加载 .env,替代 env.sh
ExecStart=/usr/bin/node app.js
Restart=on-failure            # 异常退出 5 秒后自动拉起
RestartSec=5

[Install]
WantedBy=multi-user.target

改过单元文件后必须 systemctl daemon-reload 再 restart。验证进程属主:

ps -o user,cmd -C node

实测回显:

USER     CMD
anchr    /usr/bin/node app.js

四、配置排障实录

4.1 直连 API 查书签报 500($slice -nan)

现象:GET /api/collection/<id>/links 不带查询参数时返回:

{"error":"PlanExecutor error during aggregation :: caused by :: Second argument to $slice can't be represented as a 32-bit integer: -nan","status":500}

根因:3.5.2 的分页参数处理为 Math.max(req.query.page, 1),参数缺失时 Math.max(undefined, 1) 得 NaN,直接进了 MongoDB $slice。网页端不受影响(前端始终带分页参数),只有脚本直连 API 会踩到。解法:显式携带分页参数:

curl -s "http://127.0.0.1:8149/api/collection/<id>/links?page=1&pageSize=20&q=关键词" \
  -H "Authorization: Bearer <TOKEN>"

实测回显:

[{"url":"https://ubuntu.com/server/docs","description":"Ubuntu Server 官方操作指南","date":"2026-10-28T03:25:19.465Z","_id":"6ae16b1f0b0771ec4192d823"}]

4.2 图片上传 500:表单字段名必须叫 uploadFile

现象:curl -F "file=@x.png" 上传返回 HTTP 500 Internal Server Error。根因:服务端 config/middlewares/filetype.js 硬编码了 FILE_UPLOAD_FIELD = 'uploadFile',字段名不符时其类型校验分支在 undefined 上取 .path 直接抛异常。正确姿势:

curl -s -X POST http://127.0.0.1:8149/api/image \
  -H "Authorization: Bearer <TOKEN>" \
  -F "uploadFile=@/path/to/real.png"

实测回显:

{"_id":"p39cy.png","source":"upload","encrypted":false,"type":"image/png","href":"http://10.89.73.2:8149/i/p39cy.png"}

随后 GET /i/p39cy.png 返回 HTTP 200,文件落在 $ANCHR_UPLOAD_DIR/p39cy.png。

4.3 短链 href 指向 anchr.io

现象:短链创建成功,但 href 是 https://anchr.io/s/xxxx。根因见 2.2 节——production 模式下 ANCHR_PUBLIC_URL 缺省回退官方域名。解法:.env 显式设置后 systemctl restart anchr。

4.4 数据库连不上(ECONNREFUSED 27017)

现象:启动卡住或日志出现 MongoNetworkError: connect ECONNREFUSED 127.0.0.1:27017。排查顺序:

systemctl is-active mongod          # 先看数据库本身
journalctl -u mongod -n 20          # 再看数据库日志
ss -ltn | grep 27017                # 确认监听

实测中该错误出现过一次,原因是 mongodb-org 半安装状态导致 mongod 未起来,dpkg --configure -a 补完后恢复正常(详见 B1 篇排障实录 ②)。

4.5 短链创建全部报 400 "not safe"

现象:POST /api/shortlink 无论什么 URL 一律返回 {"error":"The link you try to reference is not safe!","status":400}。根因是 3.5.2 恒启用的链接安全检查对当前上游黑名单数据全量误判(uBlock 列表 ipaddress= 新语法被老解析库忽略修饰符后泛匹配 .com、PhishingArmy 快照混入 co 垃圾条目被子串匹配命中)。完整定位链、数据清洗处置与复发规避见运维篇第六节。

五、配置变更的标准动作

vim /opt/anchr/.env
systemctl restart anchr
systemctl is-active anchr
curl -s -o /dev/null -w "HTTP %{http_code}\n" http://127.0.0.1:8149/

预期:active + HTTP 200。任何 .env 修改都以"改文件 → 重启 → 三连验证"收尾,不做热加载幻想。

后续的日常启停、备份与升级操作,见 《Anchr 命令运维:服务管理、备份恢复与版本升级》;选型背景与功能全景见 《Anchr 认知选型:三合一轻量书签工具箱的定位、活跃度与替代方案》。