数据库管理组件 — 实现文档

数据库管理组件 — 实现文档

一、整体架构

┌──────────────────────────────────────────────────────────────┐
│                    用户界面(Vue 3)                           │
│  DatabaseManager.vue                                         │
│  ┌────────────┐  ┌────────────┐                             │
│  │ 远程数据库  │  │ 本地数据库  │ ← 仅桌面端显示              │
│  └─────┬──────┘  └─────┬──────┘                             │
│        │               │                                     │
│   浏览器: fetch     桌面端: window.lragent.*                 │
│        │               │                                     │
├────────┼───────────────┼─────────────────────────────────────┤
│        ▼               ▼                                     │
│  ┌──────────┐   ┌──────────────┐                            │
│  │ 服务端API │   │ Electron IPC │                            │
│  │ /api/    │   │ ipcMain      │                            │
│  │ platform │   │ .handle()    │                            │
│  └────┬─────┘   └──────┬───────┘                            │
│       │                │                                     │
│       ▼                ▼                                     │
│  server/data/*.db    ~/AppData/UserData/databases/*.db       │
│  (远端服务器)         (桌面端本地存储)                         │
└──────────────────────────────────────────────────────────────┘

核心思路:同一个 Vue 组件根据运行环境(浏览器 vs Electron 桌面端)自动选择不同的数据通道:

  • 浏览器:直接调服务端 REST API
  • 桌面端:通过 window.lragent.*(preload 桥接 → ipcMain handler)访问本地文件系统 + 远程 HTTP

二、三层实现详解

2.1 服务端(Node.js)— 远程数据源

文件server/routes/platform-asset.jshandleDatabaseManagerRoutes()

数据目录server/data/(由 DATA_DIR = path.join(__dirname, '..', 'data') 定义)

三个 API 端点

| 端点 | 方法 | 功能 | 返回 |

|------|------|------|------|

| /api/platform/databases | GET | 列出所有 .db 文件 | { success, databases: [...], total } |

| /api/platform/databases/:name/download | GET | 下载 .db 文件 | application/octet-stream 流 |

| /api/platform/databases/:name/tables | GET | 查询表结构 | { success, tables: [{ name, sql, rowCount }] } |

列表 API 工作原理

  1. 递归扫描 server/data/ 目录及子目录
  2. 找到所有 .db 后缀文件
  3. 读取文件大小、修改时间
  4. DB_DESCRIPTIONS 字典匹配中文说明
  5. 按文件大小降序排列返回

下载 API 工作原理

  1. 对文件名做安全过滤(去除 ../\,防路径穿越)
  2. 拼接 DATA_DIR + safeName 得到绝对路径
  3. 校验文件存在性
  4. 设置 Content-Type: application/octet-stream + Content-Disposition: attachment
  5. fs.createReadStream().pipe(res) 流式传输

表结构 API 工作原理

  1. 同样做路径安全过滤
  2. better-sqlite3(或 sqlite-compat)以只读模式打开
  3. 查询 sqlite_master 获取所有用户表
  4. 对每个表执行 SELECT COUNT(*) 获取行数
  5. 关闭数据库连接后返回

关键安全措施

const safeName = dbName.replace(/\.\./g, '').replace(/[\/\\]/g, '');

防止路径穿越攻击。下载和表结构两个端点都做了此过滤。


2.2 桌面端(Electron)— 本地管理 + 远程同步

文件electron-app/main.jsregisterIpcHandlers()

本地存储目录~/AppData/Roaming/lragent/databases/(由 app.getPath('userData') + '/databases' 定义)

6 个 IPC Handler

| 通道 | 参数 | 功能 | 实现方式 |

|------|------|------|----------|

| db-list-local | 无 | 列出本地已下载的 .db | fs.readdirSync 扫描 dbStoreDir |

| db-list-remote | serverUrl? | 列出远程数据库 | HTTP GET → 先试 ylxt.chat,失败回退 ylxt.chat |

| db-download | dbName, serverUrl? | 下载远程 .db 到本地 | HTTP GET → fs.createWriteStream,带进度通知 |

| db-delete-local | dbName | 删除本地 .db | fs.unlinkSync |

| db-query | dbName, sql, params? | SQL 查询本地 .db | better-sqlite3 readonly 模式 |

| db-tables | dbName | 查看本地 .db 表结构 | better-sqlite3 + sqlite_master |

额外功能(preload 已暴露,组件暂未使用):

| 通道 | 功能 |

|------|------|

| db-export | 弹出保存对话框,复制本地 .db 到用户指定位置 |

| db-import | 弹出文件选择对话框,复制外部 .db 到本地存储 |

| db-store-path | 返回本地存储目录路径 |

下载进度通知

mainWindow?.webContents.send('db-download-progress', {
  dbName, status: 'downloading', received, total, percent
})

前端可通过 window.lragent.onDbDownloadProgress(cb) 监听。

远程连接的 fallback 逻辑

db-list-remote / db-download:
  1. 先尝试 https://ylxt.chat/api/platform/databases
  2. 失败则回退 https://ylxt.chat/api/platform/databases
  3. 都失败返回合并错误信息

2.3 前端(Vue 3)— 统一交互界面

文件src/views/DatabaseManager.vue

环境检测

const isDesktop = !!(window.lragent && window.lragent.dbListRemote)

桌面端 preload.js 会注入 window.lragent 对象,浏览器环境不存在。

双 Tab 设计

  • 远程数据库 tab:浏览器和桌面端都显示
  • 本地数据库 tab:仅桌面端显示(v-if="isDesktop"

操作矩阵

| 操作 | 浏览器·远程 | 桌面端·远程 | 桌面端·本地 |

|------|:-----------:|:-----------:|:-----------:|

| 浏览列表 | fetch API | IPC → HTTP | IPC → 本地 fs |

| 查看表结构 | fetch API | fetch API | IPC → better-sqlite3 |

| 下载 | 标签 | IPC → HTTP → 本地文件 | — |

| SQL 查询 | — | — | IPC → better-sqlite3 |

| 删除 | — | — | IPC → fs.unlink |

| 导入 | — | — | IPC → dialog (preload已暴露) |

| 导出 | — | — | IPC → dialog (preload已暴露) |


三、远端服务器需要做什么

3.1 最简方案:只放文件到指定目录

是的,放到指定目录就行。 服务端 API 会自动扫描。

步骤:

  1. .db 文件放到 server/data/ 目录
  2. 如果有子目录分类(如 kb/kb-snapshots/),API 会递归扫描
  3. 文件名后缀必须是 .db
  4. 重启服务器即可生效(无缓存,实时扫描)

当前 server/data/ 已有的数据库(实测 27+ 个):

server/data/
├── bossagents.db          (6.3 MB)  主业务数据库
├── rule_engine.db         (353 MB)  规则引擎
├── boss_analytics.db      (62 MB)   审计日志
├── digital-staff.db       (1.5 MB)  数字员工
├── core_runtime.db        (26 MB)   运行时主库
├── sciot-metadata.db      (8 MB)    SCSAI元数据
├── aml.db                 (4 MB)    AML规则
├── inspection.db          (2 MB)    巡检记录
├── sccapp_process_data.db (600 MB)  工艺规程
├── sciot_import.db        (65 MB)   SCSAI导入
├── kb/
│   └── phos_chem.db       (247 MB)  磷化工知识库
├── kb-snapshots/
│   └── phos_chem_pre-convert_*.db  知识库快照
└── ... (共 27+ 个)

3.2 添加数据库说明

server/routes/platform-asset.jsDB_DESCRIPTIONS 字典中添加条目:

const DB_DESCRIPTIONS = {
  'rule_engine.db': '规则引擎(巡检规则、模板、提示词)',
  'bossagents.db': '主业务数据库',
  // 添加新数据库的说明:
  'your_new_db.db': '你的数据库说明',
};

不添加说明也能工作,说明列会显示 -

3.3 官网部署(ylxt.chat)需要做的事

官网需要运行 LRAgent 的 Node.js 服务端,并确保:

必须项

  1. 部署 server.jsnode server.js 监听端口(默认 3006)
  2. 放置 .db 文件:在 server/data/ 目录下放入要共享的数据库文件
  3. HTTPS:Electron 桌面端默认先连 https://ylxt.chat,需要 SSL 证书
  4. CORS(如果浏览器跨域访问):服务端已内置 CORS 中间件,无需额外配置
  5. 反向代理:如果用 Nginx,需要代理 /api/platform/databases* 路径

Nginx 配置示例

server {
    listen 443 ssl;
    server_name ylxt.chat;

    ssl_certificate     /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    # 数据库管理 API
    location /api/platform/databases {
        proxy_pass https://ylxt.chat;
        proxy_set_header Host $host;
        proxy_read_timeout 300s;  # 大文件下载需要长超时
        proxy_buffering off;       # 流式下载
        client_max_body_size 0;    # 不限制请求体
    }

    # 其他 API
    location /api/ {
        proxy_pass https://ylxt.chat;
        proxy_set_header Host $host;
    }

    # 前端静态文件
    location / {
        root /path/to/lragent/dist;
        try_files $uri $uri/ /index.html;
    }
}

大文件下载注意事项

3.4 不部署服务端的替代方案

如果官网不想运行 Node.js 服务端,可以用纯静态文件方式:

方案 A:Nginx 直接托管 .db 文件

location /databases/ {
    alias /var/www/databases/;
    autoindex on;           # 开启目录列表
    autoindex_format json;  # JSON 格式目录列表
}

此方案需要修改桌面端 db-list-remote 的解析逻辑,因为返回格式不同。

方案 B:预生成数据库清单 JSON

  1. 写脚本定期扫描目录,生成 databases.json
  2. 放到 https://ylxt.chat/databases.json
  3. 下载链接指向 https://ylxt.chat/databases/xxx.db
  4. 修改 db-list-remote handler 解析此 JSON

方案 C:对象存储(OSS/S3)

  1. 将 .db 文件上传到 OSS bucket
  2. 生成一个 databases.json 索引文件
  3. 下载链接指向 OSS 的签名 URL
  4. 适合 CDN 加速大文件下载

四、数据流详解

4.1 远程列表加载(浏览器)

用户打开页面
  → onMounted() → loadRemoteDatabases()
  → get('/api/platform/databases')        ← src/utils/api.js (fetch)
  → Node.js server.js 路由匹配
  → handleDatabaseManagerRoutes()
  → scanDir('server/data/')               ← 递归扫描
  → 返回 JSON { databases: [...] }
  → Vue 组件渲染表格

4.2 远程列表加载(桌面端)

用户打开页面
  → onMounted() → loadRemoteDatabases()
  → window.lragent.dbListRemote()         ← preload.js 桥接
  → ipcMain.handle('db-list-remote')
  → httpGet('https://ylxt.chat/api/platform/databases')
  → 失败? → httpGet('https://ylxt.chat/api/platform/databases')
  → JSON.parse(response)
  → 返回给渲染进程
  → Vue 组件渲染表格

4.3 下载到本地(桌面端)

用户点击"下载"
  → downloadDb(db)
  → window.lragent.dbDownload(db.name)    ← preload.js 桥接
  → ipcMain.handle('db-download')
  → downloadFile('https://ylxt.chat/api/platform/databases/xxx.db/download', destPath)
  → HTTP 流式下载 → fs.createWriteStream
  → 进度通知: webContents.send('db-download-progress', {...})
  → 下载完成 → 返回 { success: true, path, size }
  → Vue 组件显示成功消息

4.4 本地 SQL 查询(桌面端)

用户点击"查询" → 输入 SQL → 点击"执行"
  → runQuery()
  → window.lragent.dbQuery(dbName, sql)   ← preload.js 桥接
  → ipcMain.handle('db-query')
  → require('better-sqlite3')
  → new Database(dbPath, { readonly: true })
  → db.prepare(sql).all(...params)
  → db.close()
  → 返回 { success: true, rows: [...] }
  → Vue 组件渲染结果表格

五、安全设计

| 层级 | 措施 |

|------|------|

| 服务端路径 | dbName.replace(/\.\./g, '').replace(/[\/\\]/g, '') 防路径穿越 |

| 服务端下载 | 文件不存在返回 404,异常返回 500 |

| 服务端表查询 | better-sqlite3readonly: true 打开,查询完立即关闭 |

| 桌面端查询 | readonly: true 模式,不允许写操作 |

| 桌面端删除 | 前端 confirm() 二次确认 |

| Electron | contextIsolation: true + nodeIntegration: false,preload 白名单暴露 |


六、扩展点

6.1 已实现但前端未接入的功能

preload APIIPC 通道功能接入建议
dbExport(dbName)db-export导出本地 .db 到用户指定路径本地 tab 加"导出"按钮
dbImport()db-import从外部导入 .db 到本地存储本地 tab 加"导入"按钮
dbStorePath()db-store-path获取本地存储路径工具栏显示
onDbDownloadProgress(cb)db-download-progress下载进度监听下载按钮旁显示进度条

6.2 未来可扩展


七、快速部署清单

官网服务器(ylxt.chat)

桌面端用户

浏览器用户

← 返回案例列表
分享:
🤖 Try Now →
🤖
🎁