# 采集器设置与连接验证 ## 背景 控制台现在把“数据源目录”和“采集器配置”拆开: - `/datasources` - 展示所有数据源,包括内置和自定义。 - 点击名称只打开信息抽屉。 - 负责查看状态、触发采集和查看采集中任务。 - `/settings?tab=collector_credentials` - 显示为“采集器设置”。 - 负责 endpoint、请求头、基础参数和凭证配置。 - 所有采集器都提供连接按钮,用于健康检查。 这样做是为了减少首次使用时的认知分裂:接口地址、请求头、凭证和自定义源配置都属于“采集器设置”,而不是散落在数据源列表和系统设置多个入口里。 ## 用户侧规则 连接状态不是前端样式状态,而是由后端根据配置 checksum 和已验证记录判断。 一个内置采集器被视为“已连接”需要满足任一条件: - 当前配置已经成功采集过数据。 - 当前配置点击过连接按钮,并且后端验证成功。 如果 endpoint、请求头、基础配置或凭证指纹相对上次验证成功时发生变化,状态会回到“需要重新连接”。 ## 前端入口 ### 数据源目录 文件: - [DataSources.tsx](/home/ray/dev/linkong/planet/frontend/src/pages/DataSources/DataSources.tsx) - [index.css](/home/ray/dev/linkong/planet/frontend/src/index.css) 当前行为: - 内置数据源和自定义数据源合并为 `UnifiedDataSource` 列表。 - 表格只保留查看、采集和状态类操作。 - 名称点击打开只读抽屉。 - 抽屉中展示: - 是否内置 - 是否启用 - 模块、优先级、频率 - endpoint - 请求头 - 基础配置 - 是否需要凭证 - 有任务运行时,顶部进度区显示 `采集中 N` 可点击标签。 - 点击 `采集中 N` 打开任务列表弹窗,显示各任务进度。 `data-source-bulk-toolbar__running-pill` 是“采集中”标签的样式入口。它和其他状态标签同排,但通过 hover、箭头和蓝色描边表达可交互性。 ### 采集器设置 文件: - [Settings.tsx](/home/ray/dev/linkong/planet/frontend/src/pages/Settings/Settings.tsx) 当前行为: - `collector_credentials` tab 展示为“采集器设置”。 - 下拉框列出内置采集器,并支持维护合并到内置数据的自定义补充源。 - 下拉框右侧只有一个插头图标按钮,用于健康检查。 - 下拉框下方用状态标签展示: - `需要凭证` / `无需凭证` - 模块 - `启用` / `禁用` - `未检查` / `可用` / `不可用` - 是否覆盖 endpoint - 需要凭证的采集器把凭证卡片放在基础配置上方。 - 不需要凭证的采集器只显示基础配置。 - AISStream 采集器使用 WebSocket 语义,状态会显示为连接中、实时接收、重连或停止,不使用固定百分比表达完成度。 - 自定义源入口放在采集器设置内,不在数据源目录里重复提供编辑入口;数据源目录只保留总览、运行和只读抽屉。 连接按钮使用内联 Tabler 风格插头图标,来源语义对应 `plug-connected`,避免继续使用刷新图标表达连接动作。 ## 后端接口 ### 数据源配置列表 ```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` ### 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 ` `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。 ## 自定义 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。 ### 配置语义 关键字段: - `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`。 - `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,会返回“凭证链路尚未接入”,前端显示 `不可用`。