- w4-body.js: ONE-DIRECTIONAL sync (NocoDB status = SOT, never reverse);
drift matrix: missing user/role (provision w/ username lookup to avoid
duplicates), dangling refs (adopt/recreate), disabled<->status, canonical
base_path '/' + permission 16384, scopes vs calculateEffectiveGrants,
username drift detect+log_only; verify-after-repair on every change;
password never pushed on repair (方案 C); dry-run via ?dryRun=true
- gen_w4.js: daily 03:00 cron + webhook alist-w4-reconcile; API-import-safe
(UUID node ids, no pinData, webhookId)
- ReconcileLogs table mktsb7le6fqr2u1 (add_reconcilelogs_table.py, idempotent,
v2 API); FK column nc_jitu___Customers_id for bt-link writes (v2 ignores
link column itself)
- test_w4_reconcile.js: 34 assertions / 12 mock worlds, ALL PASSED
- live-verified against production: dry-run (3 drifts + 1 error found),
real repair (3 repaired, verify all passed, 4 log rows), idempotent rerun
- NocoDB 2026.08.x pitfalls documented (v1 col endpoints gone, row delete
via body [{Id}]); n8n API import/activation pitfalls (activeVersionId)
- docs: W4-DESIGN.md new; DATABASE_SCHEMA 8 tables; N8N_WORKFLOWS, SETUP,
PROJECT_STATE updated
73 lines
2.8 KiB
Markdown
73 lines
2.8 KiB
Markdown
# n8n Setup — Environment Variables
|
||
|
||
W1/W2 通过**一个 in-flow 的 "Load Env" Code 节点**(webhook 之后第 2 步)读取全部 env,
|
||
下游所有节点引用 `$('Load Env').item.json.VAR`。secrets 不硬编码进 workflow。
|
||
|
||
## 读取方式(已实测确认)
|
||
|
||
- **Code 节点内**:`$env.VAR` 可用(例 `$env.NOCODB_URL`)。✔
|
||
- **HTTP 节点表达式字段**(url / header / body expression):`$env` 和 `process.env` **都不可用**。✘
|
||
- 所以用**一个 Code 节点 "Load Env"** 在 `$env` 里调取全部 env 输出到 `$json`,
|
||
下游 HTTP 节点用 `{{ $('Load Env').item.json.VAR }}` 取值。
|
||
|
||
> 关键点:
|
||
> - Load Env **必须接在 webhook flow 里(第 2 步)**,不在 flow 里的 Code 节点不会执行。
|
||
> - Load Env **必须透传传入的字段**(`customerId` / `customerProductId`),否则下游丢数据。
|
||
|
||
### Load Env 模板(实测可用)
|
||
|
||
```js
|
||
// Step 2: read ALL env via $env (works in Code node), and pass through
|
||
// the webhook-derived fields so downstream keeps customerId.
|
||
const prev = $input.item.json;
|
||
return { json: {
|
||
customerId: prev.customerId,
|
||
eventType: prev.eventType,
|
||
NOCODB_URL: $env.NOCODB_URL,
|
||
NOCODB_TOKEN: $env.NOCODB_TOKEN,
|
||
T_CUSTOMERS: $env.NOCODB_TABLE_CUSTOMERS,
|
||
ALIST_URL: $env.ALIST_URL,
|
||
ALIST_ADMIN_USERNAME: $env.ALIST_ADMIN_USERNAME,
|
||
ALIST_ADMIN_PASSWORD: $env.ALIST_ADMIN_PASSWORD,
|
||
ALIST_ADMIN_OTP: $env.ALIST_ADMIN_OTP || '',
|
||
} };
|
||
```
|
||
|
||
## 需要配置的 env(n8n 容器环境变量)
|
||
|
||
```
|
||
NOCODB_URL=https://nocodb.hoelee.com
|
||
NOCODB_TOKEN=<workspace token>
|
||
NOCODB_TABLE_CUSTOMERS=maqb9zgvza2te3k
|
||
NOCODB_TABLE_PRODUCTS=molyb3pxv81l75g
|
||
NOCODB_TABLE_RESOURCES=m6cm7tkxzd8m18c
|
||
NOCODB_TABLE_CUSTOMERPRODUCTS=mvyqbv8ao7u6bct
|
||
NOCODB_TABLE_ACCESSGRANTS=mxdx3yhfcyej3ao
|
||
NOCODB_TABLE_RECONCILELOGS=mktsb7le6fqr2u1
|
||
NOCODB_FK_RECONCILELOG_CUSTOMER=nc_jitu___Customers_id
|
||
NOCODB_LINK_PROD_RESOURCES=c4ko4x8kwi16swg
|
||
ALIST_URL=http://alist-serve-resource:5244
|
||
ALIST_ADMIN_USERNAME=hoelee
|
||
ALIST_ADMIN_PASSWORD=<admin 密码>
|
||
```
|
||
|
||
改完 env 必须**重建 n8n 容器**(不是 restart)。
|
||
|
||
## 导入步骤
|
||
|
||
1. n8n → Import from File → `W1-customer-provision.json` / `W2-customerproduct-sync.json`。
|
||
2. Webhook 节点 → Copy Webhook URL。
|
||
3. NocoDB → 对应表 → 建 Webhook(After Insert + After Update)粘 URL:
|
||
- Customers 表 → W1;CustomerProducts 表 → W2。
|
||
|
||
## AList user 创建顺序(重要)
|
||
|
||
`user/create` 传空 `role` 会失败(`admin or guest user can not be created`,因 AList 把空 role
|
||
自动填成 guest 后拒绝)。**必须先 `role/create` 建客户专属 role,再 `user/create` 传 `role:[新roleId]`。**
|
||
详见 `docs/API_INTEGRATION.md` §9.7。
|
||
|
||
## 验证状态
|
||
|
||
- ✅ W1 已实测跑通(create user 自动化成功)。
|
||
- W2 待测(同 Load Env 模式已套用)。
|