487 lines
17 KiB
Markdown
487 lines
17 KiB
Markdown
# 采集器设置与连接验证
|
||
|
||
## 背景
|
||
|
||
控制台现在把“数据源目录”和“采集器配置”拆开:
|
||
|
||
- `/datasources`
|
||
- 展示所有数据源,包括内置和自定义。
|
||
- 点击名称只打开信息抽屉。
|
||
- 负责查看状态、触发采集和查看采集中任务。
|
||
- `/collection-management?tab=collector_credentials`
|
||
- 显示为“采集器”。
|
||
- 负责 endpoint、请求头、基础参数和凭证配置。
|
||
- 所有采集器都提供连接按钮,用于健康检查。
|
||
|
||
这样做是为了减少首次使用时的认知分裂:接口地址、请求头、凭证和自定义源配置都属于“采集器”,而不是散落在数据源列表和系统设置多个入口里。
|
||
|
||
## 用户侧规则
|
||
|
||
连接状态不是前端样式状态,而是由后端根据配置 checksum 和已验证记录判断。
|
||
|
||
一个内置采集器被视为“已连接”需要满足任一条件:
|
||
|
||
- 当前配置已经成功采集过数据。
|
||
- 当前配置点击过连接按钮,并且后端验证成功。
|
||
|
||
如果 endpoint、请求头、基础配置或凭证指纹相对上次验证成功时发生变化,状态会回到“需要重新连接”。
|
||
|
||
## 前端入口
|
||
|
||
### 数据源目录
|
||
|
||
文件:
|
||
|
||
- [PlainResourcePages.tsx](/home/ray/dev/linkong/planet/frontend/src/admin-next/pages/PlainResourcePages.tsx)
|
||
- [AdminNextRoutes.tsx](/home/ray/dev/linkong/planet/frontend/src/admin-next/AdminNextRoutes.tsx)
|
||
|
||
当前行为:
|
||
|
||
- 内置数据源和自定义数据源合并为 `UnifiedDataSource` 列表。
|
||
- 表格只保留查看、采集和状态类操作。
|
||
- 名称点击打开只读抽屉。
|
||
- 抽屉中展示:
|
||
- 是否内置
|
||
- 是否启用
|
||
- 模块、优先级、频率
|
||
- endpoint
|
||
- 请求头
|
||
- 基础配置
|
||
- 是否需要凭证
|
||
- 有任务运行时,顶部进度区显示 `采集中 N` 可点击标签。
|
||
- 点击 `采集中 N` 打开任务列表弹窗,显示各任务进度。
|
||
|
||
`data-source-bulk-toolbar__running-pill` 是“采集中”标签的样式入口。它和其他状态标签同排,但通过 hover、箭头和蓝色描边表达可交互性。
|
||
|
||
### 采集管理
|
||
|
||
文件:
|
||
|
||
- [PlainResourcePages.tsx](/home/ray/dev/linkong/planet/frontend/src/admin-next/pages/PlainResourcePages.tsx)
|
||
|
||
当前行为:
|
||
|
||
- `/collection-management` 按旧版层级收敛为 `采集器`、`采集调度`、`采集历史 / 快照`。
|
||
- `采集器` 是配置页,左侧列表展示采集器配置;右侧表单编辑 endpoint、认证、请求头、采集参数和启用状态。
|
||
- 新增采集器和目标 Schema 使用草稿详情页,不再用透明 JSON 弹窗;保存后才固化到列表,取消会销毁草稿。
|
||
- 连接按钮只做连通性测试,不保存配置;保存按钮只持久化表单。
|
||
- 凭证教程以可拖拽 Markdown 弹窗展示。没有教程时仍打开弹窗并提供生成入口;生成时显示中心等待状态;重置确认层级必须高于教程弹窗。
|
||
- `采集历史 / 快照` 按采集器聚合。列表不再重复展示同一数据源的所有快照;详情页内部用 Time Capsule / Time Machine 式选择器切换快照版本。
|
||
- AISStream 采集器使用 WebSocket 语义,状态会显示为连接中、实时接收、重连或停止,不使用固定百分比表达完成度。
|
||
- 自定义源入口放在采集器设置内,不在数据源目录里重复提供编辑入口;数据源目录只保留总览、运行和只读抽屉。
|
||
|
||
连接按钮使用内联 Tabler 风格插头图标,来源语义对应 `plug-connected`,避免继续使用刷新图标表达连接动作。
|
||
|
||
### 映射模板和目标 Schema
|
||
|
||
映射能力仍属于采集管理,但不是采集器主 tab:
|
||
|
||
- `映射模板` 承担 sample payload、AI propose、preview、create/update、activate。
|
||
- `目标 Schema` 维护可写入目标结构。
|
||
- `run-mapped`、`stop-mapped`、`stream-status` 负责运行映射后的自定义采集器。
|
||
|
||
这些入口在新版中需要表单化,只有高级字段才折叠为 JSON。不要把模板、Schema、运行状态和采集器配置拍平成同一张表。
|
||
|
||
## 后端接口
|
||
|
||
### 数据源配置列表
|
||
|
||
```http
|
||
GET /api/v1/datasources/configs/all
|
||
```
|
||
|
||
返回 YAML 默认数据源和数据库覆盖配置的合并结果。该路由必须定义在 `/configs/{config_id}` 之前,否则 `all` 会被 FastAPI 当成路径参数并触发 422。
|
||
|
||
返回字段包括:
|
||
|
||
- `name`
|
||
- `default_url`
|
||
- `endpoint`
|
||
- `is_overridden`
|
||
- `is_active`
|
||
- `source_type`
|
||
- `auth_type`
|
||
- `headers`
|
||
- `config`
|
||
- `config_id`
|
||
- `description`
|
||
|
||
`config` 返回前会移除内部连接验证字段,避免前端把校验元数据当成用户配置展示。
|
||
|
||
### 内置采集器连接状态
|
||
|
||
```http
|
||
POST /api/v1/datasources/configs/builtin/connection-status
|
||
```
|
||
|
||
用途:
|
||
|
||
- 给定一份候选配置。
|
||
- 计算 checksum。
|
||
- 判断当前配置是否已经连接。
|
||
|
||
当前前端主要通过连接按钮即时检查,不强依赖这个接口,但它是后续保存按钮置灰、页面初始化状态恢复的后端依据。
|
||
|
||
### 内置采集器连接验证
|
||
|
||
```http
|
||
POST /api/v1/datasources/configs/builtin/connect
|
||
```
|
||
|
||
用途:
|
||
|
||
- 免费采集器直接请求 endpoint。
|
||
- 需要凭证的采集器走对应 credential provider。
|
||
- 验证成功后写入系统级连接记录。
|
||
|
||
成功返回中会带:
|
||
|
||
- `success`
|
||
- `connected`
|
||
- `checksum`
|
||
- `stage`
|
||
- `message`
|
||
- `response_time_ms`
|
||
- `credential_provider`
|
||
- `credential_source`
|
||
|
||
### 凭证教程
|
||
|
||
```http
|
||
GET /api/v1/datasources/credential-guides/{provider}
|
||
POST /api/v1/datasources/credential-guides/{provider}/generate
|
||
POST /api/v1/datasources/credential-guides/{provider}/reset
|
||
```
|
||
|
||
用途:
|
||
|
||
- 读取当前 provider 的 Markdown 凭证教程。
|
||
- 没有教程时,由 AI 根据采集器元数据生成教程。
|
||
- 重置时恢复后端默认教程。
|
||
|
||
前端展示规则:
|
||
|
||
- 展示渲染后的 Markdown,不展示后端 prompt 或元数据。
|
||
- 生成/重置是教程弹窗内部动作,不放在采集器配置主工具栏。
|
||
- 没有教程也要打开弹窗,让用户能从弹窗里生成教程。
|
||
|
||
### BarentsWatch AIS 连接验证
|
||
|
||
```http
|
||
POST /api/v1/settings/integrations/barentswatch/connect
|
||
GET /api/v1/settings/integrations/barentswatch/connectivity
|
||
```
|
||
|
||
BarentsWatch 使用独立接口,是因为它需要在保存前验证草稿凭证:
|
||
|
||
- 使用草稿 `client_id` / `client_secret` 获取 token。
|
||
- 使用 token 请求 AIS endpoint。
|
||
- 连接成功后用草稿凭证指纹写入内置采集器连接记录。
|
||
|
||
## 连接校验服务
|
||
|
||
文件:
|
||
|
||
- [datasource_connectivity.py](/home/ray/dev/linkong/planet/backend/app/services/datasource_connectivity.py)
|
||
|
||
核心职责:
|
||
|
||
- 计算内置采集器配置 checksum。
|
||
- 读取环境变量和 `~/.zshrc` 中的凭证。
|
||
- 判断当前配置是否已连接。
|
||
- 执行 endpoint 健康检查。
|
||
- 保存连接成功记录。
|
||
|
||
### checksum 组成
|
||
|
||
checksum 包含:
|
||
|
||
- 采集器名称
|
||
- endpoint
|
||
- auth type
|
||
- headers
|
||
- 去掉内部校验字段后的 config
|
||
- credential provider
|
||
- 凭证指纹
|
||
|
||
凭证指纹使用凭证内容 hash,不把明文凭证写入连接记录。
|
||
|
||
### 连接记录
|
||
|
||
连接成功记录写入 `SystemSetting`:
|
||
|
||
```text
|
||
category = datasource_connectivity_validations
|
||
```
|
||
|
||
payload 以采集器 source 为 key:
|
||
|
||
```json
|
||
{
|
||
"barentswatch_vessels": {
|
||
"checksum": "...",
|
||
"status": "success",
|
||
"validated_at": "2026-04-29T00:00:00+00:00",
|
||
"status_code": 200,
|
||
"credential_source": "datasource_config",
|
||
"connected_by": "connection_button"
|
||
}
|
||
}
|
||
```
|
||
|
||
`connected_by` 当前有两个来源:
|
||
|
||
- `connection_button`
|
||
- 用户手动点击连接按钮。
|
||
- `collection`
|
||
- 采集任务成功完成,系统自动记录当前有效配置已连通。
|
||
|
||
### 成功采集即连接
|
||
|
||
调度器在采集成功后会调用连接记录写入逻辑:
|
||
|
||
- [scheduler.py](/home/ray/dev/linkong/planet/backend/app/services/scheduler.py)
|
||
|
||
这样已有数据的采集器不会要求用户重复验证。只有当配置 checksum 变化时,才需要重新点击连接。
|
||
|
||
## BarentsWatch AIS 凭证链路
|
||
|
||
文件:
|
||
|
||
- [barentswatch.py](/home/ray/dev/linkong/planet/backend/app/services/barentswatch.py)
|
||
- [vessel_ais.py](/home/ray/dev/linkong/planet/backend/app/services/collectors/vessel_ais.py)
|
||
|
||
解析优先级:
|
||
|
||
1. `DataSourceConfig.auth_config`
|
||
2. `DataSourceConfig.config`
|
||
3. 环境变量
|
||
4. `~/.zshrc`
|
||
|
||
支持的环境变量:
|
||
|
||
```bash
|
||
export BARENTSWATCH_CLIENT_ID="..."
|
||
export BARENTSWATCH_CLIENT_SECRET="..."
|
||
```
|
||
|
||
也兼容历史拼写:
|
||
|
||
```bash
|
||
export BARRENTSWATCH_CLIENT_ID="..."
|
||
export BARRENTSWATCH_CLIENT_SECRET="..."
|
||
```
|
||
|
||
Token 请求规则:
|
||
|
||
- Token URL:`https://id.barentswatch.no/connect/token`
|
||
- `Content-Type`: `application/x-www-form-urlencoded`
|
||
- Body:
|
||
- `grant_type=client_credentials`
|
||
- `client_id`
|
||
- `client_secret`
|
||
- `scope=ais`
|
||
|
||
AIS 请求规则:
|
||
|
||
- Endpoint 默认:`https://live.ais.barentswatch.no/v1/latest/combined`
|
||
- Header:`Authorization: Bearer <access_token>`
|
||
|
||
`VesselAISCollector` 不再自己读取环境变量,而是统一走 `resolve_barentswatch_config()` 和 `fetch_barentswatch_access_token()`,避免设置页、连接验证和采集器三套凭证逻辑分叉。
|
||
|
||
## AISStream 采集器链路
|
||
|
||
文件:
|
||
|
||
- [aisstream.py](/home/ray/dev/linkong/planet/backend/app/services/collectors/aisstream.py)
|
||
- [vessel_ais_aggregation.py](/home/ray/dev/linkong/planet/backend/app/services/vessel_ais_aggregation.py)
|
||
|
||
AISStream 使用 WebSocket 实时流,采集器只写入 `ais_raw_observations` 原始观测层,不直接覆盖最终船只展示表。聚合接口负责多源去重、字段选择和冲突记录。
|
||
|
||
配置项:
|
||
|
||
- `api_key`:保存在 `DataSourceConfig.auth_config`,也可用环境变量 `AISSTREAM_API_KEY`。
|
||
- `endpoint`:默认 `wss://stream.aisstream.io/v0/stream`。
|
||
- `message_types`:默认 `PositionReport` 和 `ShipStaticData`。
|
||
- `bounding_boxes`:AISStream 格式为 `[[[lat_min, lon_min], [lat_max, lon_max]]]`,设置页提供全球、挪威 / 北海、欧洲近海、东亚、北美东西海岸 preset。
|
||
- `max_messages` 和 `receive_timeout_seconds`:控制单次批次式 WebSocket 采集窗口。
|
||
|
||
标准化规则:
|
||
|
||
- `PositionReport` 主要提供位置、速度、航向和状态。
|
||
- 船名可以从 `MetaData.ShipName` 补入,即使消息体本身没有 `name`。
|
||
- 船型通常来自低频 `ShipStaticData.Type`;后端会把 AIS 数字类型码映射为 Cargo / Tanker / Passenger / Fishing / Military。
|
||
- 如果某艘船尚未收到静态消息,聚合结果的船型仍可能是 `Other`,后续由 v5 船舶资料 enrichment 补齐。
|
||
|
||
连接验证会读取保存配置、环境变量和 `~/.zshrc` 中的 `AISSTREAM_API_KEY`。正式采集时,推荐把 API Key 保存到采集器设置;如果只写在 `~/.zshrc`,需要确认后端进程实际继承了该变量,否则连接验证可能可用但 collector 运行时拿不到 key。
|
||
|
||
连接验证和正式采集是两个不同动作。设置页出现 `AISStream 凭证已配置,WebSocket endpoint 格式有效` 只说明配置可以用于连接;运行状态仍可能是 `disconnected`。只有 `aisstream_vessels` 处于 `streaming` / `connected`,并且实时流计数、`last_seen_at` 持续更新时,全球 AIS 数据才会不断写入本地库。启动、停止、重连、健康状态和计数统一从 `/datasources -> 实时流` 和 `/api/v1/realtime-sources` 查看;AISStream 不再参与普通一键采集进度。
|
||
|
||
新版本不再使用 legacy `/api/v1/visualization/geo/vessels` 作为船只列表入口。Earth 初始状态应调用:
|
||
|
||
```http
|
||
GET /api/v1/vessels/snapshot?bbox=lon_min,lat_min,lon_max,lat_max&zoom=12&limit=1000
|
||
```
|
||
|
||
该接口优先查询本地 `ais_raw_observations` 聚合结果;当当前 raw 窗口为空时,会受控回退到 legacy `vessel_position` / `vessel_static` 最新点,并通过 `diagnostics.legacy_fallback_used` 暴露。实时更新走 `/ws` 的 `vessels` channel,订阅时必须提供 `bbox`、`zoom` 和 `limit`。服务端按连接过滤 bbox,并对 collector 广播做 1 秒合并,同一 MMSI 只推送最新位置。
|
||
|
||
## 自定义 REST / WebSocket 映射运行时
|
||
|
||
文件:
|
||
|
||
- [custom_datasource_runtime.py](/home/ray/dev/linkong/planet/backend/app/services/custom_datasource_runtime.py)
|
||
- [datasource_mapping.py](/home/ray/dev/linkong/planet/backend/app/services/datasource_mapping.py)
|
||
|
||
自定义源现在不是独立的新数据孤岛,而是作为内置数据源的补充源写入目标 schema。当前最完整的目标是 `vessel_ais`:自定义 REST 或 WebSocket 源经过确定性 mapping 后写入 AIS raw observations,再通过 `vessels` WebSocket channel 推送给 Earth。
|
||
|
||
Earth 高精度边界不再使用自定义源目标 schema。国界是 Earth 静态资产,由控制台 `运维与配置 -> Earth 内容 -> 国界精度` 保存本机源配置并触发 PMTiles 构建,不写入 `CollectedData`。
|
||
|
||
### 配置语义
|
||
|
||
关键字段:
|
||
|
||
- `source_type`:`rest` / `http` / `websocket` / `ws`。
|
||
- `endpoint`:REST 使用 `http(s)://`,WebSocket 使用 `ws(s)://`。
|
||
- `auth_type`:`none`、`bearer`、`api_key`、`basic`。
|
||
- `headers`:静态请求头。
|
||
- `auth_config`:token、API key、basic 用户名密码,API key 支持 header 或 query。
|
||
- `config.target_schema`:例如 `vessel_ais`、`geo_points` 或 `generic_records`。
|
||
- `config.delivery_mode`:REST 默认 `polling`,WebSocket 默认 `realtime_stream`。
|
||
- `config.merge_target_source`:记录该自定义源补充哪个内置数据,例如 `barentswatch_vessels`。
|
||
|
||
REST runner 支持:
|
||
|
||
- `GET` / `POST`
|
||
- query params
|
||
- JSON body
|
||
- headers 和 auth 注入
|
||
- active mapping 写入目标 schema
|
||
|
||
WebSocket runner 支持:
|
||
|
||
- endpoint 格式校验
|
||
- headers 和 auth 注入
|
||
- 可选 `ws_subscribe_message`
|
||
- `ws_message_path` / `ws_items_path` 提取消息主体或数组
|
||
- 断线重连
|
||
- `debug_max_messages` 调试上限
|
||
- 后台 stream start / stop / status
|
||
|
||
相关 API:
|
||
|
||
```http
|
||
POST /api/v1/datasources/custom/sample
|
||
GET /api/v1/datasources/target-schemas
|
||
POST /api/v1/datasources/{config_id}/run-mapped
|
||
POST /api/v1/datasources/{config_id}/stop-mapped
|
||
GET /api/v1/datasources/{config_id}/mapped-status
|
||
DELETE /api/v1/datasources/configs/{config_id}?delete_mappings=true&delete_source_data=true
|
||
```
|
||
|
||
`run-mapped?background=true` 只对 WebSocket 源有意义,会启动后台 stream。REST 源仍是一次性采集。
|
||
|
||
### 删除与数据清理
|
||
|
||
删除自定义源时有三种层级:
|
||
|
||
- 只删除配置:保留 mapping 和历史数据。
|
||
- 删除配置和 mapping:同时删除该配置的 mapping 模板。
|
||
- 删除配置、mapping 和该源数据:删除该源写入的 `collected_data`、`ais_raw_observations` 和 `ais_source_health`。
|
||
|
||
如果删除的是 `vessel_ais` 自定义源数据,后端会向 `vessels` channel 广播 `reload_required`,提示 Earth 重新拉取船只聚合结果。legacy `vessel_position` 不按自定义源直接删除,因为它没有可靠的 source 归因。
|
||
|
||
### 本地 AIS mock WebSocket
|
||
|
||
文件:
|
||
|
||
- [mock-ais-ws-server.ts](/home/ray/dev/linkong/planet/scripts/mock-ais-ws-server.ts)
|
||
|
||
运行方式:
|
||
|
||
```bash
|
||
bun run mock:ais-ws
|
||
```
|
||
|
||
mock 服务持续发送 AIS-like JSON,用于验证“WebSocket 自定义源 -> mapping -> AIS raw observation -> `vessels` channel -> Earth 船只 upsert”链路。典型配置:
|
||
|
||
```json
|
||
{
|
||
"source_type": "websocket",
|
||
"endpoint": "ws://localhost:8787",
|
||
"config": {
|
||
"target_schema": "vessel_ais",
|
||
"delivery_mode": "realtime_stream",
|
||
"merge_target_source": "barentswatch_vessels",
|
||
"ws_message_path": "$.data",
|
||
"ws_items_path": "$.vessels[*]",
|
||
"ws_reconnect": true
|
||
}
|
||
}
|
||
```
|
||
|
||
## 凭证教程
|
||
|
||
文件:
|
||
|
||
- [credential_guides.py](/home/ray/dev/linkong/planet/backend/app/services/credential_guides.py)
|
||
|
||
接口:
|
||
|
||
```http
|
||
GET /api/v1/settings/credential-guides/{provider}
|
||
POST /api/v1/settings/credential-guides/{provider}/generate
|
||
POST /api/v1/settings/credential-guides/{provider}/reset
|
||
```
|
||
|
||
当前支持:
|
||
|
||
- `barentswatch`
|
||
- `aisstream`
|
||
|
||
默认教程包含 BarentsWatch 官方 tutorial 地址:
|
||
|
||
```text
|
||
https://developer.barentswatch.no/docs/tutorial
|
||
```
|
||
|
||
如果用户点击“教程不好用”,后端会把默认 prompt 发给 AI Provider 生成新的中文教程,并保存到 `SystemSetting`:
|
||
|
||
```text
|
||
category = collector_credential_guides
|
||
```
|
||
|
||
“重置”会删除自定义教程,恢复默认教程。
|
||
|
||
## 保存规则
|
||
|
||
内置采集器配置保存时会移除内部 `connectivity_validation` 字段,避免校验状态跟用户配置混在一起。
|
||
|
||
BarentsWatch `client_secret` 保存时有特殊处理:
|
||
|
||
- 输入框显示脱敏预览。
|
||
- 如果提交值仍等于脱敏预览,后端保留原 secret。
|
||
- 如果输入新值,才替换 secret。
|
||
- 不再提供单独“清除当前 secret”复选框。
|
||
|
||
## 测试覆盖
|
||
|
||
相关测试:
|
||
|
||
- [test_vessels.py](/home/ray/dev/linkong/planet/backend/tests/test_vessels.py)
|
||
|
||
新增覆盖:
|
||
|
||
- 能从 `~/.zshrc` 解析 BarentsWatch 凭证。
|
||
- 环境变量为空时,`resolve_barentswatch_config()` 能回退到 `~/.zshrc`。
|
||
- 船只数据转换和 GeoJSON 输出保持兼容。
|
||
|
||
## 当前 Provider 覆盖
|
||
|
||
当前已经支持的凭证 provider:
|
||
|
||
- `barentswatch`
|
||
- `aisstream`
|
||
- `spacetrack`
|
||
|
||
其他 `requires_credentials=true` 的采集器如果还没有 provider,会返回“凭证链路尚未接入”,前端显示 `不可用`。
|