95 lines
6.3 KiB
Markdown
95 lines
6.3 KiB
Markdown
# Project State (Handoff) — 2026-08-22
|
||
|
||
> 交接文档:压缩/新会话后,按此文档续做。细节见各 doc。
|
||
|
||
## 已完成 ✅
|
||
|
||
### 1. AList stock 部署(Phase 1)— 运行中
|
||
- Container `alist-serve-resource` (xhofe/alist v3.63.0),DSM port **5244**,网络 `bridge_hoelee`
|
||
- 挂载 `/volume1/Class` `/volume1/Book` `/volume1/Comic` `/volume1/Template`(只读 → /mnt/Class 等)
|
||
- 旧 alist (v3.54.0) 仍运行在 5243,**暂不处理**
|
||
- Admin: username `hoelee`,2FA 已关闭;compose 在项目根 `docker-compose.yml`
|
||
- DSM 上从 host shell 访问内网容器要用 `localhost:5244`;n8n 容器内用 `http://alist-serve-resource:5244`
|
||
|
||
### 2. NocoDB schema(Phase 2 重建)— 7 表最终模型
|
||
Base: **`AlistAccess`**(`p3lzkresswyowqg`),与旧 `accessDB` 完全隔离。表 + model_id + 关键点:
|
||
| 表 | model_id | 备注 |
|
||
|---|---|---|
|
||
| Customers | maqb9zgvza2te3k | plaintext password + **password_updated_at** |
|
||
| Products | molyb3pxv81l75g | default_duration_days;M2M→Resources |
|
||
| Resources | m6cm7tkxzd8m18c | alist_path |
|
||
| CustomerProducts | mvyqbv8ao7u6bct | 核心表(customer+product+expiry) |
|
||
| AccessGrants | mxdx3yhfcyej3ao | **物化状态表,不是 trigger**(禁止 loop) |
|
||
| AccessLogs | ml12zcyzlehgi21 | 审计 |
|
||
| Purchases | mwp12z9lcohhsuk | 纯记录 |
|
||
|
||
- 建表脚本:`scripts/create_nocodb_schema.py`(幂等)
|
||
- 表/字段/链接列 ID 全表:`docs/DATABASE_SCHEMA.md`(已更新为 7 表)
|
||
- NocoDB API token(workspace 级,来自 nc_api_tokens 表 n8n 那行):在 `docs/API_INTEGRATION.md` / SETUP.md 有记(或查 NocoDB UI)
|
||
|
||
### 3. W1 客户开通 workflow — **已实测跑通** ✅
|
||
- 文件:`n8n/W1-customer-provision.json`
|
||
- 流程:Webhook → Extract customerId → **Load Env(step 2, 用 $env)** → Fetch 记录 → 判断 create/update → Create Role → Find Role Id → Create User(role:[roleId]) → Find User Id → Write Back
|
||
- 关键:AList user/create 传 **空 role 数组会被拒**(自动填 guest→报 "admin or guest user can not be created")。必须先 Create Role 拿自增 id,再 Create User 带 `role:[id]`
|
||
- 已 import n8n;用户曾在 UI 微调 step 2(见下方「n8n env 访问结论」模板)
|
||
|
||
### 4. W2 CustomerProduct 权限同步 — **已改好待测试** ⏳
|
||
- 文件:`n8n/W2-customerproduct-sync.json`
|
||
- 流程:Webhook → Extract customerProductId → Load Env → **Compute Effective Grants**(登录 AList→读 customer→读全部 active CP→读 Product 的 M2M Resources→**per-Resource MAX(expires_at)** 合并→读 AList role 现状→diff→算 newScopes) → AList Update Role Scopes → Write Back CP Sync Status
|
||
- 已修:compute 节点 `***` 损坏行、所有 HTTP 表达式改引用 `$('Load Env').item.json.X`
|
||
- ⚠️ 待用户 import + 测用例 2(1 Product 2 Resource)、用例 3(重叠取 max expiry)
|
||
- **AccessGrants 表尚未在 W2 中被写入**(设计上它是物化结果,当前版本只更新 role scopes + CP sync 状态 —— 后续版本再物化进 AccessGrants?确认设计)
|
||
|
||
## 🔴 下一步(按序)
|
||
1. **用户测试 W2**(import 覆盖 → 用例 2、3 → AList 前端实测客户登录看目录)→ 报错贴 execution 错误
|
||
2. 验证通过后:**expiry scheduler**(每 5 分钟对账:全量 diff NocoDB↔AList,到期的移除 scope)—— 规格 §26/§37
|
||
3. 其余 workflow(W3-W6):Product/Resource 变更 → 重算受影响 customer;手动 sync;日志;通知
|
||
4. 本地 git 留档 → 验证稳定后推 Gitea(git.hoelee.com)
|
||
|
||
## 🐛 已沉淀的坑(勿再踩)
|
||
- **AList**:
|
||
- `user/create` 空 role 数组 → 400 "admin or guest"(源码 `req.Role==0 → 默认 guest` + `IsGuest()` 检查);先建 role 拿 id
|
||
- `role/create`/`update` 返回 `data:null`;create 后要 **list 按 name 定位 id**
|
||
- delete 用 `?id=` query param,不是 body
|
||
- role/list 返回 `data.content[]` + `data.total`(PageResp)
|
||
- GUEST=1 / ADMIN=2 / GENERAL=0;用户级 bit14=16384 = Check path limit
|
||
- **多文件夹授权正确做法(实测确认)**:`base_path="/"` + role scope 用**绝对路径**。base_path 设成
|
||
`/Class` 会导致 scope 绝对路径被 JoinPath 双重前缀 → 全部 403。见 API_INTEGRATION §9.8。
|
||
- 源码位置:`internal/op/user.go` CreateUser / `server/handles/role.go` / `internal/model/roles.go`
|
||
- **NocoDB**(`nocodb-selfhosted` skill 已更新):
|
||
- SingleSelect 不能建表内联选项;要先建空列再 PATCH `{"colOptions":{options},"dtxp":"a,b,c"}`
|
||
- Link 字段用 `type:"bt"`(belongs-to)建,`hm` 会把列名搞乱
|
||
- base/table/column ID 全在 DATABASE_SCHEMA.md
|
||
- **n8n env 访问(v1.123.72)**(memory 已记):
|
||
- Code 节点内 `$env` ✅(**用户实测可用**,如 `$env.NOCODB_URL`)
|
||
- HTTP 节点表达式字段里 `$env` / `process.env` ❌ 都不行
|
||
- 模式:step 2 放 "Load Env" Code 节点输出全部 env **并透传 payload 字段**,下游 HTTP 用 `$('Load Env').item.json.VAR`
|
||
- step 2 已验证模板:
|
||
```js
|
||
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 || '',
|
||
} };
|
||
```
|
||
|
||
## 📁 项目结构
|
||
```
|
||
alist-serve-resource/
|
||
├── docker-compose.yml / .env.example / .gitignore
|
||
├── n8n/ W1-customer-provision.json · W2-customerproduct-sync.json · SETUP.md · W2-DESIGN.md · w2-compute.js
|
||
├── scripts/ create_nocodb_schema.py · healthcheck.sh
|
||
└── docs/ API_INTEGRATION.md · DATABASE_SCHEMA.md · INSTALL.md · CONFIGURATION.md · SECURITY.md · BACKUP_RESTORE.md · UPGRADE.md · TROUBLESHOOTING.md · PROJECT_STATE.md
|
||
```
|
||
|
||
## 待确认的设计点
|
||
1. W2 当前不写 AccessGrants 表 —— 是否在验证后补物化写入?(避免 AccessGrant→n8n loop 的前提下,由 W2 单向写)
|
||
2. 密码:客户可自改 AList 密码 → NocoDB plaintext 会失真;当前不强制回写(用户已选方案 C)
|
||
3. 对账/到期只靠 n8n 定时扫描(AList 无 expiry 概念)—— 已确认 |