dsh-api-runner 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -44,7 +44,7 @@ DSH 插件:把「后端接口清单」「本地服务启停」「日志增量
44
44
  dsh plugin --profile web add dsh-api-runner
45
45
  ```
46
46
 
47
- npm 上已发布 `dsh-api-runner@0.2.1`,这条是推荐路径,也是 DSH「插件中心」里一键安装的形式。
47
+ npm 上已发布 `dsh-api-runner@0.3.1`,这条是推荐路径,也是 DSH「插件中心」里一键安装的形式。
48
48
 
49
49
  ### 从 GitHub 源码安装
50
50
 
@@ -77,6 +77,33 @@ DSH 会把仓库 clone 到 profile 的 `node_modules` 下。写成带包名的
77
77
 
78
78
  数据本身不用迁移:之前被抹掉的 `query` 需要你重新填一次(或从 YAML 恢复)。
79
79
 
80
+ ### 从 0.3.0 升级到 0.3.1
81
+
82
+ 0.3.1 加了**按项目的数据库授权**和一批修复,**需要硬刷新页面**:
83
+
84
+ 1. **数据库默认拒绝访问**。连接创建后处于「未授权」状态,必须到「数据库 → 项目授权」里
85
+ 给当前项目授权(未授权 / 只读 / 读写)。未授权的连接模型完全看不见,也查不了。
86
+ 2. **模型侧永远只读**。即使授权了「读写」,工具调用也只允许查询语句;
87
+ 写数据只能在面板里由人操作。
88
+ 3. **毁灭性操作一律拒绝**:`DROP` / `TRUNCATE` / 无 `WHERE` 的 `DELETE` 和 `UPDATE`,
89
+ 即使有读写权限也拦。请到数据库客户端人工处理。
90
+ 4. **修复**:编辑连接后点「测试」会报「连接池别名已存在」(达梦);试连没有超时上限,
91
+ 最长要等 60 秒且期间其它按钮不可点。现在试连 15 秒超时,且只锁「测试」按钮。
92
+ 5. **面板 UI 重做**:统一配色分层、圆角、阴影;响应查看的切换条与一级标题胶囊彻底分开命名。
93
+ 6. **支持 URL 直连**:连接可填整条 URL(如 `dm://user:pass@host:5236`),密码加密保存、界面只显示打码。
94
+
95
+ 升级后你之前建的连接会变成「未授权」,需要到「项目授权」里点一下「只读」或「读写」。
96
+
97
+ ### 从 0.2.x 升级到 0.3.0
98
+
99
+ 0.3.0 新增数据库能力,**需要硬刷新页面**,且有两个注意点:
100
+
101
+ 1. **Node 版本要求提高到 `>=22.5`**(SQLite 用 Node 内置的 `node:sqlite`,22.5 起才有)。低版本 Node 上插件会加载失败,请先升级 Node。
102
+ 2. **面板多了一个「数据库」页签**,支持 SQLite / MySQL / PostgreSQL / Oracle / 达梦。SQLite 开箱即用(零依赖);其它类型需要按需装驱动,见「数据库」章节。
103
+ 3. **面板 UI 重做**:顶部页签和一级标题改成胶囊分段控件,窄面板下横向滚动不换行。
104
+
105
+ 原 dsh-db-tool 用户可以卸载它了——数据库能力已合并进来,且不再依赖 `betterSidebar`(原插件把 `betterSidebar` 当硬依赖,服务未就绪时会卡住整个 web 启动)。
106
+
80
107
  ### 本地开发:link 安装
81
108
 
82
109
  ```bash
@@ -129,9 +156,10 @@ Windows 下 link 路径写成 `link:D:/path/to/dsh-api-runner` 也等价。
129
156
 
130
157
  - **DSH**:`>=0.1.0-rc.6 <0.2.0-0 || >=0.2.0-rc.1 <0.3.0-0`
131
158
  - **已验证环境**:DSH `0.2.1-alpha.1` / Windows
132
- - **Node.js**:`>=20`
159
+ - **Node.js**:`>=22.5`(SQLite 用内置 `node:sqlite`,22.5 起才有)
133
160
  - **平台**:Windows / macOS / Linux(进程与端口发现按平台分流)
134
161
  - **可选环境**:JDK(`jps` / `jcmd`,用于 Java 服务发现与接管)、Maven(`mvn`,用于接管时编译)
162
+ - **可选数据库驱动**:SQLite 用 Node 内置,无需安装;MySQL 需 `mysql2`、PostgreSQL 需 `pg`、Oracle 需 `oracledb`(原生模块)、达梦需 `dmdb`(纯 JS)。都是按需加载,不装只影响对应类型
135
163
 
136
164
  ## 配置
137
165
 
@@ -213,6 +241,7 @@ logs:
213
241
  | `call` | 按 `api_id` 调接口,`params` 传占位符;已保存 token 会自动注入 |
214
242
  | `login` | 调登录接口并把 token 存进项目档(`api_id` 指向配了 `login.tokenPath` 的接口) |
215
243
  | `java` | 本机 Java 服务:`java_action` = `list` / `detail`(详情+日志)/ `start`(接管启动)/ `stop` / `restart` / `log` / `ignore` / `unignore` / `save`(保存启动配方)/ `unsave` / `start_saved`(按配方启动,不需要 IDEA 里先有进程)/ `start_all`(一键启动所有已保存的服务,可用 `java_ids` 只启部分)/ `settings`(读或改全局设置)/ `status`,详见下文 |
244
+ | `db` | 数据库:`db_action` = `list_connections` / `test` / `save` / `delete` / `query`(执行 SQL,**默认只读**)/ `list_databases` / `list_tables` / `describe_table`,详见下文 |
216
245
  | `service` | `service_action` = `start` / `stop` / `restart` / `wait` / `status` |
217
246
  | `log` | 增量读日志(`pattern` 正则过滤,`max_bytes` / `max_lines` 限流) |
218
247
  | `wait_log` | 等某个特征出现在日志里(`timeout_ms` 超时) |
@@ -332,6 +361,82 @@ apis:
332
361
  - **IDEA Run/Debug 按钮 + 不想被接管** → 配 `mode: observe`。模型调 `verify` 时不会杀进程,只等健康检查通过;需要重启时你在 IDEA 里点重启,模型调 `service action=wait` 等你起来。
333
362
  - **完全交给插件** → 配 `mode: managed`,`command` 写成 `mvnw.cmd spring-boot:run` 这类可拉起的命令。注意 IDEA 和插件不要同时占用同一个端口。
334
363
 
364
+ ## 数据库
365
+
366
+ 把数据库能力合并进来,替代原来的 `dsh-db-tool`。
367
+
368
+ > **为什么合并**:原 `dsh-db-tool` 把 `betterSidebar` 当**硬依赖**(`inject` 里列出且没有兜底),服务没就绪就一直 `pending`,拖垮整个 web 启动(`web boot: 1 entry did not activate`)。本插件的数据库模块**不依赖任何 DSH 服务**,只依赖 Node 内置能力。
369
+
370
+ ### 支持的数据库
371
+
372
+ | 类型 | 驱动 | 说明 |
373
+ | --- | --- | --- |
374
+ | `sqlite` | **Node 内置 `node:sqlite`** | 零依赖、零原生编译。填文件路径,或 `:memory:` 用内存库 |
375
+ | `mysql` | `mysql2`(纯 JS) | 需要 `npm install mysql2` |
376
+ | `postgres` | `pg`(纯 JS) | 需要 `npm install pg` |
377
+ | `oracle` | `oracledb`(原生模块) | 需要 `npm install oracledb`,且要匹配 Node ABI |
378
+ | `dm` | `dmdb`(纯 JS) | 达梦 DM8,需要 `npm install dmdb`。默认端口 5236 |
379
+
380
+ 驱动都是**按需动态加载**:没装某个驱动只会让那种类型的连接报「缺少 xx 依赖」,不会让整个插件加载失败。SQLite 开箱即用,不需要装任何东西。
381
+
382
+ ### 面板
383
+
384
+ 「数据库」子页(和「接口 / 服务 / 设置」并列)分三块:
385
+
386
+ - **连接管理**:新增 / 编辑 / 删除 / 测试。密码框是 `type=password`,编辑时不回填,留空表示不修改。
387
+ - **执行 SQL**:写 SQL → 点「执行」→ 结果表格(列头 + 行 + 行数/耗时)。**只读模式默认开启**,拒绝 INSERT/UPDATE/DELETE/DDL;关掉时会显示醒目提示。
388
+ - **表结构**:选连接 → 刷新表清单 → 点表名看字段/类型/可空/默认值/主键/索引,同时自动把 `SELECT * FROM xxx LIMIT 100` 填进 SQL 框。
389
+
390
+ ### 模型调用
391
+
392
+ ```
393
+ action=db, db_action=query, connection_id=<id>, sql="SELECT * FROM t WHERE id = ?", db_params=[7]
394
+ ```
395
+
396
+ 默认只读。要写数据必须显式传 `db_readonly=false`。
397
+
398
+ ```jsonc
399
+ // 保存一个 SQLite 连接
400
+ { "action": "db", "db_action": "save",
401
+ "connection": { "name": "本地库", "type": "sqlite", "database": "D:\\data\\app.db" } }
402
+
403
+ // 看表结构
404
+ { "action": "db", "db_action": "describe_table", "connection_id": "<id>", "table": "users" }
405
+ ```
406
+
407
+ ### 安全设计
408
+
409
+ **四层防线,任何一层不通过就拒绝。**
410
+
411
+ | 层 | 项 | 做法 |
412
+ | --- | --- | --- |
413
+ | 1 | 项目授权 | **默认拒绝**。连接创建后是「未授权」,必须到面板「项目授权」里给当前工作空间授权(未授权 / 只读 / 读写)。未授权的连接模型完全看不见(连 `describe` 都不列出),也查不了 |
414
+ | 2 | 模型侧只读 | 工具调用**固定** `readonly: true`,不接受参数里的 `db_readonly=false`。模型无论怎么构造参数都写不了数据——写操作只能由人在面板里做 |
415
+ | 3 | SQL 性质 | 全串扫描(剔除字符串与注释)拦危险关键字;识别 `WITH ... DELETE`、`FOR UPDATE`、`INTO OUTFILE`、`load_file()`;`PRAGMA` 区分只读与赋值式;多语句一律拒绝 |
416
+ | 4 | 毁灭性操作 | 即使有读写授权,`DROP` / `TRUNCATE` / 无 `WHERE` 的 `DELETE` 和 `UPDATE` / `GRANT` / `REVOKE` 仍一律拒绝 |
417
+
418
+ 其余:
419
+
420
+ | 项 | 做法 |
421
+ | --- | --- |
422
+ | 密码存储 | AES-256-GCM 加密,密钥由机器特征派生。**落盘文件与任何返回值里都没有明文**,列表只给掩码 |
423
+ | URL 模式 | 整条连接串加密保存,界面回显打码成 `dm://user:***@host:port` |
424
+ | SQL 注入 | 一律参数化(`?` / `$1` / `:1`),`db_params` 传值,**不要拼字符串** |
425
+ | 多语句 | 只允许单条。用「跳过字符串/注释」的扫描判断,所以 `WHERE note = 'a;b'` 不会被误判 |
426
+ | 超时 | 查询默认 30s;**试连 15s**(交互动作,不能等驱动自己卡 60 秒) |
427
+ | 结果集 | 默认上限 1000 行,超出截断并提示真实总行数 |
428
+ | 错误信息 | 驱动异常会抹掉 `password=xxx` 和 `://user:pass@` 这类片段再返回 |
429
+ | 信息探测 | 未授权时统一返回 `NOT_AUTHORIZED`,不暴露「某个连接 id 是否存在」 |
430
+
431
+ > 想看这些规则的实际测试,见 `test/security.mjs`(60 项,覆盖 14 种只读语句、27 种写操作、7 种毁灭性操作)。
432
+
433
+ ### 已知限制
434
+
435
+ - **SQLite 是同步驱动**(`node:sqlite` 的 `DatabaseSync`):超大结果集会阻塞事件循环。行数上限(默认 1000)能挡住大部分场景。
436
+ - **`oracledb` 是原生模块**:如果 Node ABI 不匹配会加载失败,此时该类型连接报错,但其它类型不受影响。确实需要时再单独装并验证。
437
+ - **达梦(dmdb)已知问题**:连接失败后它自己的池清理会抛 `TypeError: ... reading 'log'`(未捕获异步异常)。插件已加防护拦掉这个精确签名,不会带崩宿主;但连接失败本身仍会正常报错。
438
+ - 连接配置是**跨项目**的(同一套库常被多个项目共用),但**访问权按项目隔离**——授权只对授权的那个项目生效。
439
+
335
440
  ## HTTP 接口
336
441
 
337
442
  除工具外,插件还注册了同源路由(需要 DSH 会话认证):
@@ -359,6 +464,14 @@ apis:
359
464
  - `POST /dsh-api-runner/api/java/start-saved` → `{ id, compile?, port?, debug?, timeout_ms? }` 按配方启动
360
465
  - `POST /dsh-api-runner/api/java/start-all` → `{ ids?, compile?, restart?, timeout_ms? }` 一键启动(后台跑,立刻返回 `{ batchId, total }`;`ids` 省略即全部已保存的)
361
466
  - `GET /dsh-api-runner/api/java/start-all/status?batch=<batchId>` → 批次进度 `{ finished, done, total, current, items, result }`(`batch` 省略取最近一个)
467
+ - `GET /dsh-api-runner/api/db` → 数据库连接列表(密码只给掩码)
468
+ - `POST /dsh-api-runner/api/db/test` → `{ connection_id }` 或 `{ connection }` 试连
469
+ - `POST /dsh-api-runner/api/db/save` → `{ connection }` 保存/更新连接
470
+ - `POST /dsh-api-runner/api/db/delete` → `{ connection_id }` 删除连接
471
+ - `POST /dsh-api-runner/api/db/query` → `{ connection_id, sql, params?, readonly?, max_rows?, timeout_ms? }` 执行 SQL
472
+ - `GET /dsh-api-runner/api/db/databases?connection_id=` → 库清单
473
+ - `GET /dsh-api-runner/api/db/tables?connection_id=&database=` → 表清单
474
+ - `GET /dsh-api-runner/api/db/table?connection_id=&name=` → 表结构(字段 + 索引)
362
475
  - `GET /dsh-api-runner/api/java/log?id=<服务名>&from=<字节偏移>` → 插件启动服务的日志增量
363
476
  - `GET /dsh-api-runner/api/java/detail?id=<id 或 pid>&source=&from=&lines=&max_bytes=` → 服务详情 + 日志(含可选日志文件清单;不给 `from` 时按 `lines` 取末尾若干行)
364
477
  - `GET /dsh-api-runner/api/settings` → 全局设置(默认工作区、Maven 主路径、数据目录、生效目录)