Files
dsh_ai1net_server/交付物/插件建库权限-B档落地-20260923.md
T
admin ce8e6ceed9 chore(工作区): 纳入版本控制基线(回收 411 MB 过程产物)
回收 411 MB(470 M → 58.8 M),全部经回收站,可恢复:
- 待清理/(146.2 M,含 relay 分片 128 M 与 42 项过程目录)
- tmp/(32.4 M,按接续棒命名的过程临时区)
- .workbuddy/tmp/(39.5 M)
- 4 份 workbuddy.db 冗余副本(101 M,09-23 事故的坏副本 / 抢救产物)
- tmp/im16/gw/centrifugo 二进制(63.9 M,可重下)+ 缓存残留

入库范围:常驻规则(CODEBUDDY.md / README.md / state.py)、在途接续入口与
接续包、docs/、交付物/、交接单/、归档/、scripts/、.codebuddy/、
.workbuddy/memory/;共 398 件,其中 >60 KB 的 26 件全为文档。

排除(.gitignore):tmp/、待清理/、运行态日志与缓存、*.db 与 DB 备份整目录、
打包二进制(*.tar.gz / *.tgz)、记忆修复前备份。
2026-09-24 07:51:03 +08:00

102 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# B 档落地 · 插件建库权限(角色级授权 `CREATEDB`)
> **拍板**:2026-09-23 用户原话「**B**」⇒ 建库通路 = **B 档(给 `dshs` 角色加 `CREATEDB`)**。
> **状态**:✅ **已在 47 落地并通过 11 条验收**(2026-09-23 05:4x)。
> **机位**:仅 **47**(PG 只在 Manager 上;106 无 PG)。
> **前置件**:`交付物/建库权限-D档被证伪与修正判定-20260923.md`(本档为何取代 D 档)。
---
## 一 拍板内容与落地动作
```sql
ALTER ROLE dshs CREATEDB; -- 唯一一句 DDL(幂等)
```
平台侧建库 = 用**现有** `dshs` 连接、**单语句**执行:
```sql
CREATE DATABASE dshs_pl_<pluginId> OWNER dshs ENCODING 'UTF8' TEMPLATE template0;
```
🔴 **必须单语句、不走事务** —— `CREATE DATABASE` 不能在事务块内(这是 D 档被证伪的同一条 PG 限制,B 档同样受约束,只是不再需要函数封装)。平台侧用 `pg` 的**非事务**执行路径(顶层 `client.query('CREATE DATABASE …')`,⛔ 不要包在 `BEGIN/COMMIT` 里)。
---
## 二 验收(11 条 · 实测读数)
| # | 断言 | 实测 | 判定 |
|---|---|---|---|
| ① | `dshs` 可建库 | 建库成功 | ✅ |
| ② | 重名建库被拒(幂等靠错误码捕获) | `ERROR: database … already exists` | ✅ |
| ③ | **新库属主 = `dshs`** | `dshs_pl_bselftest/dshs` | ✅ |
| ④ | 可连新库并建表 | `create table` 成功 | ✅ |
| ⑤ | 新库内对象归 `dshs` | 表数 = `1` | ✅ |
| ⑥ | **权限精确**(须 `f\|t\|f`) | `false\|true\|false` | ✅ |
| ⑦ | ⛔ 未拿到 `CREATEROLE` | `t`(= 无该权) | ✅ |
| ⑧ | ⛔ 仍非超级用户 | `t`(= 非超管) | ✅ |
| ⑨ | 库名白名单 | **PG 侧零拦截**(建 `evil_probe` 成功) | ⚠️ 见 §三 |
| ⑩ | 内核表完好 | `13` | ✅ |
| ⑪ | 现有库未损 | `2`(`dshs` + `postgres`) | ✅ |
| — | 清理后残留 | `0` | 服务 `dshs = active` | ✅ |
⑥ 是本档的**核心断言**:只多出 `CREATEDB` 一位,`rolsuper` / `rolcreaterole` 必须保持 `false`。
---
## 三 🔴 本档的已知代价(必须由平台代码兜住)
⑨ 揭示了 B 档的真实缺口:**PG 侧没有"只允许建 `dshs_pl_*` 前缀库"的原生机制** ⇒ 白名单**只能靠平台代码自觉**。这正是 09-22 否掉 A 档的同一条理由,B 档原样继承。
**因此平台侧必须强制三点**(缺一即为该档的漏洞面):
1. **库名单点生成** —— 库名只能来自 `.dsh` 声明的换算函数(包名去 scope、小写、`-`→`_`、前缀 `dshs_pl_`),⛔ 不接受外部传入的任意串。
2. **建库前正则复校** —— 照抄 D 档那条:`^dshs_pl_[a-z][a-z0-9_]{0,40}$`,不匹配直接 400,**⛔ 不把变量拼进 SQL 字符串**(用标识符引用或参数化)。
3. **每次建库落台账 + 审计** —— 写 `plugin_datastores`(库名 / 插件 ID / 建库时间 / 结果),使"平台到底建过哪些库"可对账;`DROP` 须 admin 二次确认(`DB-03 §六-4`)。
---
## 四 回滚
```sql
ALTER ROLE dshs NOCREATEDB; -- 一句话回滚
```
影响面:**已建好的 `dshs_pl_*` 库不受影响**(属主是 `dshs`,平台照常建表读写)⇒ 只是新插件建不了库。回滚点 = 平台建库口返回 `PG_CREATEDB_MISSING`(503)。
---
## 五 ⚠️ 必须同步落的文档(回滚/重建时不能漏)
1. **`dsh-server-docs/DEPLOY-本部署.md`** DB 初始化步骤追加:`ALTER ROLE dshs CREATEDB;`(标 🔴 用途 = 插件建库;⛔ 不得顺手给 `CREATEROLE` / `SUPERUSER`)。
2. **`交接单/插件投放与分库线-①…md`** —— `§五 S4-2` 建库 = **admin 显式按钮**;`§十-D1` 原文「上传钩子建库」须同步改。
3. **`DB-03-插件数据面规范.md`** —— `§三`(谁建库)/ `§四`(何时建)/ `§六`(台账表 `plugin_datastores`)。
---
## 六 平台侧代码接口
```ts
// src/db/plugin-data/datastore.ts
export function pluginDbNameOf(pkgName: string): string // 纯函数:包名 → dshs_pl_*
export function assertPluginDbName(dbName: string): void // ^dshs_pl_[a-z][a-z0-9_]{0,40}$
export async function createPluginDatabase(client: PoolClient, dbName: string): Promise<'created' | 'exists'>
// 错误映射(供路由层用):
// PG 42P04 duplicate_database ⇒ 'exists'(幂等,非错误)
// PG 42501 权限不足 ⇒ 503 PG_CREATEDB_MISSING(未授 CREATEDB / 已回滚)
// 名称不合白名单 ⇒ 400 invalid_plugin_db_name(平台侧复校,⛔ 不下发到 PG)
```
> 错误码变更:D 档的 `PLUGIN_DB_HELPER_MISSING` 随档位**作废**,恢复为 `PG_CREATEDB_MISSING`。
---
## 七 未做(具名原因)
| 项 | 原因 |
|---|---|
| 平台侧代码实现(§六) | 属交接单 §五 S4 的**代码面**,按用户拍板的「A 档排期」= 等 IM 线整阶段收官后一次性跑完 |
| §五 三份文档回写 | 本轮落点已定,但改交接单/DB-03 会与他线**共享文件**冲突;按 R9 拉长到同一批执行 |
| `drop_plugin_database` | ⛔ 不在本次拍板范围(`DB-03 §六-4` 将来一并走) |