dsh-lh-data 0.1.0
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 +332 -0
- package/cordis.patch.yml +7 -0
- package/lib/admin-contract.d.ts +274 -0
- package/lib/admin-http.d.ts +31 -0
- package/lib/admin-validate.d.ts +87 -0
- package/lib/admin.d.ts +60 -0
- package/lib/client.js +2969 -0
- package/lib/client.js.map +1 -0
- package/lib/datasource/columns.d.ts +13 -0
- package/lib/datasource/connection.d.ts +33 -0
- package/lib/datasource/connector/base.d.ts +21 -0
- package/lib/datasource/connector/mysql.d.ts +23 -0
- package/lib/datasource/connector/postgresql.d.ts +23 -0
- package/lib/datasource/crypto.d.ts +13 -0
- package/lib/datasource/driver.d.ts +39 -0
- package/lib/datasource/errors.d.ts +25 -0
- package/lib/datasource/importer.d.ts +38 -0
- package/lib/datasource/source-sql.d.ts +11 -0
- package/lib/datasource/source-store.d.ts +28 -0
- package/lib/datasource/types.d.ts +119 -0
- package/lib/db.d.ts +91 -0
- package/lib/http-common.d.ts +31 -0
- package/lib/http.d.ts +13 -0
- package/lib/index.d.ts +50 -0
- package/lib/index.js +5735 -0
- package/lib/parse.d.ts +41 -0
- package/lib/preview.d.ts +75 -0
- package/lib/render.d.ts +100 -0
- package/lib/scope-registry.d.ts +47 -0
- package/lib/scope.d.ts +56 -0
- package/lib/sql.d.ts +133 -0
- package/lib/store.d.ts +174 -0
- package/lib/table.d.ts +38 -0
- package/lib/tooling.d.ts +267 -0
- package/lib/tools/datasource.d.ts +16 -0
- package/lib/tools/import.d.ts +21 -0
- package/lib/tools/read.d.ts +77 -0
- package/lib/tools/registry.d.ts +12 -0
- package/lib/tools/write.d.ts +37 -0
- package/lib/view.d.ts +138 -0
- package/package.json +61 -0
- package/src/admin-contract.ts +351 -0
- package/src/admin-http.ts +254 -0
- package/src/admin-validate.ts +393 -0
- package/src/admin.ts +558 -0
- package/src/client/index.ts +403 -0
- package/src/client/settings/CreateForm.tsx +186 -0
- package/src/client/settings/DataSourceForm.tsx +278 -0
- package/src/client/settings/DataSourcesPanel.tsx +301 -0
- package/src/client/settings/DatasetEditor.tsx +245 -0
- package/src/client/settings/DatasetTable.tsx +110 -0
- package/src/client/settings/DatasetsPanel.tsx +207 -0
- package/src/client/settings/RowsPanel.tsx +196 -0
- package/src/client/settings/Section.tsx +41 -0
- package/src/client/settings/SourceTablesPanel.tsx +226 -0
- package/src/client/settings/api.ts +154 -0
- package/src/client/settings/styles.ts +125 -0
- package/src/datasource/columns.ts +56 -0
- package/src/datasource/connection.ts +124 -0
- package/src/datasource/connector/base.ts +54 -0
- package/src/datasource/connector/mysql.ts +187 -0
- package/src/datasource/connector/postgresql.ts +212 -0
- package/src/datasource/crypto.ts +61 -0
- package/src/datasource/driver.ts +108 -0
- package/src/datasource/errors.ts +69 -0
- package/src/datasource/importer.ts +262 -0
- package/src/datasource/index.ts +62 -0
- package/src/datasource/source-sql.ts +64 -0
- package/src/datasource/source-store.ts +152 -0
- package/src/datasource/types.ts +133 -0
- package/src/db.ts +277 -0
- package/src/http-common.ts +91 -0
- package/src/http.ts +130 -0
- package/src/index.ts +486 -0
- package/src/parse.ts +213 -0
- package/src/preview.ts +198 -0
- package/src/render.ts +294 -0
- package/src/scope-registry.ts +111 -0
- package/src/scope.ts +159 -0
- package/src/sql.ts +491 -0
- package/src/store.ts +412 -0
- package/src/table.ts +160 -0
- package/src/tooling.ts +551 -0
- package/src/tools/datasource.ts +378 -0
- package/src/tools/import.ts +282 -0
- package/src/tools/read.ts +536 -0
- package/src/tools/registry.ts +56 -0
- package/src/tools/write.ts +241 -0
- package/src/view.ts +371 -0
package/README.md
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# dsh-lh-data
|
|
2
|
+
|
|
3
|
+
dsh(deepseek-harness)插件:把工作区内的 Excel / CSV 导入本地 Turso(libSQL),并以**句柄化的 `dataset_*` 工具**做增删改查。
|
|
4
|
+
|
|
5
|
+
核心设计:**物理表名从不外泄**。模型侧只见 `datasetId` / 登记名 / 业务列名(保留中文表头);查询只拿「少量预览行 + 全量统计摘要」,完整结果由**前端表格卡片按 `viewId` 分页拉取**。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 特性
|
|
10
|
+
|
|
11
|
+
- **8 个 `dataset_*` 工具**:导入、列表、列信息、查询、插入、更新、删除、删表。
|
|
12
|
+
- **4 个 `datasource_*` 工具**:登记 MySQL / PostgreSQL 连接、测试连通、浏览远端表、把远端表全量导入成数据集;导入产出的数据集与文件导入完全同权。
|
|
13
|
+
- **句柄化**:SQL 里的物理表名由插件持有与替换,模型与 HTTP 响应中都不可见。
|
|
14
|
+
- **结果视图**:大结果集自动建视图,模型只拿片段,前端卡片翻页 / 排序 / 导出 CSV;视图持久化,重启后仍可翻页。
|
|
15
|
+
- **工作区隔离**:数据集按会话 cwd(或 WorkspaceId)分 scope,导入文件必须落在工作区内(禁止 `../` 穿越)。
|
|
16
|
+
- **写操作 fail-closed**:`tools/pre-execute` 门禁 + 单调守卫 + 只读模式三重保护。
|
|
17
|
+
- **只读 SQL 校验**:原始 SQL 仅允许单条 `SELECT` / `WITH` / `EXPLAIN`,表引用白名单,无 DDL / 写关键字。
|
|
18
|
+
- **设置页**:浏览器「设置 → 数据集」内分「数据集 / 数据源」两个页签,可聚合查看、新建空表、改描述、分页看数据、删除;数据源页签支持登记连接、测试连通、浏览远端表并一键导入。
|
|
19
|
+
- **可选依赖降级**:`jobs` / `systemPrompt` / `webServer` / `connection` 任一缺失都只降级对应能力,不影响装载;`mysql2` / `pg` 已随插件**默认安装**,但仍走动态加载——只用文件导入时不会拖慢启动,万一运行环境缺包也给明确安装提示而非抛裸栈。
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 常用命令
|
|
24
|
+
|
|
25
|
+
### 编译
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
pnpm run build
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
产物两个半身(由 `tsdown` 并行构建,共用 `lib/`):
|
|
32
|
+
|
|
33
|
+
| 产物 | 格式 | 入口 | 说明 |
|
|
34
|
+
| --- | --- | --- | --- |
|
|
35
|
+
| `lib/index.js` | ESM | `src/index.ts` | 主机半身,cordis 插件 |
|
|
36
|
+
| `lib/client.js` | CJS 工厂 | `src/client/index.ts` | 浏览器半身,注册到 `window.__ModuleLoader__` |
|
|
37
|
+
|
|
38
|
+
### 类型检查
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
pnpm run typecheck # 主机 + 客户端
|
|
42
|
+
pnpm run typecheck:host # 仅主机
|
|
43
|
+
pnpm run typecheck:client # 仅客户端
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 启动 web 服务
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
npx @deepseek-ai/dsh web
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### 安装插件
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
本地源码
|
|
56
|
+
```
|
|
57
|
+
npx @deepseek-ai/dsh plugin --profile web add D:\fastwork\projects\node\dsh-lh-data
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
github
|
|
61
|
+
```
|
|
62
|
+
npx @deepseek-ai/dsh plugin --profile web add https://github.com/linkedshine/dsh-lh-data.git
|
|
63
|
+
```
|
|
64
|
+
中央仓库
|
|
65
|
+
```
|
|
66
|
+
npx @deepseek-ai/dsh plugin --profile web add dsh-lh-data
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
删除:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
npx @deepseek-ai/dsh plugin --profile web remove dsh-lh-data
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
插件通过 `cordis.patch.yml` 注入默认配置(`dbPath` 留空、`requireApprovalForWrites: false`)。
|
|
76
|
+
|
|
77
|
+
### 验证脚本(需先 build)
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
pnpm run import # examples/run-import.mjs:单元校验 + 导入 → list → schema → query → 写操作 → 门禁 → 生命周期
|
|
81
|
+
pnpm run view # examples/run-view.mjs :大结果集 → 片段 → 前端分页 → 鉴权 / 持久化恢复 / 降级
|
|
82
|
+
pnpm run admin # examples/run-admin.mjs :聚合列表 / 新建空表 / 改名改描述 / 删除 / 分页看数据
|
|
83
|
+
pnpm run datasource # examples/run-datasource.mjs:加解密 / 列映射 / 装载门禁 / 数据源 CRUD / 脱敏 / 鉴权 / 降级
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`mysql2` / `pg` 已随插件默认安装,`pnpm install` 后即可连接数据库。想跑真实的端到端导入验证:把脚本顶部的 `DEMO` 改成你的库,再 `node examples/run-datasource.mjs --live`。
|
|
87
|
+
|
|
88
|
+
连接失败(端口未开放、主机不可达、账号密码错误、库不存在等)时,工具与设置页统一返回**可读的中文原因**(如「连接被拒绝(主机可达,但端口未开放或服务未启动)」),不会把驱动的裸栈抛给模型或浏览器;驱动确实缺失时仍给 `pnpm add mysql2` / `pnpm add pg` 提示。
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 工具一览
|
|
93
|
+
|
|
94
|
+
| 工具 | 类型 | 主要参数 | 返回要点 |
|
|
95
|
+
| --- | --- | --- | --- |
|
|
96
|
+
| `dataset_list` | 读 | — | `datasets[]`:`datasetId`、`name`、行数、列数、`status`、`sourcePath`、时间 |
|
|
97
|
+
| `dataset_schema` | 读 | `dataset` | 列名(原始表头)、`sanitizedName`、推断类型、可空、样例值、说明 |
|
|
98
|
+
| `dataset_query` | 读 | `dataset`、`columns`、`where`、`orderBy`、`limit`、`offset`、`sql` | `matchedRows` / `totalRows`、`preview`(少量预览行)、`summary`(列统计)、可选 `view` |
|
|
99
|
+
| `dataset_import` | 写 | `path`、`name?`、`sheet?`、`limit?`、`background?` | `datasetId`、行列数、`status: ready \| running`、`jobId?`、列信息 |
|
|
100
|
+
| `dataset_insert` | 写 | `dataset`、`rows[]` | `inserted`、最新 `rowCount` |
|
|
101
|
+
| `dataset_update` | 写 | `dataset`、`rowId`、`data` | `updated`、`changedColumns[]` |
|
|
102
|
+
| `dataset_delete` | 写 | `dataset`、`rowId` | `deleted`、剩余 `rowCount` |
|
|
103
|
+
| `dataset_drop` | 写 | `dataset` | `dropped`(连同物理表与元数据删除,不可恢复) |
|
|
104
|
+
| `datasource_list` | 读 | — | `sources[]`:`id`、`name`、`type`、`host`、`port`、`database`、`status`、`lastError`、`lastCheckedAt`(不含密码) |
|
|
105
|
+
| `datasource_test` | 读 | `source` | `success` / `latency` / `version` / `error` |
|
|
106
|
+
| `datasource_tables` | 读 | `source`、`schema?`、`q?` | `tables[]`:表名 / schema / 估计行数 / 列结构(列名 / 推断类型 / 可空 / 注释) |
|
|
107
|
+
| `datasource_import` | 写 | `source`、`table`、`schema?`、`name?`、`limit?` | `datasetId`、行列数、`status: ready \| running`、`jobId?` |
|
|
108
|
+
|
|
109
|
+
要点:
|
|
110
|
+
|
|
111
|
+
- `datasource_*` 的连接密码在**任何**工具描述、返回值、HTTP 响应、日志与错误文本里都不回显,只暴露「是否设置了密码」。
|
|
112
|
+
- `datasource_import` 加入写门禁;`readOnly=true` 或 `datasourceEnabled=false` 时直接拒绝;导入产出的数据集落在**当前会话工作区**,之后一律用 `dataset_*` 工具操作。
|
|
113
|
+
- 驱动(`mysql2` / `pg`)未安装时,`datasource_test` / `datasource_tables` / `datasource_import` 返回可读错误并提示 `pnpm add mysql2`(或 `pg`),不抛裸栈。
|
|
114
|
+
|
|
115
|
+
- `dataset` 一律传 **datasetId 或登记名**,不要猜物理表名。
|
|
116
|
+
- 查询优先用结构化参数;只有需要聚合 / 连接时才传 `sql`,用保留别名 `ds` 指代数据集,例如:
|
|
117
|
+
```sql
|
|
118
|
+
SELECT 状态, COUNT(*) AS c FROM ds GROUP BY 状态
|
|
119
|
+
```
|
|
120
|
+
- 系统列 `_row_id`(自增主键)会随结果返回,是 `update` / `delete` 的定位键;`_uploaded_at` 由系统维护。两者都不能写入。
|
|
121
|
+
- 5 个写工具(`dataset_import` / `_insert` / `_update` / `_delete` / `_drop`)默认触发人工确认;`readOnly=true` 时直接 deny。
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 数据模型
|
|
126
|
+
|
|
127
|
+
### 元数据表 `datasets`(插件自有,每个 scope 库一份)
|
|
128
|
+
|
|
129
|
+
| 列 | 说明 |
|
|
130
|
+
| --- | --- |
|
|
131
|
+
| `id` | `ds_<ts36><rand>`,对外的 datasetId |
|
|
132
|
+
| `scope_key` | 归属键(cwd 规范化路径,或 `ws:<id>`) |
|
|
133
|
+
| `name` | 登记名,`(scope_key, name)` 唯一 |
|
|
134
|
+
| `table_name` | 物理表名,形如 `d_<scopeHash8>_<base40>_<ts36>`,只由插件持有 |
|
|
135
|
+
| `source_path` / `description` / `row_count` / `columns` / `status` / `error` / `created_at` / `updated_at` | 元数据 |
|
|
136
|
+
|
|
137
|
+
`status`:`importing` → `ready` / `failed`。导入失败会删掉半截物理表并标记 `failed`,非 `ready` 数据集会被拒绝读写。
|
|
138
|
+
|
|
139
|
+
来自数据源的数据集会在 `source_id` / `source_ref` 两列留下定位信息(`source_ref` 形如 `schema.table` 或 `table`,脱敏、不含凭据);`source_path` 同时写成 `db:<数据源名>`,因此 `dataset_list` 与设置页搜索零改动即可按数据源名检索。
|
|
140
|
+
|
|
141
|
+
### 数据源登记表 `lh_data_sources`(catalog 库,全局共享)
|
|
142
|
+
|
|
143
|
+
数据源连接配置与数据集**不在同一个库**:数据源存 catalog 库(与 `dataset_scopes` 同级),跨工作区共享;导入产出的数据集仍落在各 scope 业务库。
|
|
144
|
+
|
|
145
|
+
| 列 | 说明 |
|
|
146
|
+
| --- | --- |
|
|
147
|
+
| `id` | `dsrc_<ts36><rand>`,对外的 sourceId |
|
|
148
|
+
| `name` | 登记名,全局唯一 |
|
|
149
|
+
| `type` / `host` / `port` / `database` / `username` | 连接参数 |
|
|
150
|
+
| `password_enc` | AES-256-GCM 密文(`iv:authTag:encrypted`),密钥取自 `datasourceEncryptKey` → `LH_DATA_ENCRYPT_KEY` → 内置默认;**永不回显** |
|
|
151
|
+
| `ssl_mode` / `pool_max` / `description` | 可选连接参数与说明 |
|
|
152
|
+
| `status` | `unknown` / `connected` / `error`,最近一次测试结论 |
|
|
153
|
+
| `last_error` / `last_checked_at` | 最近一次测试的脱敏错误与时间戳 |
|
|
154
|
+
| `created_at` / `updated_at` | 元数据 |
|
|
155
|
+
|
|
156
|
+
### 物理表
|
|
157
|
+
|
|
158
|
+
```sql
|
|
159
|
+
CREATE TABLE <table_name> (
|
|
160
|
+
_row_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
161
|
+
"<业务列>" <REAL | INTEGER | TEXT>, -- 列名保留原始表头(含中文),SQL 中双引号包裹
|
|
162
|
+
_uploaded_at INTEGER DEFAULT (strftime('%s','now'))
|
|
163
|
+
);
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
类型映射:`numeric → REAL`、`boolean → INTEGER`、`date / text → TEXT`。写入时按列类型强制转换(布尔→0/1、数值→Number、日期→ISO、对象/数组→JSON)。
|
|
167
|
+
|
|
168
|
+
### 列名与类型推断
|
|
169
|
+
|
|
170
|
+
- 列名**保留原名**(含中文);空列名回落 `column`;重名追加 `_2` / `_3`(SQL 用 `sanitizedName`)。
|
|
171
|
+
- 类型推断(列名优先级最高,采样默认前 100 行):
|
|
172
|
+
1. 编码 / 号码类列名 → `text`(中文「编码/编号/代码/代号/账号/证件号/邮编/区号/电话/手机」,英文 `code` / `sku` / `ean` / `upc` / `isbn` / `issn` / `postal` / `zip` / `phone` / `tel` / `mobile`);
|
|
173
|
+
2. 13 位以上整数或 `1.78E+12` 类科学计数法 → `text`(防精度丢失);
|
|
174
|
+
3. 数值占比 > 80% → `numeric`;布尔占比 > 90% → `boolean`;日期正则占比 > 70% → `date`;
|
|
175
|
+
4. 其余 → `text`。
|
|
176
|
+
|
|
177
|
+
### 库位置
|
|
178
|
+
|
|
179
|
+
连接优先级:`dbUrl` > `dbPath` > `SQLITE_PATH` > `TURSO_DATABASE_URL` > 默认 `$DSH_HOME/lh-data/data.db`(`DSH_HOME` 未设时回落 `~/.dsh`)。启动 PRAGMA:`journal_mode=WAL`、`foreign_keys=ON`。
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 结果视图与前端分页
|
|
184
|
+
|
|
185
|
+
模型调用 `dataset_query` 时:
|
|
186
|
+
|
|
187
|
+
1. `viewMode=auto` 下,命中行数 > `viewThresholdRows`(默认 20)或片段字节 > `viewThresholdBytes`(默认 4KB)才建视图;`always` / `never` 强制开关。
|
|
188
|
+
2. 建视图后,模型只拿到 `previewRows`(默认 5)行预览 + 全表聚合的 `summary`;`viewId` / `endpoint` 经 **`presentationMeta`** 传给前端(模型不可见)。
|
|
189
|
+
3. 前端卡片(`src/client/index.ts`)接管 `tool.call.toolview` 上 key = `dataset_query` 的渲染位,按 `viewId` 分页拉取:翻页、点表头排序、导出 CSV。
|
|
190
|
+
4. 视图元数据持久化到 `lh_views`(与 `datasets` 同库同 scope),重启后预载恢复;翻页行数据实时查询原表,因此卡片常驻「数据可能已发生变化」提示。
|
|
191
|
+
|
|
192
|
+
### 视图路由(`viewRoutePrefix` 可配,默认 `/api/lh-data`)
|
|
193
|
+
|
|
194
|
+
| 方法 | 路径 | 说明 |
|
|
195
|
+
| --- | --- | --- |
|
|
196
|
+
| `GET` | `{prefix}/views/:viewId` | 视图元信息(列、总行数、分页上限、可排序列) |
|
|
197
|
+
| `GET` | `{prefix}/views/:viewId/rows?page=&pageSize=&sort=&order=` | 取一页数据 |
|
|
198
|
+
| `DELETE` | `{prefix}/views/:viewId` | 释放视图 |
|
|
199
|
+
|
|
200
|
+
接口**不接受任何 SQL**(语句由主机侧 `ViewRegistry` 持有),每个请求先过 `connection.requestRejection()`(Host/Origin 围栏 + 浏览器鉴权)。无 `webServer` / `connection`(CLI / TUI 剖面)时自动降级为纯文本片段。
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 设置页管理接口
|
|
205
|
+
|
|
206
|
+
固定挂在 `/api/lh-data/admin`(不受 `viewRoutePrefix` 影响),`adminEnabled=false` 时不注册。
|
|
207
|
+
|
|
208
|
+
| 方法 | 路径 | 说明 |
|
|
209
|
+
| --- | --- | --- |
|
|
210
|
+
| `GET` | `/scopes` | 已知工作区列表 |
|
|
211
|
+
| `GET` | `/datasets?q=&page=&pageSize=` | 跨工作区聚合列表(超 `adminMaxDatasets` 截断并提示) |
|
|
212
|
+
| `POST` | `/datasets` | 新建空数据集(body 含 `scopeKey` 与列定义) |
|
|
213
|
+
| `GET` | `/datasets/:id?scope=` | 单条详情(含只读列结构) |
|
|
214
|
+
| `GET` | `/datasets/:id/rows?scope=&page=&pageSize=` | 分页查看表数据(只读) |
|
|
215
|
+
| `PATCH` | `/datasets/:id?scope=` | 改名 / 改描述 / 改来源 / 改列说明与样例 |
|
|
216
|
+
| `DELETE` | `/datasets/:id?scope=` | 连同物理表与元数据删除 |
|
|
217
|
+
| `GET` | `/sources` | 数据源列表(脱敏,不含密码) |
|
|
218
|
+
| `POST` | `/sources` | 新建数据源(`test:true` 时先测连,不通不落库) |
|
|
219
|
+
| `POST` | `/sources/test` | 测试连接(body 可含未保存的连接参数) |
|
|
220
|
+
| `GET` | `/sources/:id` | 单条详情(不含密码) |
|
|
221
|
+
| `PATCH` | `/sources/:id` | 改连接参数 / 改密码 / 改名改描述 |
|
|
222
|
+
| `DELETE` | `/sources/:id` | 删除数据源 |
|
|
223
|
+
| `GET` | `/sources/:id/tables?schema=&q=` | 列远端表(含 schema / 估计行数 / 列结构) |
|
|
224
|
+
| `POST` | `/sources/:id/import` | 把远端表导入指定工作区(`body.scopeKey` + 表名) |
|
|
225
|
+
|
|
226
|
+
约束:列名 / 类型 / 可空性对应物理表 DDL,创建后**不可改**;`scope` 由调用方从「已知工作区列表」回传,不接收任意路径。错误响应脱敏,不回显 SQL 与物理表名,数据源接口也绝不回显密码。
|
|
227
|
+
|
|
228
|
+
错误码:`UNAUTHORIZED` / `FORBIDDEN` / `NOT_FOUND` / `METHOD_NOT_ALLOWED` / `BAD_REQUEST` / `PAYLOAD_TOO_LARGE` / `READ_ONLY` / `ADMIN_DISABLED` / `SCOPE_UNKNOWN` / `DUPLICATE_NAME` / `INVALID_COLUMNS` / `QUERY_FAILED` / `SOURCE_DISABLED`(datasourceEnabled=false)/ `DRIVER_MISSING`(驱动未安装)/ `SOURCE_UNREACHABLE`(连接失败)/ `IMPORT_FAILED`(导入异常)。
|
|
229
|
+
|
|
230
|
+
浏览器侧在「设置 → 数据集」(`ADMIN_SECTION_ID = lh-data`,order 100)注册分区,组件见 `src/client/settings/`。
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## 配置
|
|
235
|
+
|
|
236
|
+
在 `cordis.patch.yml` 或 dsh 插件配置中设置(`Config` schema 是默认值唯一真源):
|
|
237
|
+
|
|
238
|
+
### 存储
|
|
239
|
+
|
|
240
|
+
| 键 | 默认 | 说明 |
|
|
241
|
+
| --- | --- | --- |
|
|
242
|
+
| `dbPath` | `''` | libSQL 库路径;空 → `$DSH_HOME/lh-data/data.db` |
|
|
243
|
+
| `dbUrl` | `''` | 非空则覆盖 `dbPath`,可为 `file:` 或 `libsql://`(远程 Turso) |
|
|
244
|
+
| `authToken` | `''` | 远程 token;建议留空走 `TURSO_AUTH_TOKEN` |
|
|
245
|
+
| `perWorkspace` | `false` | scope 用 WorkspaceId 且每个工作区独立库文件 |
|
|
246
|
+
|
|
247
|
+
### 安全
|
|
248
|
+
|
|
249
|
+
| 键 | 默认 | 说明 |
|
|
250
|
+
| --- | --- | --- |
|
|
251
|
+
| `requireApprovalForWrites` | `true` | 写工具走人工确认(无审批通道即拒绝) |
|
|
252
|
+
| `readOnly` | `false` | 只读模式,写工具直接 deny |
|
|
253
|
+
| `allowRawSql` | `true` | 关闭后 `dataset_query` 仅接受结构化参数 |
|
|
254
|
+
| `maxFileBytes` | `209715200` | 单个导入文件字节上限 |
|
|
255
|
+
| `maxInsertRows` | `500` | `dataset_insert` 单次行数上限 |
|
|
256
|
+
| `maxQueryRows` | `200` | `dataset_query` 可服务行数上限 |
|
|
257
|
+
| `batchSize` / `backgroundThresholdRows` | `100` / `20000` | 插入批大小 / 超过该行数自动转后台导入 |
|
|
258
|
+
| `previewSampleRows` | `100` | 类型推断采样行数 |
|
|
259
|
+
|
|
260
|
+
### 结果视图与预览
|
|
261
|
+
|
|
262
|
+
| 键 | 默认 | 说明 |
|
|
263
|
+
| --- | --- | --- |
|
|
264
|
+
| `viewMode` | `auto` | `auto` / `always` / `never` |
|
|
265
|
+
| `viewThresholdRows` / `viewThresholdBytes` | `20` / `4096` | `auto` 模式的建视图阈值 |
|
|
266
|
+
| `previewRows` / `previewStrategy` | `5` / `head` | 预览行数;`head` 或 `head-tail` |
|
|
267
|
+
| `previewCellChars` / `previewColumns` | `40` / `12` | 单元格截断长度 / 展示列数上限 |
|
|
268
|
+
| `summaryEnabled` | `true` | 是否生成列统计摘要 |
|
|
269
|
+
| `summaryMaxColumns` / `summaryMaxTextColumns` | `24` / `3` | 参与摘要的列数 / 取值分布的文本列数上限 |
|
|
270
|
+
| `defaultPageSize` / `maxPageSize` / `maxViewRows` | `100` / `500` / `50000` | 前端首页行数 / 单页上限 / 单视图可翻到的最大行数 |
|
|
271
|
+
| `viewRoutePrefix` | `/api/lh-data` | 前端分页接口路由前缀 |
|
|
272
|
+
|
|
273
|
+
### 设置页
|
|
274
|
+
|
|
275
|
+
| 键 | 默认 | 说明 |
|
|
276
|
+
| --- | --- | --- |
|
|
277
|
+
| `adminEnabled` | `true` | 是否挂载管理接口 |
|
|
278
|
+
| `adminMaxBodyBytes` | `65536` | 请求体字节上限 |
|
|
279
|
+
| `adminMaxDatasets` | `500` | 聚合列表扫描上限 |
|
|
280
|
+
|
|
281
|
+
### 数据源(关系型数据库)
|
|
282
|
+
|
|
283
|
+
| 键 | 默认 | 说明 |
|
|
284
|
+
| --- | --- | --- |
|
|
285
|
+
| `datasourceEnabled` | `true` | 总开关;`false` 时不注册 `datasource_*` 工具与 `/sources` 接口 |
|
|
286
|
+
| `datasourceFetchBatchSize` | `1000` | 远端表分块拉取的行数 |
|
|
287
|
+
| `datasourceConnectTimeoutMs` | `10000` | 连接 / 连通性测试的超时毫秒数 |
|
|
288
|
+
| `datasourceMaxImportRows` | `0` | 单次从远端表导入的行数上限,`0` 表示不限 |
|
|
289
|
+
| `datasourceEncryptKey` | `''` | 数据源密码的加密密钥;留空则回落到环境变量 `LH_DATA_ENCRYPT_KEY`,再空用内置默认并告警(生产不安全) |
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## 作用域与安全
|
|
294
|
+
|
|
295
|
+
- **scope 解析**:默认取 `exec.agent.session.header.cwd`(兼容扁平 `session.cwd`),经 `realpath` 规范化后作为 `scopeKey`;`perWorkspace=true` 时提升为 `ws:<WorkspaceId>`。拿不到会话 cwd 时 **fail-loud 报错**,绝不静默回落 `process.cwd()`。
|
|
296
|
+
- **路径围栏**:导入文件解析后必须落在 scope 目录内,扩展名白名单 `.xlsx` / `.xls` / `.csv`。
|
|
297
|
+
- **SQL 校验**:剥离注释与字符串字面量后检测多语句与写关键字;表引用必须在白名单内;单条 `SELECT` / `WITH` / `EXPLAIN`,超长(> 8000 字符)拒绝。
|
|
298
|
+
- **写门禁**:`tools/pre-execute` 对写工具返回 `ask`;`ctx.tools.guard()` 单调守卫,写工具缺 `dataset`(导入缺 `path`)一律拒绝;`readOnly` 下 deny。
|
|
299
|
+
- **句柄化**:物理表名只出现在 `store.ts` / `view.ts` 内部,工具描述、返回值、HTTP 响应、错误文本都不含。
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## 目录结构
|
|
304
|
+
|
|
305
|
+
```
|
|
306
|
+
src/
|
|
307
|
+
index.ts 插件入口:name / inject / Config / apply,门禁与生命周期
|
|
308
|
+
tooling.ts 工具定义适配器(零 dsh 运行时依赖):toolDef、参数校验、ToolError
|
|
309
|
+
db.ts libSQL 客户端与生命周期、PRAGMA、JSON 规整、库 URL 解析
|
|
310
|
+
store.ts datasets 元数据层、物理表名生成、归属断言
|
|
311
|
+
table.ts 物理表建/插/改/删,按列类型转换
|
|
312
|
+
sql.ts 只读校验器、结构化查询拼装、标识符引号化
|
|
313
|
+
parse.ts XLSX / CSV 解析、列名消毒与类型推断
|
|
314
|
+
preview.ts 模型可见片段(预览行 + 全表列统计)
|
|
315
|
+
render.ts 纯函数文本渲染(列表 / 列 / 行 / 查询预览)
|
|
316
|
+
view.ts 结果视图注册中心(持久化到 lh_views)
|
|
317
|
+
scope.ts scope 解析与路径围栏
|
|
318
|
+
scope-registry.ts scope 注册表
|
|
319
|
+
http.ts 视图分页路由
|
|
320
|
+
http-common.ts 鉴权、请求体、响应公共逻辑
|
|
321
|
+
admin*.ts 设置页服务层 / 路由 / 校验 / 双半身契约
|
|
322
|
+
tools/ registry(12 工具聚合)、import、read、write、datasource
|
|
323
|
+
datasource/ 数据源模块:types / crypto / columns / driver / connector(mysql|pg)/ connection / source-sql / source-store / importer
|
|
324
|
+
client/ 浏览器半身:查询结果卡片 + 设置页分区(DatasetsPanel / DataSourcesPanel / DataSourceForm / SourceTablesPanel)
|
|
325
|
+
docs/ 设计文档
|
|
326
|
+
examples/ 四个自包含验证脚本(构造最小假 ctx 跑通全链路)
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## 设计文档
|
|
330
|
+
|
|
331
|
+
- `docs/excel-to-turso-skill设计.md` —— 数据层、工具集、作用域与安全设计
|
|
332
|
+
- `docs/查询结果视图与前端分页设计.md` —— 结果视图、分页协议与前端卡片
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 设置页管理接口的**双半身契约**。
|
|
3
|
+
*
|
|
4
|
+
* 主机半身(`admin.ts` / `admin-http.ts`)与浏览器半身(`client/settings/*`)
|
|
5
|
+
* 共用本文件,因此有两条硬约束:
|
|
6
|
+
*
|
|
7
|
+
* 1. **禁止 import 任何 node 内置模块** —— 本文件会被打进浏览器 bundle
|
|
8
|
+
* (`tsdown.config.ts` 的 client 产物);
|
|
9
|
+
* 2. **只有常量与类型**,不含任何运行时逻辑 —— 浏览器 bundle 是 CJS 工厂包,
|
|
10
|
+
* 多引一个模块就多一份内联体积。
|
|
11
|
+
*
|
|
12
|
+
* 存在理由:浏览器拿不到主机的 `Config`。`viewRoutePrefix` 之类是用户在 dsh
|
|
13
|
+
* 配置里改的,如果路由前缀两边各写一份字面量,改配置就会让设置页悄悄失联。
|
|
14
|
+
* 下面的常量是唯一真源,主机注册路由和浏览器拼 URL 都从这里取。
|
|
15
|
+
*/
|
|
16
|
+
/** 管理接口的路由前缀。与视图接口(可配置)解耦,固定不变。 */
|
|
17
|
+
export declare const ADMIN_ROUTE_PREFIX = "/api/lh-data";
|
|
18
|
+
/** 管理接口的固定挂载点:`${ADMIN_ROUTE_PREFIX}/admin`。 */
|
|
19
|
+
export declare const ADMIN_API_BASE: string;
|
|
20
|
+
/**
|
|
21
|
+
* `settings.section` 的注册 id。既有分区用的是 0(general)/ 10(models)/
|
|
22
|
+
* 15(plugins)/ 20(agent-presets),这里取 100 排在最后。
|
|
23
|
+
*/
|
|
24
|
+
export declare const ADMIN_SECTION_ID = "lh-data";
|
|
25
|
+
export declare const ADMIN_SECTION_ORDER = 100;
|
|
26
|
+
export declare const ADMIN_SECTION_LABEL = "\u6570\u636E\u96C6";
|
|
27
|
+
export declare const ADMIN_PAGE_SIZE = 20;
|
|
28
|
+
export declare const ADMIN_MAX_PAGE_SIZE = 100;
|
|
29
|
+
/** 表数据默认每页行数。 */
|
|
30
|
+
export declare const ADMIN_ROW_PAGE_SIZE = 20;
|
|
31
|
+
/** 表数据每页行数的上限。 */
|
|
32
|
+
export declare const ADMIN_ROW_MAX_PAGE_SIZE = 100;
|
|
33
|
+
/** 行号列:物理表的自增主键,只用于稳定排序与前端行键,不算业务列。 */
|
|
34
|
+
export declare const ADMIN_ROW_ID_COLUMN = "_row_id";
|
|
35
|
+
export declare const ADMIN_MAX_NAME_LENGTH = 80;
|
|
36
|
+
export declare const ADMIN_MAX_DESCRIPTION_LENGTH = 500;
|
|
37
|
+
export declare const ADMIN_MAX_SOURCE_LENGTH = 1024;
|
|
38
|
+
export declare const ADMIN_MAX_COLUMNS = 64;
|
|
39
|
+
export declare const ADMIN_MAX_QUERY_LENGTH = 80;
|
|
40
|
+
/** 单列样例值的个数上限(前端拼接与后端夹紧共用)。 */
|
|
41
|
+
export declare const ADMIN_MAX_SAMPLE_VALUES = 5;
|
|
42
|
+
/** 单个样例值的字符上限。 */
|
|
43
|
+
export declare const ADMIN_MAX_SAMPLE_LENGTH = 40;
|
|
44
|
+
export declare const ADMIN_MAX_SOURCE_NAME_LENGTH = 80;
|
|
45
|
+
export declare const ADMIN_MAX_HOST_LENGTH = 255;
|
|
46
|
+
export declare const ADMIN_MAX_DATABASE_LENGTH = 128;
|
|
47
|
+
export declare const ADMIN_MAX_USERNAME_LENGTH = 128;
|
|
48
|
+
export declare const ADMIN_MAX_PASSWORD_LENGTH = 256;
|
|
49
|
+
export declare const ADMIN_MIN_PORT = 1;
|
|
50
|
+
export declare const ADMIN_MAX_PORT = 65535;
|
|
51
|
+
export declare const ADMIN_MAX_POOL_MAX = 100;
|
|
52
|
+
/** 数据源类型下拉的可选项(顺序即展示顺序)。 */
|
|
53
|
+
export declare const ADMIN_SOURCE_TYPES: readonly string[];
|
|
54
|
+
export declare const ADMIN_SOURCE_TYPE_LABELS: Readonly<Record<string, string>>;
|
|
55
|
+
/** 各类型的默认端口(前端带出与后端兜底共用)。 */
|
|
56
|
+
export declare const ADMIN_SOURCE_DEFAULT_PORTS: Readonly<Record<string, number>>;
|
|
57
|
+
/** 与 `parse.ts` 的 `ColumnType` 同构(本文件不能 import 主机侧的 parse)。 */
|
|
58
|
+
export type AdminColumnType = 'text' | 'numeric' | 'boolean' | 'date';
|
|
59
|
+
/** 人类可读的类型名:前端下拉与列结构表共用,避免两处各写一份中文。 */
|
|
60
|
+
export declare const ADMIN_COLUMN_TYPE_LABELS: Readonly<Record<AdminColumnType, string>>;
|
|
61
|
+
/** 列类型下拉的可选项(顺序即展示顺序)。 */
|
|
62
|
+
export declare const ADMIN_COLUMN_TYPES: readonly AdminColumnType[];
|
|
63
|
+
export type DatasetStatus = 'importing' | 'ready' | 'failed';
|
|
64
|
+
/** 设置页可见的一列。列名是业务表头(可含中文),不含任何物理表信息。 */
|
|
65
|
+
export interface DatasetColumnView {
|
|
66
|
+
/** 原始表头。 */
|
|
67
|
+
name: string;
|
|
68
|
+
/** SQL 中使用的列名(重名时追加 `_2`)。 */
|
|
69
|
+
sanitizedName: string;
|
|
70
|
+
type: AdminColumnType;
|
|
71
|
+
nullable: boolean;
|
|
72
|
+
description: string;
|
|
73
|
+
sample: unknown[];
|
|
74
|
+
}
|
|
75
|
+
/** 新建数据集时提交的一列。 */
|
|
76
|
+
export interface DatasetColumnSpec {
|
|
77
|
+
name: string;
|
|
78
|
+
type: AdminColumnType;
|
|
79
|
+
nullable?: boolean;
|
|
80
|
+
description?: string;
|
|
81
|
+
}
|
|
82
|
+
/** 列表里的一条数据集(跨工作区聚合;不含列结构,避免列表响应膨胀)。 */
|
|
83
|
+
export interface DatasetAdminView {
|
|
84
|
+
id: string;
|
|
85
|
+
scopeKey: string;
|
|
86
|
+
name: string;
|
|
87
|
+
description: string | null;
|
|
88
|
+
sourcePath: string | null;
|
|
89
|
+
rowCount: number;
|
|
90
|
+
columnCount: number;
|
|
91
|
+
status: DatasetStatus;
|
|
92
|
+
error: string | null;
|
|
93
|
+
createdAt: number;
|
|
94
|
+
updatedAt: number;
|
|
95
|
+
}
|
|
96
|
+
/** 详情 = 列表项 + 只读列结构。 */
|
|
97
|
+
export interface DatasetDetailView extends DatasetAdminView {
|
|
98
|
+
columns: DatasetColumnView[];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 一页表数据(只读)。`columns` 只含业务列,`rows` 每行额外带 `_row_id`
|
|
102
|
+
* (`ADMIN_ROW_ID_COLUMN`)作为稳定行键;`page` 由服务端夹到分页边界内。
|
|
103
|
+
*/
|
|
104
|
+
export interface DatasetRowsResult {
|
|
105
|
+
columns: string[];
|
|
106
|
+
rows: Record<string, unknown>[];
|
|
107
|
+
page: number;
|
|
108
|
+
pageSize: number;
|
|
109
|
+
/** `COUNT(*)` 得到的真实行数(元数据里的 rowCount 可能与之不符)。 */
|
|
110
|
+
total: number;
|
|
111
|
+
totalPages: number;
|
|
112
|
+
}
|
|
113
|
+
/** 设置页可见的一个工作区。 */
|
|
114
|
+
export interface ScopeView {
|
|
115
|
+
scopeKey: string;
|
|
116
|
+
lastSeen: number;
|
|
117
|
+
}
|
|
118
|
+
/** 单个工作区读取失败时的降级提示(不阻断整页)。 */
|
|
119
|
+
export interface AdminWarning {
|
|
120
|
+
scopeKey: string;
|
|
121
|
+
message: string;
|
|
122
|
+
}
|
|
123
|
+
export interface ListDatasetsResult {
|
|
124
|
+
items: DatasetAdminView[];
|
|
125
|
+
total: number;
|
|
126
|
+
page: number;
|
|
127
|
+
pageSize: number;
|
|
128
|
+
/** 命中 `adminMaxDatasets` 上限被截断。 */
|
|
129
|
+
truncated: boolean;
|
|
130
|
+
warnings: AdminWarning[];
|
|
131
|
+
}
|
|
132
|
+
/** 新建空数据集:列名 / 类型 / 可空性提交后冻结,说明与样例仍可改。 */
|
|
133
|
+
export interface CreateDatasetRequest {
|
|
134
|
+
scopeKey: string;
|
|
135
|
+
name: string;
|
|
136
|
+
description?: string;
|
|
137
|
+
sourcePath?: string | null;
|
|
138
|
+
columns: DatasetColumnSpec[];
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* 列定义的补丁:只改「说明」与「样例」这两项纯元数据。
|
|
142
|
+
* 列名 / 类型 / 可空性对应物理表 DDL,设置页不可改。
|
|
143
|
+
* 用 `sanitizedName` 定位列——原始表头 `name` 在重名列上会重复,不能当键。
|
|
144
|
+
*/
|
|
145
|
+
export interface DatasetColumnPatch {
|
|
146
|
+
/** 列的 SQL 名(去重后唯一)。 */
|
|
147
|
+
sanitizedName: string;
|
|
148
|
+
description?: string;
|
|
149
|
+
sample?: unknown[];
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* 设置页可见的一条数据源。
|
|
153
|
+
* **密码永不回显**:只给 `hasPassword`,前端据此显示「已设置 · 不可查看」。
|
|
154
|
+
*/
|
|
155
|
+
export interface DataSourceView {
|
|
156
|
+
id: string;
|
|
157
|
+
name: string;
|
|
158
|
+
type: string;
|
|
159
|
+
host: string;
|
|
160
|
+
port: number;
|
|
161
|
+
database: string;
|
|
162
|
+
username: string;
|
|
163
|
+
hasPassword: boolean;
|
|
164
|
+
sslMode: string | null;
|
|
165
|
+
poolMax: number | null;
|
|
166
|
+
description: string | null;
|
|
167
|
+
status: string;
|
|
168
|
+
lastError: string | null;
|
|
169
|
+
lastCheckedAt: number | null;
|
|
170
|
+
createdAt: number;
|
|
171
|
+
updatedAt: number;
|
|
172
|
+
}
|
|
173
|
+
export interface ConnectionTestView {
|
|
174
|
+
success: boolean;
|
|
175
|
+
latency: number;
|
|
176
|
+
version: string | null;
|
|
177
|
+
error: string | null;
|
|
178
|
+
}
|
|
179
|
+
/** 新建数据源。`test` 为 true 时先测连,不通就不落库。 */
|
|
180
|
+
export interface CreateDataSourceRequest {
|
|
181
|
+
name: string;
|
|
182
|
+
type: string;
|
|
183
|
+
host: string;
|
|
184
|
+
port?: number | null;
|
|
185
|
+
database: string;
|
|
186
|
+
username: string;
|
|
187
|
+
password?: string;
|
|
188
|
+
sslMode?: string | null;
|
|
189
|
+
poolMax?: number | null;
|
|
190
|
+
description?: string | null;
|
|
191
|
+
test?: boolean;
|
|
192
|
+
}
|
|
193
|
+
/** 改数据源:字段缺省表示不改;`password` 给了字符串就替换(空串表示置空)。 */
|
|
194
|
+
export interface PatchDataSourceRequest {
|
|
195
|
+
name?: string;
|
|
196
|
+
type?: string;
|
|
197
|
+
host?: string;
|
|
198
|
+
port?: number | null;
|
|
199
|
+
database?: string;
|
|
200
|
+
username?: string;
|
|
201
|
+
password?: string;
|
|
202
|
+
sslMode?: string | null;
|
|
203
|
+
poolMax?: number | null;
|
|
204
|
+
description?: string | null;
|
|
205
|
+
test?: boolean;
|
|
206
|
+
}
|
|
207
|
+
/** `POST /sources/test` 的请求体:给 `source` 就测已登记的,给全参数就测未保存的草稿。 */
|
|
208
|
+
export interface TestDataSourceRequest {
|
|
209
|
+
source?: string;
|
|
210
|
+
type?: string;
|
|
211
|
+
host?: string;
|
|
212
|
+
port?: number | null;
|
|
213
|
+
database?: string;
|
|
214
|
+
username?: string;
|
|
215
|
+
password?: string;
|
|
216
|
+
sslMode?: string | null;
|
|
217
|
+
poolMax?: number | null;
|
|
218
|
+
}
|
|
219
|
+
export interface SourceTableView {
|
|
220
|
+
tableName: string;
|
|
221
|
+
schemaName: string | null;
|
|
222
|
+
/** 远端估计行数;-1 表示未知。 */
|
|
223
|
+
rowCount: number;
|
|
224
|
+
primaryKey: string | null;
|
|
225
|
+
columns: {
|
|
226
|
+
name: string;
|
|
227
|
+
type: AdminColumnType;
|
|
228
|
+
nullable: boolean;
|
|
229
|
+
description: string | null;
|
|
230
|
+
}[];
|
|
231
|
+
}
|
|
232
|
+
export interface ListSourceTablesResult {
|
|
233
|
+
/** 实际使用的 schema(MySQL 为库名,PostgreSQL 默认 public)。 */
|
|
234
|
+
schema: string | null;
|
|
235
|
+
/** 可选 schema 列表(PostgreSQL 多个,MySQL 一个)。 */
|
|
236
|
+
schemas: string[];
|
|
237
|
+
tables: SourceTableView[];
|
|
238
|
+
}
|
|
239
|
+
/** `POST /sources/:id/import`。 */
|
|
240
|
+
export interface ImportSourceTableRequest {
|
|
241
|
+
scopeKey: string;
|
|
242
|
+
tableName: string;
|
|
243
|
+
schemaName?: string | null;
|
|
244
|
+
name?: string | null;
|
|
245
|
+
limit?: number | null;
|
|
246
|
+
}
|
|
247
|
+
export interface ImportSourceTableResult {
|
|
248
|
+
datasetId: string;
|
|
249
|
+
name: string;
|
|
250
|
+
rowCount: number;
|
|
251
|
+
columnCount: number;
|
|
252
|
+
status: 'ready' | 'running';
|
|
253
|
+
jobId?: string;
|
|
254
|
+
}
|
|
255
|
+
/** 字段缺省表示不改动。 */
|
|
256
|
+
export interface PatchDatasetRequest {
|
|
257
|
+
name?: string;
|
|
258
|
+
description?: string | null;
|
|
259
|
+
sourcePath?: string | null;
|
|
260
|
+
columns?: DatasetColumnPatch[];
|
|
261
|
+
}
|
|
262
|
+
export type AdminErrorCode = 'UNAUTHORIZED' | 'FORBIDDEN' | 'NOT_FOUND' | 'METHOD_NOT_ALLOWED' | 'BAD_REQUEST' | 'PAYLOAD_TOO_LARGE' | 'READ_ONLY' | 'ADMIN_DISABLED' | 'SCOPE_UNKNOWN' | 'DUPLICATE_NAME' | 'INVALID_COLUMNS' | 'QUERY_FAILED' | 'SOURCE_DISABLED' | 'DRIVER_MISSING' | 'SOURCE_UNREACHABLE' | 'IMPORT_FAILED';
|
|
263
|
+
/** 错误响应体。主机侧统一脱敏:不回显 SQL 与物理表名。 */
|
|
264
|
+
export interface AdminErrorBody {
|
|
265
|
+
error: {
|
|
266
|
+
code: AdminErrorCode;
|
|
267
|
+
message: string;
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
/** 管理接口是否可写(前端据此置灰按钮)。 */
|
|
271
|
+
export interface AdminCapabilities {
|
|
272
|
+
writable: boolean;
|
|
273
|
+
reason: string;
|
|
274
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 设置页管理接口的 HTTP 路由 —— 与视图路由同源(自建前缀 + `connection` 鉴权),
|
|
3
|
+
* 但不接受任何 SQL:所有读写都走 `admin.ts` 的服务函数,物理表名与 SQL 不出现在接口里。
|
|
4
|
+
*
|
|
5
|
+
* 路由表(固定挂在 `ADMIN_API_BASE = /api/lh-data/admin`):
|
|
6
|
+
* GET /scopes 列出已知工作区
|
|
7
|
+
* GET /datasets 聚合列表(?q=&page=&pageSize=)
|
|
8
|
+
* POST /datasets 新建空数据集(body 含 scopeKey 与列定义)
|
|
9
|
+
* GET /datasets/:id?scope= 单条详情
|
|
10
|
+
* GET /datasets/:id/rows?scope=&page=&pageSize= 分页查看表数据(只读)
|
|
11
|
+
* PATCH /datasets/:id?scope= 改名 / 改描述 / 改来源
|
|
12
|
+
* DELETE /datasets/:id?scope= 连同物理表与元数据删除
|
|
13
|
+
*
|
|
14
|
+
* 数据源(datasourceEnabled=false 时整组不注册):
|
|
15
|
+
* GET /sources 数据源列表(脱敏,不含密码)
|
|
16
|
+
* POST /sources 新建数据源(body.test=true 时先测连)
|
|
17
|
+
* POST /sources/test 测试连接(已登记的 source,或未保存的完整参数)
|
|
18
|
+
* GET /sources/:id 单条详情
|
|
19
|
+
* PATCH /sources/:id 改连接参数 / 密码 / 名称 / 描述
|
|
20
|
+
* DELETE /sources/:id 删除数据源
|
|
21
|
+
* GET /sources/:id/tables?schema=&q= 浏览远端表
|
|
22
|
+
* POST /sources/:id/import 把远端表导入指定工作区
|
|
23
|
+
*
|
|
24
|
+
* scope 一律由写操作的调用方从「已知工作区列表」里回传,不接收任意路径输入。
|
|
25
|
+
*/
|
|
26
|
+
import type { DataServices } from './store';
|
|
27
|
+
/**
|
|
28
|
+
* 注册管理路由。宿主缺失(`webServer` / `connection`)时返回 undefined,由调用方降级。
|
|
29
|
+
* 路由路径重复会抛错(webserver 契约),与视图路由路径不同前缀,互不影响。
|
|
30
|
+
*/
|
|
31
|
+
export declare function registerAdminRoutes(services: DataServices): (() => void) | undefined;
|