Dittofeed v0.24.0-alpha.21 配置参数详解
Dittofeed 的配置分为三层:环境变量(进程级)、工作区数据库记录(SMTP/SMS 提供商、密钥、写键)、以及构建期常量。本文以 Ubuntu 22.04 源码部署实例(PostgreSQL 15、ClickHouse 24.12.6.70、Temporal 1.22.4、Node.js v20.20.2)为基准,逐项给出默认值、是否必填、生效方式与
Dittofeed v0.24.0-alpha.21 配置参数详解
Dittofeed 的配置分为三层:环境变量(进程级)、工作区数据库记录(SMTP/SMS 提供商、密钥、写键)、以及构建期常量。本文以 Ubuntu 22.04 源码部署实例(PostgreSQL 15、ClickHouse 24.12.6.70、Temporal 1.22.4、Node.js v20.20.2)为基准,逐项给出默认值、是否必填、生效方式与验证命令,所有「实测」条目均来自该实例的真实回显。
安装步骤见 《Ubuntu 22.04 安装 Dittofeed v0.24.0-alpha.21 指南》,产品定位与版本选型见 《Dittofeed v0.24.0-alpha.21 介绍与选型指南》。本文只讨论参数本身。
一、配置加载机制
1.1 加载顺序
packages/backend-lib/src/config/loader.ts 的 loadConfig() 先用 dotenv.config({ path: <baseDir>/.env }) 读取 .env,再从 process.env 取值。dotenv 默认不覆盖已存在的环境变量,因此优先级为:
命令行/服务单元 Environment= 赋的值 > .env 文件 > 代码内置默认值
baseDir 由 findBaseDir()(packages/backend-lib/src/dir.ts)决定:从进程工作目录向上找名为 packages 的目录,取其父目录。所以三个进程的 WorkingDirectory 必须是 packages/api、packages/worker、packages/dashboard,.env 才会被读到;若从 /srv/dittofeed 直接启动,则 baseDir=/srv/dittofeed,同样成立。
1.2 变量命名规则
配置对象字段经 change-case 的 constantCase() 转成环境变量名:
databaseHost -> DATABASE_HOST
clickhouseDatabase-> CLICKHOUSE_DATABASE
temporalAddress -> TEMPORAL_ADDRESS
apiBodyLimit -> API_BODY_LIMIT
bootstrapEvents -> BOOTSTRAP_EVENTS
因此不存在 PG_HOST 这类别名,写错名字会被当作未设置而回退默认值,且不报错。
1.3 默认值来源
DEFAULT_BACKEND_CONFIG(packages/backend-lib/src/config.ts)内置了四个兜底值:
export const DEFAULT_BACKEND_CONFIG = {
databasePassword: "password",
clickhousePassword: "password",
password: "password",
secretKey: "o/UopmFUqiYriIzOCXnzZXGbcYTWuE3iVx2822jC0fY=",
} as const;
这四项在生产部署中必须全部显式覆盖,否则等于使用公开口令与公开签名密钥。
1.4 查看最终生效配置
设置 LOG_CONFIG=true 后,api 与 worker 启动时会打印一份完整的解析后配置(msg":"Initialized with config")。这是排查「变量到底有没有生效」最快的手段:
curl -s http://127.0.0.1:3101/api/ -o /dev/null
grep -o '"databaseName":"[^"]*"' /var/log/dittofeed-api.log | tail -1
实测:LOG_CONFIG=true 时日志原文包含明文口令字段,例如 "databasePassword":"Dit2feedPg2026"、"clickhousePassword":"Dit2feedCh2026";LOG_CONFIG 未设置(默认 false)时,同一次启动只有 {"level":30,"msg":"Server listening at http://0.0.0.0:3101"},没有配置 JSON。生产环境不建议长期开启。
二、PostgreSQL 关系库配置
2.1 参数表
| 变量 | 默认值 | 必填 | 说明 |
|---|---|---|---|
DATABASE_URL | 无 | 与分组二选一 | 单条连接串,优先级最高;给了它就不再读下面的分组变量(DATABASE_NAME_SUFFIX 除外) |
DATABASE_USER | postgres | 生产必填 | 生产环境缺少它或口令会抛错 |
DATABASE_PASSWORD | password | 生产必填 | 同上 |
DATABASE_HOST | localhost | 是 | 数据库地址 |
DATABASE_PORT | 5432 | 否 | 端口 |
DATABASE_NAME | dittofeed(测试环境为 dittofeed_test) | 否 | 业务库名 |
DATABASE_NAME_SUFFIX | 无 | 否 | 追加到默认库名后,- 会被替换为 _ |
DATABASE_PARAMS | connect_timeout=60 | 否 | 形如 sslmode=require&connect_timeout=30,与默认值合并 |
DATABASE_URL 拼装逻辑见 parseDatabaseUrl():URL 会强制补上 connect_timeout=60,URL 中的 query 参数优先覆盖默认值。因此 postgresql://postgres:xxx@10.89.59.3:5432/dittofeed_src 与分组写法等价。
生产环境校验在前:
In production must provide database credentials
NODE_ENV=production 且 DATABASE_USER/DATABASE_PASSWORD 任一缺失即启动失败,不会悄悄用 password。
2.2 验证方法
变量是否生效,看库名与表是否落在预期库:
psql "postgresql://postgres:Dit2feedPg2026@10.89.59.3:5432/dittofeed_src" -c '\dt'
实测项 1(DATABASE_NAME):DATABASE_NAME=dittofeed_src 时,information_schema.tables 在当前 schema 下共 31 张表,核心表为引号大写的 "Workspace"、"Secret"、"WriteKey"、"UserProperty";SELECT name, type FROM "Workspace" 返回:
Default|Root
Tenant2|Parent
2.3 与其他进程的关系
api、worker、dashboard 读同一份配置;admin-cli 也读同一份 .env,因此迁移、建工作区、导出配置(admin config-print)与运行时看到的是同一套连接参数。
三、ClickHouse 配置
3.1 参数表
| 变量 | 默认值 | 必填 | 说明 |
|---|---|---|---|
CLICKHOUSE_HOST | http://localhost:8123 | 是 | 可写 10.89.59.2(自动补 http:// 与 8123)或带协议完整地址 |
CLICKHOUSE_USER | dittofeed | 是 | 生产必须显式设置 |
CLICKHOUSE_PASSWORD | password | 是 | 生产必须显式设置 |
CLICKHOUSE_DATABASE | dittofeed(测试环境 dittofeed_test) | 否 | 事件库名 |
CLICKHOUSE_PROTOCOL | http | 否 | 仅在 CLICKHOUSE_HOST 未写协议时参与补全 |
CLICKHOUSE_MAX_EXECUTION_TIME | 300 | 否 | 单查询最大执行秒数 |
CLICKHOUSE_MAX_MEMORY_USAGE | 10000000000 | 否 | 单查询内存上限(字节) |
READ_QUERY_PAGE_SIZE | 1000 | 否 | 用户列表分页大小 |
READ_QUERY_CONCURRENCY | 10 | 否 | 并发查询数 |
CLICKHOUSE_HOST 补全规则来自 defaultChUrl():无协议补 http://,无端口补 8123,末尾统一带 /。实测日志中 "clickhouseHost":"http://10.89.59.2:8123/" 即补全结果。
3.2 验证方法
clickhouse client --host 10.89.59.2 --port 9000 \
--user dittofeed --password Dit2feedCh2026 \
--query "SELECT name FROM system.databases ORDER BY name"
实测项 2(CLICKHOUSE_DATABASE):同一实例上同时存在 dittofeed(默认库,由部署实例里另一套运行环境创建)和 dittofeed_src(本实例通过 CLICKHOUSE_DATABASE 指定)。查询结果为:
INFORMATION_SCHEMA
default
dittofeed
dittofeed_src
information_schema
system
SHOW TABLES FROM dittofeed_src 返回 25 张表,含 user_events_v2、computed_property_state_v3、user_property_idx_str、user_property_idx_str_mv 等;事件确实写入了配置指定的库:
SELECT event, user_id, count() AS c
FROM dittofeed_src.user_events_v2
GROUP BY event, user_id
ORDER BY c DESC, event
u-c1-001 1
async_probe u-async-001 1
doc_test_track u-c1-001 1
sync_probe u-sync-001 1
首行为 identify 事件的空 event 字段,说明 identify 走同一条写入链路。
注意:
user_events_v2的列名是user_id,不是userId;用userId查询会报Unknown expression identifier 'userId' ... Maybe you meant: ['user_id']。
四、Temporal 配置
| 变量 | 默认值 | 必填 | 说明 |
|---|---|---|---|
TEMPORAL_ADDRESS | localhost:7233 | worker 必填 | 只写主机名时自动补 :7233 |
TEMPORAL_NAMESPACE | default | 否 | 命名空间 |
TEMPORAL_CONNECTION_TIMEOUT | 无(使用 SDK 默认) | 否 | 连接超时毫秒数 |
GLOBAL_CRON_TASK_QUEUE | default | 否 | 全局定时任务队列 |
COMPUTED_PROPERTIES_TASK_QUEUE | default | 否 | 计算属性任务队列 |
BOOTSTRAP_WORKER | 生产环境 true | 否 | 启动时注册默认 workflow/cron |
验证方法
worker 启动日志出现 "msg":"Worker created" 与 "state":"RUNNING" 即连上;连接错误会直接抛出。实测项 3(TEMPORAL_ADDRESS 指向错误端口 127.0.0.1:7299):
TransportError: tonic::transport::Error(Transport, ConnectError(ConnectError("tcp connect error", 127.0.0.1:7299, Os { code: 111, kind: ConnectionRefused, message: "Connection refused" })))
at NativeConnection.connect (/srv/dittofeed/node_modules/@temporalio/worker/lib/connection.js:199:23)
worker 进程随即退出,不会降级为「无 Temporal 继续跑」。api 进程则不同:Temporal 不可用时 api 仍能启动并响应 HTTP,只是触发 journey、计算属性等异步动作失败,因此生产上必须把 worker 与 Temporal 的存活一起监控。
五、HTTP 服务配置
| 变量 | 默认值 | 必填 | 说明 |
|---|---|---|---|
API_PORT | 3001 | 否 | api 监听端口 |
API_HOST | 生产 0.0.0.0,开发 localhost | 否 | 监听地址 |
API_BODY_LIMIT | 5242880(5 MB) | 否 | 请求体上限(字节) |
API_SERVICE_NAME | dittofeed-api | 否 | 服务标识,随日志输出 |
APP_VERSION | 无 | 否 | 健康检查返回的版本字段 |
DASHBOARD_URL | 生产 https://app.dittofeed.com,开发 http://localhost:3000 | 是 | 控制台基地址,同时用于 ALLOWED_ORIGINS 默认值与退出跳转 |
DASHBOARD_API_BASE | 由 DASHBOARD_URL 推导 | 否 | 控制台访问 api 的地址 |
验证方法
curl -s -i http://127.0.0.1:3101/api/
实测项 4(API_PORT/API_HOST):API_PORT=3101 时日志为 {"msg":"Server listening at http://0.0.0.0:3101"},GET /api/ 返回 200;此时 3001 端口无监听。
实测项 5(APP_VERSION):未设置时响应体是 {}(只有 version 字段且值为 undefined,序列化后消失);设置 APP_VERSION=0.24.0-alpha.21 后重启,同一请求返回:
{"version":"0.24.0-alpha.21"}
实测项 6(API_BODY_LIMIT):默认 5 MB 下发送 6000064 字节的 track 请求:
{"statusCode":413,"code":"FST_ERR_CTP_BODY_TOO_LARGE","error":"Payload Too Large","message":"Request body is too large"}
关于 ALLOWED_ORIGINS
ALLOWED_ORIGINS 虽在配置 schema 中(默认取 DASHBOARD_URL,逗号分隔),但本版本的 api 在 packages/api/src/buildApp/cors.ts 里写死了:
await fastify.register(fastifyCors, {
origin: "*",
methods: "*",
allowedHeaders: "*",
exposedHeaders: ["Content-Disposition"],
});
实测项 7:无论请求 Origin 是 http://192.168.146.131:8138 还是 http://evil.example.com,响应头都是 access-control-allow-origin: *,OPTIONS 预检返回 204 且 access-control-allow-methods: *。即该变量在本版本中不参与 api 的 CORS 判定,不能依赖它做来源白名单;若需限制来源,必须在反向代理层实现。
六、安全与鉴权配置
| 变量 | 默认值 | 必填 | 说明 |
|---|---|---|---|
SECRET_KEY | 固定常量 o/UopmFUqiYriIzOCXnzZXGbcYTWuE3iVx2822jC0fY= | 是 | 会话 Cookie 签名密钥,32 字节随机串的 base64(44 字符) |
PASSWORD | password | 单租户必填 | 单租户模式共享登录口令(无用户名) |
AUTH_MODE | anonymous | 是 | anonymous / single-tenant / multi-tenant |
SESSION_COOKIE_SECURE | false | 生产建议 true | Cookie 是否带 Secure |
AUTH_PROVIDER | 无 | 多租户必填 | 多租户控制台登录方式,缺失时会话鉴权 500 |
SIGN_OUT_URL / SIGN_OUT_REDIRECT_URL | single-tenant 时为 /api/public/single-tenant/signout;跳转默认 DASHBOARD_URL | 否 | 退出路径 |
ALLOWED_ORIGINS | DASHBOARD_URL | 否 | 见 5.3,本版本未生效 |
生成密钥:
openssl rand -base64 32
6.1 SECRET_KEY
会话由签名 Cookie 承载,密钥变更会让所有旧 Cookie 立即失效。
实测项 8:用旧实例签发的 Cookie 访问用新 SECRET_KEY 启动的实例(新值 Slq3ThXlbdYhKqdE3NMB6jDCEquBvgQ1LgyfumcaiDE=):
POST /api/users code=401
同一实例用新密钥重新登录后 POST /api/users 返回 200。多副本部署必须共享同一 SECRET_KEY,否则用户会在实例间随机掉线。
6.2 AUTH_MODE
single-tenant:POST /api/public/single-tenant/login 注册,口令正确返回 200 并下发会话 Cookie:
curl -s -i -c /srv/ck.txt -X POST http://127.0.0.1:3101/api/public/single-tenant/login \
-H 'content-type: application/json' -d '{"password":"q1w2e3r4"}'
HTTP/1.1 200 OK
set-cookie: session=...; Max-Age=1209600; Path=/; HttpOnly
口令错误返回 {"error":"Invalid password."} 与 401。
anonymous:该登录路由不注册。实测项 9(AUTH_MODE=anonymous 单独启动一个实例):
POST /api/public/single-tenant/login -> 404 {"message":"Route POST:/api/public/single-tenant/login not found",...}
POST /api/users(不带 Cookie) -> 200
也就是说 anonymous 模式下控制台 API 组完全不校验会话,不能对外暴露,只能用于本地调试或前置了可信网关的场景。
multi-tenant:管理接口必须带 workspaceId(body、query 或请求头 x-workspace-id),缺失时请求体校验直接返回 400 body must have required property 'workspaceId';单个工作区内的管理密钥只对所属工作区及子工作区有效。实测项 10:多租户模式下走会话鉴权的 /api/users 返回:
{"statusCode":500,"error":"Internal Server Error","message":"Misconfigured auth provider, missing."}
即 multi-tenant 必须同时配置 AUTH_PROVIDER 才能使用控制台。
6.3 PASSWORD
单租户模式只校验 PASSWORD,没有用户名,示例口令统一为 q1w2e3r4。
实测项 11(PASSWORD 被显式置空):
{"error":"Application is misconfigured, contact support."} code=500
代码路径是 if (!password?.length) { 500 },所以空口令不会退化成默认口令。但完全不设置该变量时,rawConfig.password ?? DEFAULT_BACKEND_CONFIG.password 会落到字符串 password,即控制台口令就是 password,这是必须避免的部署姿势。
6.4 SESSION_COOKIE_SECURE
默认 false,登录响应只有 HttpOnly;置为 true 后:
set-cookie: session=...; Max-Age=1209600; Path=/; HttpOnly; Secure
实测项 12。上线 HTTPS 后应同时开启该开关,否则 Cookie 会在明文 HTTP 上传输。
七、日志配置
| 变量 | 默认值 | 必填 | 说明 |
|---|---|---|---|
LOG_LEVEL | 生产 info、开发 debug、测试 error | 否 | pino 日志级别 |
LOG_CONFIG | false | 否 | 启动时打印完整配置(含口令) |
PRETTY_LOGS | 生产 false、开发/测试 true | 否 | 人类可读格式,生产建议关闭 |
GOOGLE_OPS | false | 否 | 是否启用 GCP 日志字段 |
START_OTEL / OTEL_COLLECTOR | false / http://localhost:4317 | 否 | OpenTelemetry 导出 |
实测项 13(LOG_LEVEL=error):以该级别启动另一个实例,发起一次 GET /api/ 后日志文件仅 2031 字节,且 grep -c 'incoming request' 为 0;默认 info 级别下同一请求会产生成对的 "msg":"incoming request" 与 "msg":"request completed"。因此排障时临时降到 debug,压测时升到 warn/error 可显著降低日志量。
八、初始化与写入开关
| 变量 | 默认值 | 必填 | 说明 |
|---|---|---|---|
BOOTSTRAP | false | 否 | 仅被 lite 入口和 admin-cli 消费 |
BOOTSTRAP_EVENTS | false | 否 | 初始化时是否写入默认事件 |
BOOTSTRAP_WORKER | 生产 true | 否 | worker 启动时注册默认 workflow 与 cron |
BOOTSTRAP_SAFE | false | 否 | 安全模式初始化(不覆盖已有数据) |
WRITE_MODE | ch-async(测试环境 ch-sync) | 否 | 事件写入策略 |
DEFAULT_USER_EVENTS_TABLE_VERSION | 空 | 否 | 已废弃字段,保留兼容 |
8.1 BOOTSTRAP 的真实作用范围
实测项 14:以 BOOTSTRAP=true 启动 api 后请求用户列表:
relation "workspace" does not exist
api 与 worker 都只把该值原样打进启动日志("bootstrap":true),并不执行迁移。真正消费它的是 packages/lite/scripts/startLite.ts 与 packages/admin-cli:源码安装必须先执行
node /srv/dittofeed/packages/admin-cli/dist/scripts/cli.js bootstrap -n Default
日志出现 "msg":"Running migrations" 与 "msg":"Upserting workspace." 后,表结构才建立。仓库自带的 scripts/bootstrap.sh 调用 node ./packages/api/dist/scripts/bootstrap.js,该产物在本版本不存在,属陈旧脚本,不要用。
8.2 WRITE_MODE
ch-async 为生产默认:事件先进入应用侧缓冲,再由后台批量写入 ClickHouse;ch-sync 在请求内同步写入。
实测项 15:分别在 ch-sync 与 ch-async 实例上各发一条 track 后立即查询:
ch-sync : track=204,79 ms 后查询到 1 条(sync_probe)
ch-async: track=204,141 ms 后查询到 1 条(async_probe)
小流量下两种模式的「写入后可见」延迟都在百毫秒级,差异主要出现在批量高峰;ch-sync 会把 ClickHouse 抖动直接传导为接口延迟,生产不建议切换,除非下游强依赖写后立即可查。
九、邮件、短信与工作区级配置(不在 .env 中)
SMTP、SendGrid、Postmark、Amazon SES、MailChimp、Resend、Twilio、SignalWire 等提供商参数不是环境变量,而是按工作区存储在 PostgreSQL 的 "Secret" 表(name 为 smtp、sendgrid 等,configValue 为 JSON)。
配置入口(会话鉴权):
curl -s -X PUT http://127.0.0.1:3101/api/settings/email-providers \
-b /srv/ck.txt -H 'content-type: application/json' \
-d '{"workspaceId":"7bd6a519-18d7-4d26-8f4b-66b4788afa63","setDefault":true,
"config":{"type":"Smtp","host":"smtp.example.com","port":"587",
"username":"mailer","password":"SmtpPass2026"}}'
实测项 16:返回 201;库中记录为
Default | smtp | {"host": "smtp.example.com", "port": "587", "type": "Smtp", "password": "SmtpPass2026", "username": "mailer"}
用 GET /api/secrets?workspaceId=...&names=smtp 查询却返回 []:因为列表接口只返回 value 非空的行,SMTP 参数在 configValue 中,不会通过 HTTP 回显,属于有意的防泄漏设计。校验请直接查库或看控制台设置页。
GET /api/secrets?workspaceId=... 不带 names 时,本实例只返回 default-write-key 与 subscription-key 两个名称,可作为「密钥是否初始化」的快速探针。
十、工作区与多租户
工作区没有 HTTP CRUD 接口(GET /api/admin/workspaces 实测返回 404 Route GET:/api/admin/workspaces not found),只能通过命令行初始化:
node /srv/dittofeed/packages/admin-cli/dist/scripts/cli.js bootstrap \
-n Tenant2 -t Parent -d tenant2.example.com
参数含义:-n 工作区名、-t 类型(Root 或 Parent,默认 Root)、-d 允许登录的邮箱域名。同名工作区再次执行是幂等更新,不会重复创建。
实测项 17:执行后 "Workspace" 表内容为
7bd6a519-18d7-4d26-8f4b-66b4788afa63 | Default | Root | |
20dbc319-8f49-4147-989b-ddbdfb6ed290 | Tenant2 | Parent | tenant2.example.com |
初始化子工作区会同时写入一套默认密钥(default-write-key、各邮件/短信提供商占位、subscription-key),因此多工作区部署要先评估密钥表体积。
单租户模式下若请求未带 workspaceId,服务端回落到 SELECT ... FROM "Workspace" LIMIT 1 的第一条(见 packages/api/src/workspace.ts)。实测表中存在两个工作区时,回落结果取决于数据库返回顺序,因此单租户部署应只保留一个工作区,否则管理接口可能作用到错误的工作区。
十一、配置验证清单
| # | 配置项 | 验证命令/方法 | 期望结果(本实例实测) |
|---|---|---|---|
| 1 | DATABASE_NAME | psql -c '\dt' | dittofeed_src 下 31 张表,"Workspace" 有数据 |
| 2 | CLICKHOUSE_DATABASE | SHOW TABLES FROM <库> | 25 张表,user_events_v2 存在且事件可查 |
| 3 | TEMPORAL_ADDRESS | 启动 worker 看日志 | 正常为 Worker created + RUNNING;错误地址抛 ConnectError |
| 4 | API_PORT / API_HOST | curl -i http://127.0.0.1:3101/api/ | 200,日志 Server listening at http://0.0.0.0:3101 |
| 5 | APP_VERSION | curl -s .../api/ | 设置后 {"version":"0.24.0-alpha.21"},未设置为 {} |
| 6 | API_BODY_LIMIT | 发 6 MB JSON | 413 FST_ERR_CTP_BODY_TOO_LARGE |
| 7 | ALLOWED_ORIGINS | 带任意 Origin 请求 | 恒为 access-control-allow-origin: *(该版本不生效) |
| 8 | SECRET_KEY | 用旧 Cookie 访问新密钥实例 | 401 |
| 9 | AUTH_MODE=anonymous | 访问 /api/public/single-tenant/login、POST /api/users | 404 与 200(无鉴权) |
| 10 | AUTH_MODE=multi-tenant | 会话接口 /api/users | 500 Misconfigured auth provider, missing. |
| 11 | PASSWORD | 空值启动后登录 | 500 Application is misconfigured, contact support. |
| 12 | SESSION_COOKIE_SECURE | 登录看 set-cookie | HttpOnly; Secure |
| 13 | LOG_LEVEL | grep -c 'incoming request' | error 级别为 0,info 级别成对出现 |
| 14 | BOOTSTRAP | BOOTSTRAP=true 启动 api 后查询 | relation "workspace" does not exist(不迁移) |
| 15 | WRITE_MODE | track 后立即查事件 | ch-sync 79 ms、ch-async 141 ms 均可查到 |
| 16 | 邮件提供商 | PUT /api/settings/email-providers | 201,"Secret".smtp 写入 configValue |
| 17 | 工作区 | bootstrap -n Tenant2 -t Parent | "Workspace" 新增一行,类型 Parent |
十二、实测发现的问题
| 问题 | 现象(证据原文) | 建议 |
|---|---|---|
| 默认口令与默认签名密钥 | DEFAULT_BACKEND_CONFIG.password = "password"、secretKey 为固定常量 | 部署前必须显式设置 PASSWORD、SECRET_KEY、两个数据库口令 |
| 配置日志泄漏口令 | LOG_CONFIG=true 时输出 "databasePassword":"Dit2feedPg2026"、"clickhousePassword":"Dit2feedCh2026" | 生产关闭 LOG_CONFIG,或限制日志文件权限 |
ALLOWED_ORIGINS 形同虚设 | 任意 Origin 均返回 access-control-allow-origin: * | 白名单放到反向代理层 |
anonymous 模式裸奔 | POST /api/users 不带 Cookie 返回 200 | 只允许单租户或多租户模式对外 |
多租户缺 AUTH_PROVIDER | {"message":"Misconfigured auth provider, missing."} | 多租户部署必须配齐认证方式 |
BOOTSTRAP 不负责迁移 | relation "workspace" does not exist | 用 admin-cli bootstrap 初始化 |
| 仓库启动脚本陈旧 | scripts/bootstrap.sh 调用不存在的 packages/api/dist/scripts/bootstrap.js | 改用 admin-cli 入口 |
| 事件列名与 API 字段不一致 | Unknown expression identifier 'userId' ... Maybe you meant: ['user_id'] | 自定义查询用 user_id |
| 单租户回落依赖顺序 | GET ... FROM "Workspace" LIMIT 1 | 单租户部署只保留一个工作区 |
| 健康检查不含版本 | 未设 APP_VERSION 时 GET /api/ 返回 {} | 设置 APP_VERSION 供探针与排障使用 |
十三、参考
- 《Ubuntu 22.04 安装 Dittofeed v0.24.0-alpha.21 指南》
- 《Dittofeed v0.24.0-alpha.21 介绍与选型指南》
- 配置文件:
packages/backend-lib/src/config.ts、packages/backend-lib/src/config/loader.ts、packages/api/src/config.ts - 鉴权实现:
packages/api/src/buildApp/adminAuth.ts、packages/api/src/controllers/singleTenantController.ts