docs: architecture, permissions, admin guide + refresh schema/workflows

- New ARCHITECTURE.md (3-layer model + data flow + lifecycle matrix)
- New PERMISSIONS.md (grant calculation rules + enforcement)
- New ADMIN_GUIDE.md (華文极简:Customer/CustomerProduct 授权 + Product/Resource 管理)
- Rewrite N8N_WORKFLOWS.md from WF1-6 (obsolete) to actual W1/W2/W3
- Update DATABASE_SCHEMA.md: fix status enum (drop 'disabled'), add 5-status semantics
- Update README.md doc index
This commit is contained in:
2026-08-23 07:30:41 +08:00
parent 89b769cfd9
commit 80eb2b71be
6 changed files with 292 additions and 39 deletions
+59
View File
@@ -0,0 +1,59 @@
# 权限模型说明(Permissions)
> 本文解释「最终生效的 AList 权限」是怎么从 NocoDB 数据算出来的。更新日期 2026-08-23。
## 最终生效权限的三要素
一个客户最终能访问哪些 AList 路径,由三样东西决定:
1. `Customers.status` — 整户开关(account-level)
2. `CustomerProducts` — 买了哪些产品 + 何时到期(product entitlement)
3. `Product → Resources` — 每个产品包含哪些路径(M2M),资源有 `alist_path`
三者合起来 → n8n 调 `calculateEffectiveGrants` → 算出 desired scopes → 写到 AList Role。
## 计算规则(calculateEffectiveGrants)
对某个客户,desired scopes = 以下两类的**并集**:
### (A) CustomerProduct 派生
遍历该客所有 CustomerProduct:
- 跳过 `status != active` 的
- 跳过 `expires_at <= 今天` 的(**到期当天视为已过期**)
- 通过 `product` link → Product 的 M2M Resources → 拿到每个 resource 的路径
### (B) AccessGrant 派生(预留,当前不经 n8n 写入)
- 同 (A),跳过非 active / 已过期
### 合并规则
- 同一个 path 来自多个来源 → 取 **MAX(expires_at)**(两个产品共享一个资源时,取较晚的到期)
- permission:`readwrite` 覆盖 `read`(readwrite=1,否则 0)
- basis:本系统客户一律 **只读**(permission 0),读写在 AccessGrant 手工授权时可用
## AList 侧落地(enforcement)
- 每个客户 = 1 个专属 AList Role(`name = customer-<username>`)
- Role 的 `permission_scopes` = desired scopes 的绝对路径数组
- 客户 User:`base_path = '/'`(**必须 `/`**,否则 JoinPath 双重前缀 → 全 403)
- 客户 User:`permission = 16384`(bit 14 = Check path limit 置位 → 只能访问 role scope 内的路径)
- `disabled` 由 status 控制:非 active → `disabled=true`
## 多文件夹授权正确做法(已实测)
- `base_path = "/"` + role scope 用**绝对路径**(如 `/Class/Forex`、`/Template/Music`)
- 结果:客户只看到被授权路径,未授权挂载点(`/Book` 等)不泄露
## 到期与状态的关系
| 场景 | 结果 |
|---|---|
| CustomerProduct 到期 | 该产品派生的路径从 scopes 移除(W2/W3 对账自动修)|
| 整个 Customer status 变 non-active | 整户 disable + scopes 清空(W1)|
| non-active → active 恢复 | 重算当前有效 scopes,**不恢复已过期产品** |
## 重要边界
- `Customers.status` 是账户级开关,**不替代** CustomerProduct 的到期。
- 例:Customer = `expired`,其 Product A 仍 active 且未到期。恢复 `expired → active` 后 Product A 会自动回来;已过期的 Product B 不会回来。
- 状态变化**不自动改** CustomerProducts(不批量改 CP.status、不改 expires_at、不删除 CP)。
- AccessGrants 历史记录**不删除**;恢复时重算,不用旧 AccessGrant scope 直接恢复。