release: bump version to 0.42.0

This commit is contained in:
rayd1o
2026-04-28 04:27:18 +08:00
parent eeee788530
commit b4e8afb272
53 changed files with 6863 additions and 87 deletions

View File

@@ -0,0 +1,35 @@
# Technical Docs
This directory holds "current implementation and current structure" documentation, focusing on:
- How the code is organized right now
- Where the current entry points are
- How state and components work
- Which implementation boundaries future changes should follow
What belongs here:
- Quickstart and user manual
- Frontend context
- Earth frontend structure
- Earth satellite footprint policy
- Earth render layer order
- Earth layer style property index
- Backend runtime control
- Collector status
- Collection format conventions
## Entry Points
- [quickstart.md](/home/ray/dev/linkong/planet/docs/technical/en/quickstart.md): The shortest path to getting Planet running from scratch
- [manual.md](/home/ray/dev/linkong/planet/docs/technical/en/manual.md): Complete usage guide for the console, `planet.sh`, Earth, and Docs
What does not belong here:
- Incomplete roadmaps
- Future iteration plans
- Large-scale refactor proposals
Those belong in:
- [docs/plans/README.md](/home/ray/dev/linkong/planet/docs/plans/README.md)

View File

@@ -0,0 +1,333 @@
# AI Provider Guide
## Overview
`aiprovider` is the model-adapter service for Planet.
It isolates model-vendor details from the main backend so the rest of the system can call a stable business API:
- Caller service -> `planet backend`
- `planet backend` -> `aiprovider`
- `aiprovider` -> concrete model provider
The recommended default is:
- External and cross-service callers use `planet backend`
- Only infrastructure-grade internal jobs call `aiprovider` directly
## Responsibilities
`backend` is responsible for:
- authentication and authorization
- business-level request shaping
- stable `/api/v1/ai/...` endpoints
- internal service-to-service authentication toward `aiprovider`
`aiprovider` is responsible for:
- model protocol adaptation
- provider selection by `.env`
- timeout and lightweight retry
- request tracing via `X-Request-ID`
This now follows an OpenClaw-like seam:
- `AI_PROVIDER` identifies the vendor or logical provider
- `AI_PROVIDER_API` identifies the wire adapter
That split makes MiniMax, Claude-compatible gateways, and self-hosted OpenAI-compatible services easier to model without overloading one config field.
## Supported Providers
`aiprovider` currently supports these provider identities:
- `openai`
- `anthropic`
- `minimax`
- `ollama`
Supported request adapters:
- `openai-completions`
- `anthropic-messages`
- `ollama-generate`
Backward-compatible aliases still accepted:
- `openai_compatible`
- `anthropic_compatible`
- `claude_compatible`
Provider mapping:
- `vLLM`, `LM Studio`, `One API`: `AI_PROVIDER=openai`, `AI_PROVIDER_API=openai-completions`
- `MiniMax`: `AI_PROVIDER=minimax`, `AI_PROVIDER_API=anthropic-messages`
- Claude-compatible gateways: `AI_PROVIDER=anthropic`, `AI_PROVIDER_API=anthropic-messages`
- `Ollama`: `AI_PROVIDER=ollama`, `AI_PROVIDER_API=ollama-generate`
## API Surfaces
### Main backend API
Preferred stable entrypoints:
- `GET /api/v1/ai/provider/status`
- `POST /api/v1/ai/situational-awareness/analyze`
Authentication:
- `Authorization: Bearer <jwt>`
Optional tracing header:
- `X-Request-ID: <caller-generated-id>`
The backend will propagate `X-Request-ID` to `aiprovider` and return the same header in the response.
### AI provider internal API
Internal-only endpoints:
- `GET /v1/provider/status`
- `POST /v1/analyze`
Authentication:
- `X-Provider-Token: <shared-secret>`
Optional tracing header:
- `X-Request-ID: <caller-generated-id>`
## Request Example
### Call through backend
```bash
curl -X POST http://localhost:8000/api/v1/ai/situational-awareness/analyze \
-H "Authorization: Bearer <access_token>" \
-H "X-Request-ID: bgp-incident-20260407-001" \
-H "Content-Type: application/json" \
-d '{
"title": "BGP异常研判",
"objective": "总结当前风险并给出处置建议",
"observations": [
"collector A 在 5 分钟内出现多次 origin 变更",
"异常集中在同一地区前缀"
],
"constraints": [
"不要编造不存在的数据",
"区分事实和推断"
],
"context": {
"source": "bgp-monitor",
"severity": "high"
}
}'
```
### Call `aiprovider` directly
```bash
curl -X POST http://localhost:8010/v1/analyze \
-H "X-Provider-Token: change_me" \
-H "X-Request-ID: ai-batch-job-001" \
-H "Content-Type: application/json" \
-d '{
"title": "链路波动分析",
"objective": "给出简要态势摘要和下一步建议",
"observations": [
"多个节点出现延迟上升"
],
"constraints": [
"不要假设根因已经确认"
],
"context": {
"region": "APAC"
}
}'
```
## Response Shape
Both backend and `aiprovider` return the same payload shape:
```json
{
"provider": "minimax",
"api": "anthropic-messages",
"model": "MiniMax-M2.7",
"content": "1) 态势摘要 ...",
"content_blocks": [],
"text_blocks": [],
"thinking_blocks": [],
"raw_response": {}
}
```
Both services also return:
- `X-Request-ID: <id>`
## Configuration
### Backend
Recommended backend `.env`:
```env
AI_PROVIDER_SERVICE_URL=http://localhost:8010
AI_PROVIDER_SERVICE_TOKEN=change_me
AI_PROVIDER_TIMEOUT_SECONDS=60
AI_PROVIDER_RETRY_ATTEMPTS=2
```
Reference file:
- [backend/.env.example](/home/ray/dev/linkong/planet/backend/.env.example)
### AI Provider
Reference file:
- [aiprovider/.env.example](/home/ray/dev/linkong/planet/aiprovider/.env.example)
Frontend local reference:
- [frontend/.env.example](/home/ray/dev/linkong/planet/frontend/.env.example)
Common settings:
```env
SERVICE_NAME=planet-ai-provider
SERVICE_VERSION=0.1.0
AI_PROVIDER_SERVICE_TOKEN=change_me
AI_TIMEOUT_SECONDS=60
AI_HTTP_RETRY_ATTEMPTS=2
AI_ANALYSIS_SYSTEM_PROMPT=你是态势感知分析助手。请基于输入的上下文、观测与约束,输出结构化、克制、可执行的分析。
```
### OpenAI-compatible example
```env
AI_PROVIDER=openai
AI_PROVIDER_API=openai-completions
AI_BASE_URL=http://127.0.0.1:8001/v1
AI_API_KEY=local-key
AI_MODEL=your-local-model
```
### MiniMax CN example
```env
AI_PROVIDER=minimax
AI_PROVIDER_API=anthropic-messages
AI_BASE_URL=https://api.minimaxi.com/anthropic
AI_API_KEY=sk-cp-xxxxx
AI_MODEL=MiniMax-M2.7
AI_MAX_TOKENS=1200
AI_ANTHROPIC_VERSION=2023-06-01
```
MiniMax note:
- This follows the same Anthropic Messages request shape as the official MiniMax examples.
- For MiniMax, `aiprovider` now disables `thinking` by default unless the caller explicitly passes a `thinking` object.
- This mirrors OpenClaw's caution around MiniMax Anthropic-compatible behavior.
### Anthropic-compatible example
```env
AI_PROVIDER=anthropic
AI_PROVIDER_API=anthropic-messages
AI_BASE_URL=https://your-claude-compatible-endpoint.example.com/anthropic
AI_API_KEY=your_api_key
AI_MODEL=your-model
AI_MAX_TOKENS=1200
AI_ANTHROPIC_VERSION=2023-06-01
```
### Ollama example
```env
AI_PROVIDER=ollama
AI_PROVIDER_API=ollama-generate
AI_BASE_URL=http://127.0.0.1:11434
AI_API_KEY=
AI_MODEL=qwen2.5:7b
```
## Deployment Modes
### Single machine
Recommended local flow:
- `backend` on `localhost:8000`
- `aiprovider` on `localhost:8010`
- local model gateway on `localhost:11434` or another local port
Helpers already included:
- [planet.sh](/home/ray/dev/linkong/planet/planet.sh)
- [docker-compose.local-model.yml](/home/ray/dev/linkong/planet/docker-compose.local-model.yml)
### Multi-machine
Example topology:
- app machine: `backend`
- AI gateway machine: `aiprovider`
- model machine: local model service or cloud proxy
In that case, this becomes service-to-service HTTP RPC:
- caller -> backend
- backend -> `http://10.0.0.12:8010`
- `aiprovider` -> model endpoint
Recommended cross-machine backend config:
```env
AI_PROVIDER_SERVICE_URL=http://10.0.0.12:8010
AI_PROVIDER_SERVICE_TOKEN=change_me
AI_PROVIDER_TIMEOUT_SECONDS=60
AI_PROVIDER_RETRY_ATTEMPTS=2
```
Recommended operating rules:
- keep `aiprovider` on a private network
- protect it with `X-Provider-Token` at minimum
- always send `X-Request-ID`
- keep callers on the backend API unless they are infrastructure jobs
## Retry And Failure Behavior
`backend -> aiprovider`:
- retries lightweight network / 5xx failures
- returns `502` when the provider service is unavailable
`aiprovider -> model provider`:
- retries lightweight network / 5xx failures
- returns `502` when the model provider is unavailable
This is intentionally conservative. It avoids masking persistent errors while still absorbing short hiccups.
## Operational Notes
- `./planet.sh start` now starts `aiprovider` automatically
- `./planet.sh restart -a` restarts only `aiprovider`
- `./planet.sh log -a` tails `aiprovider` logs
- `./planet.sh health` reports `aiprovider` health
## Recommended Calling Policy
- Frontend and application services: call `backend`
- Scheduled infra jobs and diagnostics: optionally call `aiprovider`
- Do not let multiple business services integrate model vendors independently
That keeps provider switching centralized and avoids model-specific drift across the system.

View File

@@ -0,0 +1,264 @@
# Data Collectors
## I. System Architecture
```
┌─────────────────────────────────────────────────────────────────┐
│ Data Collection Architecture │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ TOP500 │ │ Epoch AI │ │ HuggingFace │ │
│ │ Collector │ │ Collector │ │ Collector │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └───────────────────┼───────────────────┘ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ BaseCollector │◄── Base class (unified) │
│ │ run() method │ │
│ └─────────┬───────────┘ │
│ │ │
│ ┌─────────────────┼─────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ fetch() │ │transform()│ │ _save_data│ │
│ │ raw data │ │ transform │ │ save to DB│ │
│ └───────────┘ └───────────┘ └───────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ CollectedData table│◄── Unified storage │
│ └─────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Scheduler (APScheduler) │ │
│ │ Scheduled tasks: every 4h/6h/12h/1d auto-execute │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
## II. Pipeline
```python
# 1. Scheduler triggers (scheduled or manual)
# ↓
# 2. run() executes the full pipeline
async def run(self, db):
# 2.1 Check if collector is enabled
if not collector_registry.is_active(self.name):
return {"status": "skipped"}
# 2.2 Record task start
task = CollectionTask(status="running")
db.add(task)
await db.commit()
# 2.3 FETCH — get raw data (implemented by subclass)
raw_data = await self.fetch()
# 2.4 TRANSFORM — convert to unified format
data = self.transform(raw_data)
# 2.5 SAVE — persist to database
records_count = await self._save_data(db, data)
# 2.6 Record task completion
task.status = "success"
task.records_processed = records_count
await db.commit()
```
**Core file**: `backend/app/services/collectors/base.py`
## III. Collector List
| Collector | Data type | Content | Frequency |
|-----------|-----------|---------|-----------|
| TOP500 | supercomputer | Global supercomputer rankings (compute, performance) | 4 hours |
| Epoch AI | gpu_cluster | GPU compute cluster info | 6 hours |
| HuggingFace Models | model | AI model information | 12 hours |
| HuggingFace Datasets | dataset | Dataset information | 12 hours |
| HuggingFace Spaces | space | Demo applications | 1 day |
| PeeringDB | ixp/network/facility | Internet exchange points / networks / facilities | 1-2 days |
| TeleGeography | submarine_cable | Submarine cable information | 7 days |
## IV. Data Format (stored in CollectedData table)
```python
# Each collector's parse_response() return format
{
"source_id": "top500_1", # Original system ID (required)
"name": "El Capitan", # Name (required)
"description": "System desc...", # Description
"country": "United States", # Country
"city": "Livermore, CA", # City
"latitude": "37.6819", # Latitude (string)
"longitude": "-121.7681", # Longitude (string)
"value": "1742.00", # Performance value (e.g. compute)
"unit": "PFlop/s", # Unit
"metadata": { # Extra data (JSON)
"rank": 1,
"r_peak": 2746.38,
"cores": 11039616
},
"reference_date": "2025-11-01" # Data reference date
}
```
## V. Database Schema
**CollectedData table** (`collected_data`)
| Field | Type | Description |
|-------|------|-------------|
| id | SERIAL | Primary key |
| source | VARCHAR(100) | Data source name (top500, huggingface, etc.) |
| source_id | VARCHAR(100) | Original data ID |
| data_type | VARCHAR(50) | Data type (supercomputer, model, etc.) |
| name | VARCHAR(500) | Name |
| title | VARCHAR(500) | Title |
| description | TEXT | Description |
| country | VARCHAR(100) | Country |
| city | VARCHAR(100) | City |
| latitude | VARCHAR(50) | Latitude |
| longitude | VARCHAR(50) | Longitude |
| value | VARCHAR(100) | Performance value |
| unit | VARCHAR(20) | Unit |
| metadata | JSONB | Extra metadata |
| collected_at | TIMESTAMP | Collection time |
| reference_date | TIMESTAMP | Data reference date |
| is_valid | INTEGER | Whether valid |
**Core file**: `backend/app/models/collected_data.py`
## VI. TOP500 Collector Example (full pipeline)
```python
# 1. fetch() — get HTML from the web
async def fetch(self):
url = "https://top500.org/lists/top500/list/2025/11/"
response = await client.get(url)
return response.text # returns HTML
# 2. parse_response() — parse HTML into unified format
def parse_response(self, html):
soup = BeautifulSoup(html, "html.parser")
table = soup.find("table")
for row in table.find_all("tr")[1:]: # skip header
cells = row.find_all("td")
entry = {
"source_id": f"top500_{cells[0].text}",
"name": cells[1].text.strip(),
"country": cells[2].text.strip(),
"city": "",
"latitude": "",
"longitude": "",
"value": "1742.00",
"unit": "PFlop/s",
"metadata": {
"rank": 1,
"cores": "11340000"
},
"reference_date": "2025-11-01"
}
data.append(entry)
return data
# 3. run() automatically calls _save_data() to save to database
```
**Core file**: `backend/app/services/collectors/top500.py`
## VII. Scheduler
```python
# Register all collectors into scheduled tasks at startup
def start_scheduler():
for name, collector in collectors.items():
if collector_registry.is_active(name):
scheduler.add_job(
run_collector_task,
trigger=IntervalTrigger(hours=collector.frequency_hours),
id=name,
name=name
)
```
| Collector | Frequency |
|-----------|-----------|
| TOP500 | Every 4 hours |
| Epoch AI | Every 6 hours |
| HuggingFace | Every 12 hours |
| PeeringDB | Every 1-2 days |
| TeleGeography | Every 7 days |
**Core file**: `backend/app/services/scheduler.py`
## VIII. Code Files
```
backend/app/services/collectors/
├── base.py # Base class: run() pipeline, _save_data() persistence
├── registry.py # Collector registry
├── scheduler.py # Scheduled task dispatch (APScheduler)
├── top500.py # TOP500 collector
├── epoch_ai.py # Epoch AI collector
├── huggingface.py # HuggingFace collector
├── peeringdb.py # PeeringDB collector
└── telegeraphy.py # TeleGeography submarine cable collector
backend/app/models/
└── collected_data.py # Unified data model
```
## IX. Data Usage
Collected data ultimately:
1. **Visualization** — displays supercomputers, GPU clusters, and submarine cables' geographic positions
2. **Situational analysis** — global compute distribution statistics and growth trends
3. **Alert system** — detects changes to important nodes
## X. Collector Registration
Collectors are automatically registered at application startup:
```python
# backend/app/services/collectors/__init__.py
collector_registry.register(TOP500Collector())
collector_registry.register(EpochAIGPUCollector())
collector_registry.register(HuggingFaceModelCollector())
collector_registry.register(HuggingFaceDatasetCollector())
collector_registry.register(HuggingFaceSpacesCollector())
collector_registry.register(PeeringDBIXPCollector())
collector_registry.register(PeeringDBNetworkCollector())
collector_registry.register(PeeringDBFacilityCollector())
collector_registry.register(TeleGeographyCableCollector())
collector_registry.register(TeleGeographyLandingPointCollector())
collector_registry.register(TeleGeographyCableSystemCollector())
```
**Core file**: `backend/app/services/collectors/registry.py`
## XI. Triggering Collection
### Method 1: Scheduled
At startup, APScheduler automatically creates scheduled tasks based on each collector's `frequency_hours` setting.
### Method 2: Manual API trigger
```bash
# Trigger TOP500 collection
curl -X POST http://localhost:8000/api/v1/datasources/1/trigger \
-H "Authorization: Bearer <token>"
```
**Core file**: `backend/app/api/v1/datasources.py`

View File

@@ -0,0 +1,347 @@
# System Service Control
This document defines the fixed mapping between admin control-plane actions and
the existing `planet.sh` service-management commands.
The goal is to reuse the current operational script semantics without exposing
arbitrary shell execution to the frontend or API callers.
## Scope
- This mapping is for admin-side operational controls only.
- The control plane must submit a fixed action name, not a raw shell command.
- The backend is responsible for translating an allowed action into a fixed
`planet.sh` invocation.
## Design Rules
- Only whitelist actions may be executed.
- The frontend must never send arbitrary shell strings.
- The backend must build command arguments from a fixed mapping table.
- High-risk actions should be restricted to `super_admin`.
- Prefer partial restarts over full-stack restarts when UI continuity matters.
## Action Mapping
| Action name | Intended use | `planet.sh` command | Notes |
| --- | --- | --- | --- |
| `restart-backend` | Restart backend API only | `./planet.sh restart -b` | Recommended first implementation for UI-triggered restart flows. |
| `restart-database` | Restart PostgreSQL and Redis containers | `./planet.sh restart -d` | Useful when database/cache services need a controlled bounce without restarting the UI. |
| `restart-system` | Restart the whole application stack | `./planet.sh restart` | Frontend continuity breaks briefly; UI should switch to guided recovery mode. |
| `restart-frontend` | Restart frontend dev server only | `./planet.sh restart -f` | Use with caution; UI continuity is weaker than backend-only restart. |
| `restart-backend-port` | Restart backend on a specific port | `./planet.sh restart -b <port>` | Port must be backend-validated before execution. |
| `restart-frontend-port` | Restart frontend on a specific port | `./planet.sh restart -f <port>` | Port must be backend-validated before execution. |
| `health-check` | Read current service health | `./planet.sh health` | Safe read-only operational action. |
| `show-logs-backend` | Inspect backend logs | `./planet.sh log -b` | Best used for CLI/operator tooling, not normal Web UI streaming. |
| `show-logs-frontend` | Inspect frontend logs | `./planet.sh log -f` | Best used for CLI/operator tooling, not normal Web UI streaming. |
## Not Exposed In UI By Default
The following existing script capabilities should not be exposed directly in the
Web UI unless there is an explicit product need and an additional safety review:
- `./planet.sh restart`
- `./planet.sh start`
- `./planet.sh stop`
- `./planet.sh createuser`
- any future raw shell passthrough
Reason:
- full restart can break the current control session;
- stop/start have larger blast radius;
- user creation is not a service-control operation;
- raw shell passthrough creates unnecessary privilege risk.
## Recommended First-Phase UI Contract
### Frontend action payload
```json
{
"action": "restart-backend"
}
```
### Backend command resolution
```text
restart-backend -> ["./planet.sh", "restart", "-b"]
restart-database -> ["./planet.sh", "restart", "-d"]
restart-system -> ["./planet.sh", "restart"]
restart-frontend -> ["./planet.sh", "restart", "-f"]
health-check -> ["./planet.sh", "health"]
```
## API Draft
### Primary Endpoint
- `POST /api/v1/system/restart-tasks`
Purpose:
- create a controlled restart task;
- resolve a whitelist action into a fixed `planet.sh` command;
- hand execution off to an external runner or detached subprocess.
### Request Body
```json
{
"action": "restart-backend"
}
```
Optional future shape:
```json
{
"action": "restart-backend-port",
"port": 8000
}
```
### Response
```json
{
"task_id": "restart_20260331_153000_ab12cd",
"action": "restart-backend",
"status": "queued",
"stage": "accepted",
"message": "Restart task accepted"
}
```
### Task Query Endpoint
- `GET /api/v1/system/restart-tasks/{task_id}`
Response shape:
```json
{
"task_id": "restart_20260331_153000_ab12cd",
"action": "restart-backend",
"status": "queued",
"stage": "accepted",
"message": "Waiting for execution",
"requested_by": {
"id": 1,
"username": "admin"
},
"created_at": "2026-03-31T15:30:00+08:00",
"updated_at": "2026-03-31T15:30:02+08:00"
}
```
### Optional Log Endpoint
- `GET /api/v1/system/restart-tasks/{task_id}/logs`
Suggested response:
```json
{
"task_id": "restart_20260331_153000_ab12cd",
"lines": [
"accepted restart-backend request",
"spawning restart command",
"waiting for backend shutdown",
"waiting for backend health recovery"
]
}
```
This log endpoint is optional for phase one. The first version can work with
task state plus `/health` polling alone.
## Task State Model
### Status
- `queued`
- `running`
- `succeeded`
- `failed`
- `timeout`
### Stage
- `accepted`
- `spawning`
- `stopping`
- `starting`
- `waiting_for_health`
- `healthy`
- `failed`
### Interpretation
- `status` is the high-level terminal or non-terminal state.
- `stage` is the operator-facing execution phase for the UI.
- `message` is the short human-readable line shown in the modal or full-screen
overlay.
## Permission Model
- `restart-backend` should require `super_admin`.
- Permission checks should follow the same role pattern already used in
[users.py](/home/ray/dev/linkong/planet/backend/app/api/v1/users.py).
- Frontend visibility may hide controls for non-`super_admin`, but backend must
still enforce authorization.
## Storage Model
Recommended first implementation:
- store restart task state in Redis;
- keep task lifetime short;
- keep recent logs as a bounded list.
Suggested keys:
- `system:restart_task:{task_id}`
- `system:restart_task:{task_id}:logs`
Suggested stored fields:
- `task_id`
- `action`
- `status`
- `stage`
- `message`
- `requested_by_id`
- `requested_by_username`
- `created_at`
- `updated_at`
## Execution Model
The request-handling API process should not depend on itself surviving long
enough to stream the whole restart output.
Recommended execution flow:
1. validate caller and action
2. create task state in Redis
3. resolve action to fixed `planet.sh` argv
4. spawn detached executor
5. return `task_id`
6. executor updates task state while restart is in progress
7. frontend polls health and/or task state until recovery
Recommended command resolution examples:
```text
restart-backend -> ["./planet.sh", "restart", "-b"]
restart-frontend -> ["./planet.sh", "restart", "-f"]
restart-backend-port -> ["./planet.sh", "restart", "-b", "<port>"]
health-check -> ["./planet.sh", "health"]
```
## Frontend Polling Flow
Recommended first-phase UX:
1. user clicks `重启后端`
2. confirmation modal explains temporary unavailability
3. frontend calls `POST /api/v1/system/restart-tasks`
4. UI enters blocking restart state
5. frontend polls `/health` every `1-2s`
6. temporary request failures are treated as expected
7. after `2-3` consecutive successful health checks, frontend reloads page
Optional richer polling:
1. poll task status endpoint while backend is still reachable
2. switch to `/health` recovery polling after disconnect begins
3. refresh page after health recovery
## Frontend State Machine
- `idle`
- `confirming`
- `submitting`
- `waiting_for_shutdown`
- `waiting_for_recovery`
- `recovered`
- `failed`
- `timeout`
Suggested UI messages:
- `已发送重启指令`
- `正在停止后端服务`
- `正在等待服务恢复`
- `服务已恢复,正在刷新页面`
- `恢复超时,请手动检查服务状态`
## Phase-One Recommendation
Implement only the following in phase one:
- `restart-backend`
- `super_admin` permission gate
- task creation endpoint
- Redis-backed task state
- frontend confirmation modal
- frontend `/health` polling
- automatic page reload after recovery
Do not implement in phase one:
- full `./planet.sh restart`
- raw shell command passthrough
- arbitrary service control
- full terminal stdout streaming
- multi-action concurrent restart queueing
## Implementation Checklist
### Backend
1. add a dedicated system-control API module under `backend/app/api/v1/`
2. add a whitelist-based action resolver for `planet.sh`
3. store restart task state in Redis
4. add detached restart-runner script execution
5. expose:
- `POST /api/v1/system/restart-tasks`
- `GET /api/v1/system/restart-tasks/{task_id}`
- optional task log endpoint
6. enforce `super_admin` permission on all restart-task endpoints
### Frontend
1. add a `重启后端` control on the dashboard for `super_admin`
2. show a confirmation modal before dispatch
3. after submission, switch modal into blocking restart state
4. poll `/health` until backend recovery is confirmed
5. auto-refresh page after consecutive successful health checks
6. show short stage-oriented logs instead of raw terminal streaming
### Operational Notes
1. phase one should target backend-only restart
2. frontend restart should remain out of scope initially
3. command execution must always originate from repository root
4. only fixed action names may cross the API boundary
## Validation Requirements
- Reject any action not present in the whitelist.
- If a port-bearing action is added, validate the port as an integer in
`1..65535`.
- Resolve commands from the repository root so `planet.sh` runs with a stable
working directory.
- Record the requested action, operator identity, execution start time, and
result.
## Implementation Guidance
- For UI-triggered restart flows, prefer `restart-backend` first.
- Do not rely on the current API request process to stream full restart output
after it triggers its own restart.
- Use a task record plus polling/health-check recovery flow instead of raw
terminal streaming as the primary UX.

View File

@@ -0,0 +1,355 @@
# BGP Context
## Current Goal
The BGP module is being evolved from an anomaly-only demo into a layered observability pipeline:
`raw observations -> enrichment -> detectors -> incidents -> console/Earth visualization`
The practical product goal is no longer just to "show incidents on the globe". The current product objective is:
1. keep BGP visually present on Earth even when incident density is low
2. make incidents clearly feel like a higher-confidence layer than anomalies
3. show that the observation network is still active even when there are no active incidents
In practice, that means Earth should behave like an observability surface, not only an incident map:
- `collectors` show that observation is happening
- `activity` shows where routing state is currently active or noisy
- `incidents` become the highest-confidence focus layer
## Current Backend Architecture
### Data Layers
1. `BGPObservation`
- File: `backend/app/models/bgp_observation.py`
- Purpose: store normalized raw routing observations from live/history sources.
- Typical fields:
- `source`
- `collector`
- `peer_asn`
- `peer_ip`
- `prefix`
- `event_type`
- `as_path`
- `origin_asn`
- `next_hop`
- `communities`
- `observed_at`
- `raw_payload`
- `collector_geo`
- `ingest_batch_id`
2. `BGPAnomaly`
- File: `backend/app/models/bgp_anomaly.py`
- Purpose: hold atomic detector outputs.
- Current detector output types include:
- `origin_change`
- `more_specific_burst`
- `mass_withdrawal`
3. `BGPIncident`
- File: `backend/app/models/bgp_incident.py`
- Purpose: aggregate atomic anomalies into incident-level objects for humans and the UI.
### Pipeline
Main flow is currently anchored in:
- `backend/app/services/collectors/bgp_common.py`
- `backend/app/services/bgp_enrichment.py`
- `backend/app/services/bgp_detectors.py`
- `backend/app/services/bgp_incidents.py`
Operational flow:
1. collectors fetch raw BGP data
2. `normalize_bgp_event()` standardizes payloads
3. observations are persisted to `bgp_observations`
4. enrichment augments events with analysis context
5. detectors create `bgp_anomalies`
6. incident aggregation rolls anomalies up into `bgp_incidents`
### Current Ingest Sources
1. `RIPE RIS Live`
- Collector file: `backend/app/services/collectors/ris_live.py`
- Used for realtime observation flow.
2. `CAIDA BGPStream Backfill`
- Collector file: `backend/app/services/collectors/bgpstream.py`
- Used as history/backfill entry point.
## Current Enrichment Status
Implemented enrichment skeleton in:
- `backend/app/services/bgp_enrichment.py`
Current enrichments:
- prefix family / prefix length
- supernet / more-specific derivation
- deduplicated AS path
- path prepending hints
- collector region info
- prefix baseline hints
- new-origin detection
- ASN organization profile from PeeringDB where available
- prefix scope / impacted region hints
- prefix geography source priority:
- `OpenGeoFeed` (override/high confidence)
- `IPtoASN` (country-range baseline)
- `NRO delegated stats` (registry-allocation fallback)
Current limitation:
- `RPKI` is still placeholder-only and returns `unknown`
- no real ROA validation source is integrated yet
- `inetnum` / `inet6num` whois fallback is still pending
## Current API Surface
Primary API file:
- `backend/app/api/v1/bgp.py`
Available endpoints:
- `/api/v1/bgp/events`
- `/api/v1/bgp/events/summary`
- `/api/v1/bgp/events/{id}`
- `/api/v1/bgp/anomalies`
- `/api/v1/bgp/anomalies/summary`
- `/api/v1/bgp/anomalies/{id}`
- `/api/v1/bgp/incidents`
- `/api/v1/bgp/incidents/summary`
- `/api/v1/bgp/incidents/{id}`
Visualization GeoJSON endpoints:
- `backend/app/api/v1/visualization.py`
- `/api/v1/visualization/geo/bgp-collectors`
- `/api/v1/visualization/geo/bgp-anomalies`
- `/api/v1/visualization/geo/bgp-incidents`
## Current Earth Behavior
Relevant files:
- `frontend/public/earth/js/bgp.js`
- `frontend/public/earth/js/main.js`
- `frontend/public/earth/js/info-card.js`
- `frontend/public/earth/js/constants.js`
- `frontend/public/earth/index.html`
Current design:
1. Collectors are always shown when BGP is enabled.
2. Incident markers are now the primary Earth BGP markers.
3. If there are no incidents, Earth falls back to anomaly markers.
4. If there are no anomalies either, collectors still provide presence.
5. A dedicated `activity layer` now adds:
- per-collector recent 15-minute activity halos
- clustered regional activity hints derived from active collectors
6. Incident markers now use:
- symbol-driven event cores
- outward ring pulses
- reduced diffuse glow compared with older Earth builds
5. The right-side stats now show:
- BGP events
- collector count
- BGP status summary
This is directionally correct, but still incomplete for low-event-density periods. Right now Earth can still feel too quiet when incidents are sparse because the system lacks a dedicated `activity layer` between raw observation and incident focus.
Current BGP status strategy:
- incidents present: show active incident count
- no incidents but anomalies present: show active anomaly count, plus active observation regions when available
- no incidents/anomalies but activity present: show `观测网络运行中`
- no incidents/anomalies but collectors present: show `观测网络运行中 · 当前未发现聚合级事件`
- no BGP data at all: show `暂无观测数据`
Earth info-card strategy:
- `bgp` card is now incident-centric in wording
- `bgp_collector` card shows collector location and current event count
## Current Product Gap
The main product gap is not architecture correctness. It is low-density visualization strategy.
Current reality:
- incident count is naturally much lower than anomaly count
- that is expected, because incidents are aggregated and de-noised
- but incident-first rendering makes the Earth view look too quiet unless there is another always-available activity layer
Implementation detail for the recommended `activity layer` is expanded in [bgp-region-aggregation-plan.md](/home/ray/dev/linkong/planet/docs/plans/earth-bgp-region-aggregation-plan.md).
So the immediate next milestone is:
`event map -> observability map`
That means Earth needs three simultaneously readable layers:
1. `observation layer`
- collectors
- recent collector activity
- baseline coverage
2. `activity layer`
- recent event density
- anomaly/noise hotspots
- regional activity scoring
- incident presence bonus
3. `incident layer`
- sparse but highly legible, high-confidence event objects
- symbol-driven markers
- outward ring pulse instead of broad diffuse glow
## Incident Visual Direction
The Earth `incident` layer should not read like a large glowing patch. It should read like a compact, high-confidence event focus.
Design principles:
1. `incident` markers should use a strong primary symbol
- the symbol shape should carry type meaning where possible
- examples:
- `origin_change`: triangle-like warning marker
- `mass_withdrawal`: alert/exclamation-style marker
- `more_specific_burst`: split/radiating marker
2. emphasis should come from outward ring pulses, not area flooding
- use a compact hot core
- use one or more expanding ring pulses
- avoid broad luminous blobs that make the event center feel vague
3. `collector` and `incident` must stay visually distinct
- collectors are observation infrastructure
- incidents are extracted event focus
- collector activity should stay quieter than incident pulse language
4. calm periods still need observability presence
- collectors and activity layers should keep the map alive
- once incidents appear, they should clearly dominate nearby BGP visuals
5. incident geography should become `prefix-centric`
- collectors should remain evidence sources, not the primary event location
- preferred geography priority:
- `prefix_geography`
- `prefix_scope`
- `ASN organization region`
- `collector centroid` as final fallback
- `prefix_scope` should remain an observation-derived scope hint
- a new `prefix_geography` layer should be introduced for actual prefix-centric placement
Reference inspiration:
- `World Monitor`
- sparse event symbols
- compact centers
- ring-like outward pulses
- stronger incident legibility than diffuse glow
## Current Console Behavior
Relevant page:
- `frontend/src/pages/BGP/BGP.tsx`
Current BGP console page has three levels:
1. observation summary
- total events
- collector count
- prefix count
2. incident summary and incident table
3. anomaly detail table plus recent observation events
This means the BGP page still has useful signal even when there are zero anomalies.
## Known Product/Engineering Boundaries
1. The current system is still closer to an event board than a full BGP sensing platform.
2. RIS coverage still needs to expand beyond narrow subscription scope.
3. BGPStream history is still not full MRT-to-prefix decoded analytics.
4. Collector geography still depends heavily on static RIPE RIS mappings.
5. Incident-to-cable/IXP/region association is still weak and early-stage.
6. Earth currently visualizes logical observation/impact structure, not true physical traffic paths.
## Test Status
BGP-specific tests live in:
- `backend/tests/test_bgp.py`
Verified status at this point:
- `25 passed` for `backend/tests/test_bgp.py`
- `62 passed` for `backend/tests`
Covered areas include:
- normalization
- observation serialization
- enrichment
- detectors, including route leak candidate and path flap
- incident aggregation
- batch anomaly creation
- BGP events/incidents API
- summary endpoints
## Most Relevant Files
Backend:
- `backend/app/models/bgp_observation.py`
- `backend/app/models/bgp_anomaly.py`
- `backend/app/models/bgp_incident.py`
- `backend/app/services/collectors/bgp_common.py`
- `backend/app/services/bgp_enrichment.py`
- `backend/app/services/bgp_detectors.py`
- `backend/app/services/bgp_incidents.py`
- `backend/app/api/v1/bgp.py`
- `backend/app/api/v1/visualization.py`
Frontend:
- `frontend/src/pages/BGP/BGP.tsx`
- `frontend/public/earth/js/bgp.js`
- `frontend/public/earth/js/main.js`
- `frontend/public/earth/js/info-card.js`
- `frontend/public/earth/js/constants.js`
- `frontend/public/earth/index.html`
## Recommended Next Steps
### Next Backend / Detection Priority
1. Integrate real RPKI validation data.
2. Expand realtime collector coverage and include withdrawals more broadly.
3. Continue refining route leak and path instability detectors with stronger heuristics.
### Next Correlation / Storytelling Priority
4. Strengthen incident aggregation semantics and titles.
5. Add weak correlation from incidents to:
- cable corridors
- landing points
- IXPs
- other traffic anomaly sources
6. Refine Earth hover/click handoff between collectors and incidents.
### Next Visualization Priority
7. Refine regional activity scoring so the activity layer is informative without becoming noisy.
8. Add more incident symbol types as new detectors land.
9. Add a real prefix geography source:
- `IPtoASN / IPtoCountry` as the first practical dataset
- `OpenGeoFeed` as a higher-quality override layer
- registry/whois only as fallback

View File

@@ -0,0 +1,252 @@
# Earth Frontend Context
This document describes the current real structure of the Earth display frontend. The focus is on helping future changes to the HUD, layers, media panel, real terrain, and BGP visualization avoid repeating past structural and state-sync pitfalls.
Related references:
- [rules.md](/home/ray/dev/linkong/planet/rules.md)
- [frontend-layout-guidelines.md](/home/ray/dev/linkong/planet/docs/technical/en/frontend-layout-guidelines.md)
## Current Goal
The Earth frontend is not an ordinary admin page — it is an independent large-screen display frontend. Current product goals:
- Maintain the spatial depth and readability of the globe view
- Keep HUD, layers, media panel, BGP, satellites, cables, and similar elements in a unified interaction model
- Clearly represent states like loading, enabled, hidden, and locked
## Current Entry Point
React route entry:
- [Earth.tsx](/home/ray/dev/linkong/planet/frontend/src/pages/Earth/Earth.tsx)
The current approach is simple:
- The React page only provides a full-screen `iframe`
- The actual Earth application runs at:
- [index.html](/home/ray/dev/linkong/planet/frontend/public/earth/index.html)
Earth frontend is essentially a standalone static application under `public/earth`.
## Current File Layers
### 1. Page Entry and Structure
- [index.html](/home/ray/dev/linkong/planet/frontend/public/earth/index.html)
Responsibilities:
- Base HUD DOM
- Layer panel
- Media panel
- Toolbar
- Settings dialog
- Legacy element ID compatibility
### 2. Main Runtime
- [main.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/main.js)
Responsibilities:
- Globe initialization
- Three.js scene assembly
- Data loading and refresh
- Layer module integration
- Earth-level state synchronization
### 3. Earth Control Layer
- [controls.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/controls.js)
Responsibilities:
- Toolbar interaction
- Layer panel interaction
- Rotation / zoom / layout
- HUD panel drag
- Layer toggle state machine
- Earth settings read, persist, and reset
This is currently the most critical UI control entry point for the Earth frontend.
### 4. UI and Status Messages
- [ui.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/ui.js)
Responsibilities:
- Loading panel
- Status message
- Tooltip / error / cleanup logic
### 5. Globe and Terrain
- [earth.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/earth.js)
- [terrain.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/terrain.js)
Responsibilities:
- Globe sphere, cloud layer, atmosphere
- Real terrain mesh
- Terrain tile fetch, decode, displacement, and shading
### 6. Layer Modules
- [satellites.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/satellites.js)
- [cables.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/cables.js)
- [bgp.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/bgp.js)
- [bgp-cruise-adapter.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/bgp-cruise-adapter.js)
- [compute-centers.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/compute-centers.js)
- [country-boundaries.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/country-boundaries.js)
Each module is responsible for its own:
- Data fetching
- Three.js mesh creation and update
- State tracking (loaded, visible, hover, locked)
- Self-cleanup (dispose on scene destroy)
### 7. HUD Panels and Search
- [hud-panels.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/hud-panels.js)
- [info-card.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/info-card.js)
- [search.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/search.js)
- [legend.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/legend.js)
### 8. Cruise Mode
- [cruise-sequencer.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/cruise-sequencer.js)
- [callout-connector.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/callout-connector.js)
The cruise sequencer handles generic logic: current target, queue order, camera focus, and dwell / hide / switch. Business modules supply target queues and content — they should not contain camera control logic.
### 9. Constants
- [constants.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/constants.js)
All material, layer, satellite, BGP, cable, terrain, celestial, and other style parameters are maintained here. Do not scatter magic numbers in module files.
## Current Style Layers
CSS files in `frontend/public/earth/css/` each correspond to a specific component scope. Do not write global Earth styles into `base.css` unless they genuinely apply to everything.
## Current Layer Toggle State Semantics
### `data-status-target`
Layer toggle buttons use `data-status-target` attributes to link button state to layer state. The state machine in `controls.js` handles:
- `loading`: showing the loading indicator
- `enabled`: layer is active
- `hidden`: layer is hidden
- `error`: layer failed to load
This is the canonical way to synchronize button visual state with actual layer state. Do not maintain separate boolean flags for button display.
## Current Settings Persistence
Earth settings are stored in `localStorage`. The key is typically a namespaced string defined in `constants.js`. `controls.js` handles read, write, and reset.
Settings that affect visual layers (terrain opacity, day/night mode, satellite display style, etc.) are read during initialization and applied immediately.
## Current Terrain Pipeline
1. `terrain.js` creates a sphere geometry with enough segments
2. On load, fetches Terrarium-format elevation tiles from the backend
3. Decodes R/G/B into elevation values
4. Displaces vertex positions radially based on elevation
5. Applies a vertex alpha that fades terrain edges at coastlines
6. Terrain writes to the scene as a mesh above the HD texture layer
When HD texture is off, terrain is temporarily hidden and its state is remembered. When HD texture comes back on, terrain restores its prior visibility.
## Current High-Frequency Risk Points
### 1. Visual State and Business State Out of Sync
The most common class of Earth bugs:
- Button shows "loaded," but layer has no objects rendered
- Button shows "hidden," but objects are still visible
- Loading ended, but button still looks like it hasn't
All future changes must prioritize checking state sync.
### 2. HUD Layout: Check Structure First, Not CSS Patches
Earth HUD has repeatedly experienced:
- Panel compressed to a sliver
- Markdown content clipped
- Tabs/iframe content consumed by `overflow: hidden`
Inspection order:
1. Who is responsible for height
2. Who is responsible for scrolling
3. Which layer is doing the clipping
Do not immediately add `overflow: hidden` or extra wrapper layers.
### 3. Transitional Paths Must Be Closed Off
Earth has gone through multiple rounds of HUD, toolbar, and media panel refactoring, making it easy to accumulate:
- Old helpers
- Old classes
- Old fallback logic
- Deprecated variants
After each major feature is complete, do a cleanup pass.
### 4. Cruise Mode and Business Events Must Not Be Deeply Coupled
The correct boundary:
- The generic cruise layer only knows:
- Current target
- Queue order
- Camera focus
- Dwell / hide / switch
- Business modules only supply:
- Target queues
- Focus coordinates
- Card content
- Highlight / layer side effects
If future cable, satellite, or news cruise is added, do not copy a new set of `main.js` state variables. Instead reuse:
- [cruise-sequencer.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/cruise-sequencer.js)
- [callout-connector.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/callout-connector.js)
- The business adapter pattern from [bgp-cruise-adapter.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/bgp-cruise-adapter.js)
## Recommended Change Approach
For future Earth changes:
1. First identify what you're changing:
- Three.js rendering layer
- HUD structure layer
- Layer state layer
- Panel content layer
2. If involving layer buttons, connect to the unified state machine
3. If involving visibility toggle, check whether tooltip / legend / info-card / lock all close together
4. If involving panel layout, check structure before touching CSS
## Current Boundary with the Console Frontend
The Earth frontend and the console frontend are not the same UI system:
- Console frontend: React + Ant Design workbench
- Earth frontend: native HUD + Three.js display under `public/earth`
Therefore:
- Earth should not directly reuse Ant Table / AppLayout semantics
- The console should not copy Earth HUD animations and glass-layer design language
For console structure, see:
- [frontend-admin-frontend-context.md](/home/ray/dev/linkong/planet/docs/technical/en/frontend-admin-frontend-context.md)

View File

@@ -0,0 +1,224 @@
# Earth Layer Style Property Index
This document records the material, color, opacity, line width, radius offset, and `renderOrder` style properties of all Earth frontend layers. For layer ordering relationships, see [earth-render-layer-order.md](/home/ray/dev/linkong/planet/docs/technical/en/earth-render-layer-order.md).
## Naming Conventions
| Category | Convention | Example |
| --- | --- | --- |
| Global config objects | `*_CONFIG` | `COUNTRY_BOUNDARY_CONFIG` |
| Layer radius offsets | `*AltitudeOffset` / `radiusOffset` | `lineAltitudeOffset`, `GRID_CONFIG.radiusOffset` |
| Opacity | `*Opacity` | `hoverLineOpacity` |
| Render order | `*RenderOrder` | `textureOverlayRenderOrder` |
| Color | `*Color`, hex number or CSS color value | `lineColor`, `colors.supercomputer` |
| Line width | `lineWidth` / `*LineWidth` | `GRID_CONFIG.lineWidth` |
## Earth Base and HD Texture
| Name | Variable | Current Value | Location / Notes |
| --- | --- | --- | --- |
| Earth base radius | `CONFIG.earthRadius` | `100` | `earth.js:createEarth()` |
| Earth base color | `EARTH_MATERIAL_CONFIG.color` | `0x010609` | `MeshPhongMaterial.color` |
| Earth base emissive | `EARTH_MATERIAL_CONFIG.emissive` | `0x010609` | `MeshPhongMaterial.emissive` |
| Earth base specular | `EARTH_MATERIAL_CONFIG.specular` | `0x1a2d45` | `MeshPhongMaterial.specular` |
| Earth base shininess | `EARTH_MATERIAL_CONFIG.shininess` | `12` | `MeshPhongMaterial.shininess` |
| Earth base opacity | `EARTH_MATERIAL_CONFIG.opacity` | `1` | `MeshPhongMaterial.opacity` |
| HD texture radius offset | `EARTH_MATERIAL_CONFIG.textureOverlayAltitudeOffset` | `0.1` | Standalone HD texture sphere radius |
| HD texture opacity | `EARTH_MATERIAL_CONFIG.textureOverlayOpacity` | `0.88` | HD texture `MeshPhongMaterial.opacity` |
| HD texture renderOrder | `EARTH_MATERIAL_CONFIG.textureOverlayRenderOrder` | `0.96` | `_earthTextureOverlay.renderOrder` |
| HD texture specular | `EARTH_MATERIAL_CONFIG.textureOverlaySpecular` | `0x05080d` | Reduces specular highlight in direct-light areas to avoid blown-out texture |
| HD texture shininess | `EARTH_MATERIAL_CONFIG.textureOverlayShininess` | `4` | Reduces specular concentration |
| HD texture color multiplier | inline | `0xffffff` | `_earthTextureOverlayMaterial.color` |
## Earth Occluder and Day/Night
| Name | Variable | Current Value | Location / Notes |
| --- | --- | --- | --- |
| Occluder radius factor | `EARTH_MATERIAL_CONFIG.occluderRadiusFactor` | `0.999` | Depth occluder sphere radius |
| Occluder segments | `EARTH_MATERIAL_CONFIG.occluderSegments` | `48` | Occluder geometry segments |
| Occluder renderOrder | inline | `-1` | `occluder.renderOrder` |
| Day/night sun direction | `EARTH_MATERIAL_CONFIG.dayNight.sunDirection` | `{ x: 1, y: 0.2, z: 0.4 }` | Custom day/night shader |
| Night-side minimum brightness | `EARTH_MATERIAL_CONFIG.dayNight.nightFloor` | `0.24` | Shader uniform |
| Day-side boost | `EARTH_MATERIAL_CONFIG.dayNight.dayBoost` | `1.12` | Shader uniform |
| Twilight width | `EARTH_MATERIAL_CONFIG.dayNight.twilightWidth` | `0.2` | Shader uniform |
| Twilight intensity | `EARTH_MATERIAL_CONFIG.dayNight.twilightIntensity` | `0.14` | Shader uniform |
| Twilight color | `EARTH_MATERIAL_CONFIG.dayNight.twilightColor` | `0x4ea0ff` | Shader uniform |
| Night tint color | `EARTH_MATERIAL_CONFIG.dayNight.nightTintColor` | `0x0b1830` | Shader uniform |
| Night tint intensity | `EARTH_MATERIAL_CONFIG.dayNight.nightTintIntensity` | `0.08` | Shader uniform |
## Atmospheric Glow and Clouds
| Name | Variable | Current Value | Location / Notes |
| --- | --- | --- | --- |
| Inner atmosphere radius factor | `EARTH_MATERIAL_CONFIG.atmosInnerRadiusFactor` | `1.01` | `atmosInnerGeo` |
| Inner atmosphere segments | `EARTH_MATERIAL_CONFIG.atmosInnerSegments` | `64` | `atmosInnerGeo` |
| Inner atmosphere color | `EARTH_MATERIAL_CONFIG.atmosInnerColor` | `[0.25, 0.62, 1.0]` | Shader RGB |
| Inner atmosphere rim power | `EARTH_MATERIAL_CONFIG.atmosInnerRimPower` | `3.2` | Shader rim falloff |
| Inner atmosphere intensity | `EARTH_MATERIAL_CONFIG.atmosInnerIntensity` | `0.18` | Shader alpha multiplier |
| Outer atmosphere radius factor | `EARTH_MATERIAL_CONFIG.atmosOuterRadiusFactor` | `1.016` | `atmosOuterGeo` |
| Outer atmosphere segments | `EARTH_MATERIAL_CONFIG.atmosOuterSegments` | `48` | `atmosOuterGeo` |
| Outer atmosphere color | `EARTH_MATERIAL_CONFIG.atmosOuterColor` | `[0.18, 0.45, 0.9]` | Shader RGB |
| Outer atmosphere rim power | `EARTH_MATERIAL_CONFIG.atmosOuterRimPower` | `5.0` | Shader rim falloff |
| Outer atmosphere intensity | `EARTH_MATERIAL_CONFIG.atmosOuterIntensity` | `0.02` | Shader alpha multiplier |
| Atmosphere blending | inline | `THREE.AdditiveBlending` | `ShaderMaterial.blending` |
| Atmosphere renderOrder | inline | `1` | `atmosInner/Outer.renderOrder` |
| No-HD-texture rim glow color | `EARTH_MATERIAL_CONFIG.rimGlowColor` | `[0.35, 0.65, 1.0]` | Fresnel shell RGB when HD texture is hidden or unavailable |
| No-HD-texture rim glow power | `EARTH_MATERIAL_CONFIG.rimGlowPower` | `3.8` | Shader rim falloff; higher = narrower edge |
| No-HD-texture rim glow intensity | `EARTH_MATERIAL_CONFIG.rimGlowIntensity` | `0.28` | Shader alpha multiplier |
| No-HD-texture rim glow segments | `EARTH_MATERIAL_CONFIG.rimGlowSegments` | `64` | `earth-rim-glow` geometry segments |
| Cloud layer radius offset | `CLOUD_LAYER_CONFIG.radiusOffset` | `3` | Cloud sphere radius |
| Cloud layer segments | `CLOUD_LAYER_CONFIG.widthSegments / heightSegments` | `64 / 64` | Cloud sphere geometry |
| Cloud layer opacity | `CLOUD_LAYER_CONFIG.opacity` | `0.15` | `MeshPhongMaterial.opacity` |
| Cloud texture | `CLOUD_LAYER_CONFIG.textureUrl` | `"./assets/earth_clouds_1024.png"` | Cloud texture map |
| Cloud blending | inline | `THREE.AdditiveBlending` | `MeshPhongMaterial.blending` |
## Land/Ocean Base and Country Borders
| Name | Variable | Current Value | Location / Notes |
| --- | --- | --- | --- |
| Country border data path | `COUNTRY_BOUNDARY_CONFIG.dataPath` | `"/earth/data/countries-admin0.min.geojson"` | GeoJSON input |
| Ocean fill color | local `OCEAN_HEX` | `0x010609` | Land/ocean base canvas background |
| Land fill color | `COUNTRY_BOUNDARY_CONFIG.landColor` | `0x080f1b` | Land/ocean base canvas land |
| Land/ocean base opacity | `COUNTRY_BOUNDARY_CONFIG.landOpacity` | `1.0` | `MeshBasicMaterial.opacity` |
| Land/ocean base radius offset | `COUNTRY_BOUNDARY_CONFIG.landAltitudeOffset` | `0.08` | `country-land-ocean` radius |
| Land/ocean base renderOrder | `COUNTRY_BOUNDARY_CONFIG.landRenderOrder` | `0.86` | `country-land-ocean.renderOrder` |
| Land/ocean mask size | `landMaskWidth / landMaskHeight` | `2048 / 1024` | Canvas / DataTexture size |
| Country tint color | `COUNTRY_BOUNDARY_CONFIG.tintColor` | `0x0b1830` | Tint when HD texture is off |
| Country tint radius offset | `COUNTRY_BOUNDARY_CONFIG.tintAltitudeOffset` | `0.04` | `country-tint` radius |
| Country tint renderOrder | `COUNTRY_BOUNDARY_CONFIG.tintRenderOrder` | `0.2` | `country-tint.renderOrder` |
| Border line color | `COUNTRY_BOUNDARY_CONFIG.lineColor` | `0x7fc7ff` | Normal border line |
| Border line opacity | `COUNTRY_BOUNDARY_CONFIG.lineOpacity` | `0.58` | Normal border line opacity |
| Border dimmed opacity on hover | `COUNTRY_BOUNDARY_CONFIG.dimmedLineOpacity` | `0.18` | Normal border opacity during hover |
| Border line radius offset | `COUNTRY_BOUNDARY_CONFIG.lineAltitudeOffset` | `0.24` | Normal border line radius |
| Border line renderOrder | `COUNTRY_BOUNDARY_CONFIG.lineRenderOrder` | `2.2` | Normal border line level |
| Border hover color | `COUNTRY_BOUNDARY_CONFIG.hoverLineColor` | `0xff3b1f` | Neon red-orange |
| Border hover opacity | `COUNTRY_BOUNDARY_CONFIG.hoverLineOpacity` | `1.0` | Hover line opacity |
| Border hover radius offset | `COUNTRY_BOUNDARY_CONFIG.hoverAltitudeOffset` | `0.32` | Hover line radius |
| Border hover renderOrder | `COUNTRY_BOUNDARY_CONFIG.hoverLineRenderOrder` | `2.3` | Hover line level |
| Border hover glow opacity | `COUNTRY_BOUNDARY_CONFIG.hoverGlowOpacity` | `0.38` | Glow line opacity |
| Border hover glow line width | `COUNTRY_BOUNDARY_CONFIG.hoverGlowLineWidth` | `3` | Glow `LineBasicMaterial.linewidth` |
| Border hover glow level offset | `COUNTRY_BOUNDARY_CONFIG.hoverGlowRenderOrderOffset` | `0.01` | Glow renderOrder = `2.29` |
| Border hover glow radius offset | `COUNTRY_BOUNDARY_CONFIG.hoverGlowRadiusOffset` | `0.04` | Glow radius = hover radius + 0.04 |
## Real Terrain
| Name | Variable | Current Value | Location / Notes |
| --- | --- | --- | --- |
| Terrain tile size | `TERRAIN_CONFIG.tileSize` | `256` | Terrarium tile read size |
| Terrain base zoom | `TERRAIN_CONFIG.baseZoom` | `4` | Terrain sampling zoom |
| Terrain geometry segments | `geometryWidthSegments / geometryHeightSegments` | `320 / 320` | Terrain sphere geometry |
| Terrain base radius offset | `TERRAIN_CONFIG.baseRadiusOffset` | `0.16` | Terrain overlays HD texture |
| Terrain exaggeration | `TERRAIN_CONFIG.exaggeration` | `34` | Elevation to world units |
| Terrain land fade height | `TERRAIN_CONFIG.landRevealFadeMeters` | `220` | Vertex alpha for coastline fade |
| Terrain opacity | `TERRAIN_CONFIG.opacity` | `0.68` | `MeshPhongMaterial.opacity` |
| Terrain color | `TERRAIN_CONFIG.color` | `0x8aa884` | `MeshPhongMaterial.color` |
| Terrain emissive | `TERRAIN_CONFIG.emissive` | `0x030704` | Reduces self-emission to preserve terrain shading |
| Terrain specular | `TERRAIN_CONFIG.specular` | `0x344438` | Gives terrain local sheen without boosting HD texture brightness |
| Terrain shininess | `TERRAIN_CONFIG.shininess` | `16` | Tightens terrain highlight |
| Terrain renderOrder | inline | `1.2` | `terrain.renderOrder` |
| Terrain polygonOffset | inline | `factor -1`, `units -1` | Reduces z-fighting near sphere surface |
## Grid Lines
| Name | Variable | Current Value | Location / Notes |
| --- | --- | --- | --- |
| Grid radius offset | `GRID_CONFIG.radiusOffset` | `0.14` | Grid sphere radius |
| Grid color | `GRID_CONFIG.color` | `0xc0e0ff` | `LineBasicMaterial.color` |
| Grid opacity | `GRID_CONFIG.opacity` | `0.08` | `LineBasicMaterial.opacity` |
| Grid line width | `GRID_CONFIG.lineWidth` | `1` | `LineBasicMaterial.linewidth` |
| Grid renderOrder | `GRID_CONFIG.renderOrder` | `2.05` | Grid level |
| Latitude step | `GRID_CONFIG.latitudeStep` | `15` | Latitude line generation step |
| Longitude step | `GRID_CONFIG.longitudeStep` | `30` | Longitude line generation step |
| Segment sample step | `GRID_CONFIG.segmentStep` | `5` | Grid line sample step |
## Submarine Cables and Landing Points
| Name | Variable | Current Value | Location / Notes |
| --- | --- | --- | --- |
| Default cable color | `CABLE_COLORS.default` | `0xffff44` | Used when no data color available |
| Cable radius offset | `CABLE_CONFIG.line.altitudeOffset` | `0.2` | Cable line radius |
| Cable line width | `CABLE_CONFIG.line.lineWidth` | `1` | `LineBasicMaterial.linewidth` |
| Cable opacity | `CABLE_CONFIG.line.opacity` | `1.0` | Cable line opacity |
| Cable renderOrder | `CABLE_CONFIG.line.renderOrder` | `1` | Cable line level |
| Landing point radius offset | `CABLE_CONFIG.landingPoint.altitudeOffset` | `0.48` | Aligns with compute center marker height |
| Landing point icon texture size | `CABLE_CONFIG.landingPoint.textureSize` | `256` | Canvas size for solid map-pin icon |
| Landing point icon aspect ratio | `CABLE_CONFIG.landingPoint.iconAspectRatio` | `0.82` | `Sprite.scale.x = height * aspect` |
| Landing point icon anchor | `CABLE_CONFIG.landingPoint.anchorX / anchorY` | `0.52 / 0.276` | `Sprite.center`, aligns pin tip to landing point lat/lon |
| Landing point base scale | `CABLE_CONFIG.landingPoint.baseScale` | `12` | Matches compute center sprite height |
| Landing point color | `CABLE_CONFIG.landingPoint.color` | `0xffaa00` | `SpriteMaterial.color` |
| Landing point opacity | `CABLE_CONFIG.landingPoint.opacity` | `1.0` | `SpriteMaterial.opacity` |
| Landing point renderOrder | `CABLE_CONFIG.landingPoint.renderOrder` | `4.5` | Aligns with compute center surface level |
| Landing point dim brightness | `landingPointVisual.dimBrightness` | `0.62` | Dim state color multiplier |
| Dimmed landing point color | `landingPointVisual.dimmed.colorRGB` | `{ r: 180, g: 116, b: 28 }` | Dim state color; avoids dark base showing through as a dark hole |
| Dimmed landing point emissive | `landingPointVisual.dimmed.emissive` | `0x3a2200` | Dim state weak amber self-emission |
| Dimmed landing point opacity | `landingPointVisual.dimmed.opacity` | `0.78` | Dim state opacity; no longer uses low alpha blending with dark base |
## Satellites, Trails, and Footprints
| Name | Variable | Current Value | Location / Notes |
| --- | --- | --- | --- |
| Satellite display radius offset | `SATELLITE_CONFIG.displayAltitudeOffset` | `8` | Satellite point position |
| Satellite dot base pixel size | `SATELLITE_CONFIG.dotBaseSize` | `2.8` | Point shader size |
| Satellite backdrop dot scale | `SATELLITE_CONFIG.dotBackdropScale` | `1.28` | Backdrop dot size |
| Satellite dot opacity range | `dotOpacityMin / dotOpacityMax` | `0.7 / 1.0` | Breathing animation |
| Satellite dot breathing speed | `SATELLITE_CONFIG.dotBreathingSpeed` | `0.12` | Dot opacity animation |
| Satellite backdrop renderOrder | inline | `5` | `satelliteBackdropPoints.renderOrder` |
| Satellite dot renderOrder | inline | `6` | `satellitePoints.renderOrder` |
| Satellite trail length | `SATELLITE_CONFIG.trailLength` | `10` | Trail buffer |
| Satellite trail line width | `SATELLITE_CONFIG.trailLineWidth` | `3` | Ribbon shader uniform |
| Selected ring size | `SATELLITE_CONFIG.ringSize` | `0.07` | Hover / locked ring sprite |
| Satellite overlay renderOrder | `SATELLITE_CONFIG.overlayRenderOrder` | `12` | Locked ring / halo / orbit |
| Footprint renderOrder | local `GROUND_FOOTPRINT_RENDER_ORDER` | `3` | Footprint fill |
## Compute Centers
| Name | Variable | Current Value | Location / Notes |
| --- | --- | --- | --- |
| Compute center radius offset | `COMPUTE_CENTER_CONFIG.altitudeOffset` | `0.48` | Marker position |
| Compute center base opacity | `COMPUTE_CENTER_CONFIG.marker.baseOpacity` | `0.88` | `SpriteMaterial.opacity` |
| Supercomputer marker scale | `COMPUTE_CENTER_CONFIG.marker.supercomputerScale` | `12` | Supercomputer marker |
| GPU cluster marker scale | `COMPUTE_CENTER_CONFIG.marker.gpuClusterScale` | `12` | GPU marker |
| Hover scale | `COMPUTE_CENTER_CONFIG.marker.hoverScale` | `1.16` | Hover state |
| Locked scale | `COMPUTE_CENTER_CONFIG.marker.lockedScale` | `1.22` | Locked state |
| Dimmed scale / opacity | `dimmedScale / dimmedOpacity` | `0.82 / 0.34` | Dim state |
| Supercomputer color | `COMPUTE_CENTER_CONFIG.colors.supercomputer` | `"#38bdf8"` | Marker texture |
| GPU cluster color | `COMPUTE_CENTER_CONFIG.colors.gpu_cluster` | `"#2dd4bf"` | Marker texture |
| Linked color | `COMPUTE_CENTER_CONFIG.colors.linked` | `"#f8fafc"` | Linked state |
| Compute center renderOrder | local `COMPUTE_CENTER_RENDER_ORDER` | `4.5` | Surface facility below satellites |
## BGP Observation
| Name | Variable | Current Value | Location / Notes |
| --- | --- | --- | --- |
| BGP event radius offset | `BGP_CONFIG.altitudeOffset` | `2.1` | Anomaly marker |
| BGP collector radius offset | `BGP_CONFIG.collectorAltitudeOffset` | `1.6` | Collector marker |
| Event base scale | `BGP_CONFIG.marker.eventBaseScale` | `6.2` | Anomaly sprite |
| Collector base scale | `BGP_CONFIG.marker.collectorBaseScale` | `7.4` | Collector plane |
| Hover / dim scale | `hoverScale / dimmedScale` | `1.16 / 0.92` | Interaction states |
| Normal event opacity | `BGP_CONFIG.opacity.normal` | `0.78` | Anomaly sprite |
| Hover opacity | `BGP_CONFIG.opacity.hover` | `1.0` | Hover state |
| Dimmed opacity | `BGP_CONFIG.opacity.dimmed` | `0.24` | Dim state |
| Collector opacity | `BGP_CONFIG.opacity.collector` | `0.62` | Collector state |
| Critical color | `BGP_CONFIG.severityColors.critical` | `0xff4d4f` | Critical event |
| High color | `BGP_CONFIG.severityColors.high` | `0xff9f43` | High-severity event |
| Medium color | `BGP_CONFIG.severityColors.medium` | `0xffd166` | Medium-severity event |
| Low color | `BGP_CONFIG.severityColors.low` | `0x4dabf7` | Low-severity event |
| Collector base color | `BGP_CONFIG.collectorColor` | `0x6db7ff` | Default collector color |
| Region color | `BGP_CONFIG.regionColor` | `0x2dd4bf` | Region overlay |
## Celestial and Starfield
| Name | Variable | Current Value | Location / Notes |
| --- | --- | --- | --- |
| Sky sphere radius | `CELESTIAL_CONFIG.skyRadius` | `2600` | Celestial background |
| Sky opacity | `CELESTIAL_CONFIG.skyOpacity` | `1` | Background material |
| Sun distance / scale | `sunDistance / sunScale` | `2150 / 78` | Sun sprite |
| Moon distance / scale | `moonDistance / moonScale` | `2050 / 38` | Moon sprite |
| Sun halo scale | `CELESTIAL_CONFIG.sunHaloScale` | `136` | Sun halo |
| Moon halo scale | `CELESTIAL_CONFIG.moonHaloScale` | `62` | Moon halo |
| Sun light color / intensity | `sunLightColor / sunLightIntensity` | `0xfff4df / 1.02` | Scene light |
| Back light color / intensity | `backLightColor / backLightIntensity` | `0x2b4c78 / 0.3` | Scene light |
| Star count | `STARFIELD_CONFIG.count` | `8000` | `createStars()` |
| Star radius range | `minRadius + radiusJitter` | `800 + 200` | Random distribution |
| Star color | `STARFIELD_CONFIG.color` | `0xffffff` | `PointsMaterial.color` |
| Star size | `STARFIELD_CONFIG.size` | `0.5` | `PointsMaterial.size` |

View File

@@ -0,0 +1,175 @@
# News Live Streams Collector Format
The `news_live_streams` collector accepts a "channel directory JSON" as input rather than scraping web pages directly.
Goals:
- Allow the backend to stably ingest live news streams from around the world
- Ensure the Earth page TV module always consumes a consistent structure
- Make it easy to integrate channel directories like `worldmonitor` that mix YouTube / HLS / iframe sources
## Recommended JSON Structure
```json
{
"sources": [
{
"id": "bbc-world-news",
"name": "BBC World News",
"provider": "BBC",
"region": "UK",
"language": "en",
"source_type": "youtube",
"youtube_video_id": "dQw4w9WgXcQ",
"youtube_channel": "https://www.youtube.com/@BBCNews",
"embed_url": "",
"stream_url": "",
"homepage_url": "https://www.youtube.com/@BBCNews/live",
"poster_url": "",
"sort_order": 220,
"is_enabled": true,
"notes": "Primary English global news channel"
},
{
"id": "france24-en",
"name": "France 24 English",
"provider": "France 24",
"region": "France",
"language": "en",
"source_type": "hls",
"stream_url": "https://example.com/live.m3u8",
"homepage_url": "https://www.france24.com/en/live",
"sort_order": 230,
"is_enabled": true
},
{
"id": "cctv4-page",
"name": "CCTV-4 Chinese International",
"provider": "CCTV",
"region": "China",
"language": "zh-CN",
"source_type": "iframe",
"embed_url": "https://tv.cctv.com/live/cctv4/",
"homepage_url": "https://tv.cctv.com/live/cctv4/",
"sort_order": 10,
"is_enabled": true
}
]
}
```
## Field Conventions
- `id`: unique identifier, should be stable
- `name`: channel display name
- `provider`: provider name
- `region`: country or region
- `language`: language code
- `source_type`: `iframe` / `hls` / `video` / `external` / `youtube`
- `embed_url`: page suitable for iframe embedding
- `stream_url`: direct video stream URL
- `homepage_url`: official website or channel page
- `youtube_video_id`: YouTube live video ID
- `youtube_channel`: YouTube channel handle or channel URL
- `poster_url`: cover image, optional
- `sort_order`: sort value, smaller = higher in the list
- `is_enabled`: whether enabled
- `notes`: brief notes
## Panel Behavior Conventions
- `youtube`
- Prefers `youtube_video_id`
- When embedding is not possible, at least keep `youtube_channel` or `homepage_url` for external opening
- `hls` / `video`
- Prefers `stream_url`
- `iframe`
- Prefers `embed_url`
- `external`
- No embedding attempt; only keeps external open link
## Current Implementation Status
- The backend settings page supports manually maintaining channel directories
- The Earth TV module merges:
- Manually configured sources
- Sources collected by the `news_live_streams` collector
- The current default fallback source is CCTV-4 Chinese International
- When no override is configured, `news_live_streams` defaults to `iptv-org`:
- `channels.json`
- `streams.json`
- `logos.json`
and automatically filters for news-category channel directories
## Collector Configuration
`news_live_streams` does not need a separate new page; it reuses the existing data source configuration:
- `endpoint`
- Channel directory JSON API URL
- `auth_type`
- `none` / `bearer` / `api_key` / `basic`
- `headers`
- Additional request headers
- `config`
- Collector request and parsing behavior
### Supported `config` Fields
```json
{
"timeout": 30,
"method": "GET",
"params": {
"region": "global"
},
"body_type": "json",
"body": {
"include_disabled": false
},
"response_path": "payload.channels"
}
```
- `timeout`: request timeout in seconds
- `method`: `GET` or `POST`
- `params`: query parameter object
- `body_type`: `json` or `form`
- `body`: request body for `POST`
- `json_body`: explicit JSON request body, takes priority over `body`
- `form_body`: explicit form request body, takes priority over `body`
- `response_path`: path to the channel array in the response JSON, supports dot notation, e.g.:
- `payload.channels`
- `data.items`
- `result.streams`
### Authentication Details
- `bearer`: uses `Authorization: Bearer <token>`
- `api_key`: sent as request header by default; if `auth_config.in = "query"`, sent as query param
- `basic`: uses HTTP Basic Authorization
## Compatible Response Structures
The collector first tries to read:
- Top-level array
- Or an array under these common fields:
- `sources`
- `streams`
- `channels`
- `items`
- `results`
- `data`
It also accepts these field aliases:
- `id` / `source_id` / `slug` / `channel_id` / `code`
- `name` / `title` / `channel` / `display_name`
- `provider` / `publisher` / `network`
- `stream_url` / `stream` / `playback_url` / `hls_url` / `m3u8_url`
- `embed_url` / `embed` / `page_url`
- `homepage_url` / `source_url` / `website`
- `language` / `lang` / `locale`
- `youtube_video_id` / `video_id`
- `youtube_channel` / `channel_handle`

View File

@@ -0,0 +1,56 @@
# Earth Render Layer Order
This document records the current Earth renderer's layer order and the intent of each layer. When adjusting `renderOrder`, radius offsets, depth strategy, or pointer interaction, update this document accordingly.
Note: the layer control panel order and the registration / startup load order are two separate semantics.
| Order type | Current sequence | Notes |
| --- | --- | --- |
| Control panel order | Cables → Trails → Satellites → Compute Centers → BGP → Terrain → HD Texture → Cloud Layer → Borders → Grid | Controlled by `displayOrder`, sorted by operational relevance. |
| Registration / startup load order | Grid → Borders → HD Texture → Cloud Layer → Cables → Compute Centers → BGP → Satellites | Controlled by registration order and `startupPriority`, sorted surface-to-sky; Trails and Terrain are dependency/optional display layers and do not participate in normal startup data loading. |
## Surface Layer Stack
| Order | Layer | Source | Render / Radius Strategy | Depth / Interaction Strategy | Notes |
| --- | --- | --- | --- | --- | --- |
| -1000 | Celestial background mesh | `celestial.js` | Background sphere | Not part of surface picking | Behind all Earth content. |
| -1 | Earth occluder sphere | `earth.js` | Invisible inner sphere | Writes depth buffer | Occludes objects behind the Earth. |
| 0 | Earth base sphere | `earth.js` | `CONFIG.earthRadius` | Surface picking fallback target | Dark base; still visible when all optional map layers are off. |
| 0.2 | Country dark tint | `country-boundaries.js` | `tintAltitudeOffset` | Raycast disabled | Used when HD texture is off. |
| 0.86 | Land/ocean base fill | `country-boundaries.js` | `landAltitudeOffset`; ocean `#010609`, land `#080f1b` | Raycast disabled | Base map remains usable even when country borders are off. |
| 0.96 | HD Earth texture | `earth.js` | `textureOverlayAltitudeOffset` | Surface picking target when visible | HD texture always overlays the land/ocean base fill. |
| 1 | Atmospheric glow and clouds | `earth.js` | Atmosphere / cloud spheres | Not in normal object selection path | Cloud layer controlled by the "Cloud Layer" toggle. |
| 1 | Submarine cables | `cables.js` | `CABLE_CONFIG.line.renderOrder` | Cable picking path | Preserves existing cable layer level. |
| 1.2 | Real terrain | `earth.js`, `terrain.js` | `TERRAIN_CONFIG.baseRadiusOffset` plus terrain displacement | Raycast disabled | Terrain overlays HD texture; temporarily hidden when HD texture is off, restores to prior state when re-enabled. |
| 2.05 | Grid lines | `earth.js` | `CONFIG.earthRadius + 0.14` | Raycast disabled | Low-opacity lines over HD texture. |
| 2.2 | Country borders | `country-boundaries.js` | `lineAltitudeOffset` | Raycast disabled | Only needs to stay above HD texture. |
| 2.29 | Country border hover glow | `country-boundaries.js` | Hover radius + glow offset | `depthTest: false`, raycast disabled | Additive glow to reinforce border edge and terrain hover visibility. |
| 2.3 | Country border hover line | `country-boundaries.js` | `hoverAltitudeOffset` | `depthTest: false`, raycast disabled | Neon red-orange hover line; China and Taiwan share the same highlight group. |
| 3 | Satellite footprint fill | `satellites.js` | `GROUND_FOOTPRINT_RENDER_ORDER` | depth-tested, Group renderOrder stays 0 | Footprint above country borders, below compute centers and satellites. |
| 3-5 | BGP markers and overlays | `bgp.js` | Each marker's own renderOrder | BGP picking path | Preserves existing BGP visual level. |
| 4.5 | Compute centers | `compute-centers.js` | `COMPUTE_CENTER_RENDER_ORDER` | Compute center picking path | Surface facilities, below satellites. |
| 5 | Satellite background dot | `satellites.js` | Fixed renderOrder | Screen-space satellite picking | Below satellite dots. |
| 6 | Satellite dots | `satellites.js` | Fixed renderOrder | Screen-space satellite picking | Satellite dots above footprints and compute centers. |
| 12+ | Satellite locked ring, halo, predicted orbit | `satellites.js` | `SATELLITE_CONFIG.overlayRenderOrder` and offsets | Satellite overlay path | Used for selected/locked satellite emphasis. |
| 98-100 | Sun / moon halo and sprite | `celestial.js` | Fixed renderOrder | Celestial picking disabled | Foreground celestial sprites. |
## Toggle Behavior
| Toggle | Behavior |
| --- | --- |
| HD texture off | Hides HD texture, enables country tint / base surface, disables terrain and day/night toggle interaction, and remembers terrain and day/night previous states. |
| HD texture on | Restores HD texture and the remembered terrain / day/night states. |
| Terrain on | Displayed above HD texture, but below country border hover, footprints, satellites, and other emphasis layers. |
| Cloud layer | Only controls cloud mesh visibility. |
| Country borders | Controls border line and hover line visibility; land/ocean base fill exists independently as the Earth base map. |
## Interaction Rules
| Interaction | Current Rule |
| --- | --- |
| Earth coordinate hover | When HD texture is visible, uses the HD texture overlay as the surface picking target; otherwise uses the Earth base sphere. |
| Country border hover | Converts surface pick coordinates to lat/lon, then uses GeoJSON point-in-polygon; the border hover line itself does not receive raycasts. |
| Country border hover visual | On hover, dims normal border lines and draws no-depth-test glow and solid lines. |
| China / Taiwan hover | `CHN` and `TWN` are grouped in the same hover highlight group; the tooltip still shows the actually-hit feature. |
| Terrain | Acts as a visual layer only; `terrain.raycast` is disabled. |
| Satellites | Uses screen-space satellite picking to prevent footprints or surface layers from blocking satellite clicks. |

View File

@@ -0,0 +1,198 @@
# Earth Satellite Footprint Policy
This document records the current product boundary, data rationale, and implemented behavior for `footprint` in the Earth satellite layer. The goal is to prevent the Starlink-specific ground coverage model from being misapplied to other constellations.
Related context:
- [earth-frontend-context.md](/home/ray/dev/linkong/planet/docs/technical/en/earth-frontend-context.md)
- [backend-collectors.md](/home/ray/dev/linkong/planet/docs/technical/en/backend-collectors.md)
- [backend/app/services/collectors/celestrak.py](/home/ray/dev/linkong/planet/backend/app/services/collectors/celestrak.py)
- [frontend/public/earth/js/satellites.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/satellites.js)
## Current Goal
- Define which non-Starlink satellites should not show a ground footprint
- Define which constellations may have their own footprint in the future but cannot reuse the Starlink bowtie / GSO-gap model
- Solidify this policy as an executable implementation boundary, not leave it scattered across visual parameters
## Current Local Categories
Current CelesTrak satellite groups in [backend/app/services/collectors/celestrak.py](/home/ray/dev/linkong/planet/backend/app/services/collectors/celestrak.py) include:
- `starlink`
- `gps-ops`
- `galileo`
- `glonass`
- `beidou`
- `leo`
- `geo`
- `iridium-next`
Non-Starlink categories:
- `gps-ops`
- `galileo`
- `glonass`
- `beidou`
- `leo`
- `geo`
- `iridium-next`
## Research Conclusions
### 1. GNSS / RNSS: `gps-ops`, `galileo`, `glonass`, `beidou`
Do not draw a localized ground footprint by default.
Reason:
- Public sources emphasize `Earth-pointing`, `Earth coverage`, `continuous global coverage`
- The public semantic of these systems is global navigation / timing coverage, not the localized spot footprint associated with Starlink's end-user service
More appropriate representation:
- Default: show only the satellite body and orbit
- If future needs require showing "service reachability," only a weak global coverage semantic is appropriate — do not draw a localized ground spot
References:
- [GPS III EC Antenna Patterns](https://www.navcen.uscg.gov/sites/default/files/pdf/gps/GPS_ZIP/GPS_III_EC_Antenna_Patterns_SVN_74_75_76_77_78.pdf)
- [ESA Galileo satellites](https://www.esa.int/Applications/Satellite_navigation/Galileo/Galileo_satellites)
- [Navipedia Galileo General Introduction](https://gssc.esa.int/navipedia/index.php/Galileo_General_Introduction)
- [BeiDou official overview](https://www.beidou.gov.cn/xt/gfxz/201812/P020190117356387956569.pdf)
- [GPS.gov GNSS overview](https://www.gps.gov/systems/gnss/)
### 2. `iridium-next`
Can have a footprint, but cannot reuse Starlink's single bowtie footprint.
Reason:
- Iridium NEXT public documentation emphasizes a fixed multi-spot beam system
- Public examples commonly show `48 fixed spot beams in 4 tiers`
- This is not the same problem as Starlink's "single satellite, single primary footprint, with GSO gap" business visualization
More appropriate representation:
- Default: still do not draw a Starlink-style ground footprint
- Future implementation: connect an independent Iridium multi-beam adapter layer
- Visually closer to multi-beam clusters / honeycomb / layered beams, not a single bowtie spot
Reference:
- [Iridium Satellite Spot Beam Coverage on the US](https://www.mathworks.com/help/phased/ug/iridium-satellite-spot-beam-coverage-on-the-us-1.html)
### 3. `geo`
Do not draw a unified footprint by default.
Reason:
- GEO communication satellites may use global beam, zone beam, spot beam, or steerable spot beam
- Without operator / payload / beam contour metadata, drawing a unified footprint is very likely incorrect
More appropriate representation:
- Default: show only the GEO belt and satellite parking position semantics
- Only allow footprint drawing when beam contour / operator metadata is available
Reference:
- [ITU Handbook on Satellite](https://www.itu.int/dms_pub/itu-r/opb/hdb/R-HDB-42-2002-PDF-E.pdf)
### 4. `leo` (generic)
Do not draw a footprint by default.
Reason:
- The `leo` group is too mixed — it may include communication, remote sensing, experimental, and observation satellites
- Without mission / payload / antenna pattern metadata, there is no basis for a service-coverage visualization
More appropriate representation:
- Default: show only the satellite and orbit
- Future: if subdivided by operator / mission subtype, decide then whether to introduce an independent coverage mode
## Product Policy
Current unified policy:
- `Starlink`
- Keep the current dedicated `ground_footprint` logic
- `Iridium NEXT`
- Reserve an independent adapter layer
- Do not reuse Starlink footprint currently
- `GPS / Galileo / GLONASS / BeiDou`
- No ground footprint
- `GEO`
- No footprint without beam metadata
- `Generic LEO`
- No footprint without mission metadata
## Implemented Behavior
This implementation only does the minimum executable version and does not change existing Starlink visual parameters:
1. Backend passes constellation group and footprint policy hint to the frontend
- CelesTrak collector stores `GROUP` in `metadata.constellation_group`
- Visualization API outputs:
- `properties.constellation_group`
- `properties.footprint_policy`
Current policy values:
- `starlink_ground_footprint`
- `iridium_coverage_ring`
- `none`
Relevant code:
- [backend/app/services/collectors/celestrak.py](/home/ray/dev/linkong/planet/backend/app/services/collectors/celestrak.py)
- [backend/app/api/v1/visualization.py](/home/ray/dev/linkong/planet/backend/app/api/v1/visualization.py)
2. Frontend makes footprint a capability-gated renderer
- `ground_footprint` is only actually enabled when `footprint_policy === starlink_ground_footprint`
- `iridium-next` no longer falls back to a placeholder branch; it goes through an independent Iridium coverage ring adapter
- Other non-Starlink satellites automatically fall back to `self_glow` even if the user globally selects `ground_footprint`
Relevant code:
- [frontend/public/earth/js/satellites.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/satellites.js)
- [frontend/public/earth/js/iridium-footprint-adapter.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/iridium-footprint-adapter.js)
3. Satellite info card shows capability, not just orbital parameters
- Satellite details now clearly display:
- `Constellation / Group`
- `Coverage Capability`
- `Current Display`
- `Coverage Model`
- Users can directly see:
- Whether the current satellite supports footprint
- Whether the current display has been fallen back due to capability gating
- That Iridium and Starlink use different models
Relevant code:
- [frontend/public/earth/js/main.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/main.js)
- [frontend/public/earth/js/info-card.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/info-card.js)
## Current Implementation Boundary
This boundary must be maintained:
- Starlink's footprint parameters and shader logic serve Starlink only
- Non-Starlink capability decisions belong to the "policy layer / adapter layer"
- Do not re-mix different constellations' coverage models into the same parameter set
- `iridium-next` has been separated into an independent adapter and should continue along this boundary rather than adding more if/else to the existing Starlink bowtie
## Recommended Next Steps
If continuing forward, the recommended order is:
1. Create a dedicated footprint adapter for `iridium-next`
2. Add a read-only indicator in the UI to tell users whether the current satellite supports footprint
3. If GEO beam contour / operator metadata becomes available, enable operator-specific footprint for GEO

View File

@@ -0,0 +1,293 @@
# Admin Frontend Context
This document describes the current real structure of the console frontend. The goal is to help future page development, table refactoring, layout governance, and state consolidation quickly find the right entry points.
Related references:
- [rules.md](/home/ray/dev/linkong/planet/rules.md)
- [frontend-layout-guidelines.md](/home/ray/dev/linkong/planet/docs/technical/en/frontend-layout-guidelines.md)
## Current Goal
The console frontend is a backend workbench, not a display-style dashboard. Current constraints:
- Pages default to a single-screen work area
- Primary interaction happens through in-module scrolling, not relying on the whole page growing infinitely
- Lists, tables, and analysis pages prioritize keeping the main work area visible
- Common layout, scrollbar, and table scroll behavior should be reused across pages
## Current Route Entry Points
Main entry point:
- [App.tsx](/home/ray/dev/linkong/planet/frontend/src/App.tsx)
Current admin-related routes:
- `/admin`
- `/users`
- `/datasources`
- `/data`
- `/alerts/system`
- `/alerts/bgp`
- `/alerts/situational`
- `/bgp`
- `/playground`
- `/settings`
`/earth` is a standalone display page and is not part of the console shell.
## Current Page Shell
The console shared shell is at:
- [AppLayout.tsx](/home/ray/dev/linkong/planet/frontend/src/components/AppLayout/AppLayout.tsx)
Responsibilities:
- Left-side navigation
- Collapse and expand
- Current account / version information
- Content area height closure
- Site-wide unified sidebar scrollbar
Current structure:
```tsx
<Layout className="dashboard-layout">
<Sider className="dashboard-sider">...</Sider>
<Layout>
<Content className="dashboard-content">
<div className="dashboard-content-inner">{children}</div>
</Content>
</Layout>
</Layout>
```
Future console pages should adapt to this shell rather than redefining full-page height semantics.
## Current Shared Components
### 1. `Scrollbar`
File:
- [Scrollbar.tsx](/home/ray/dev/linkong/planet/frontend/src/components/Scrollbar/Scrollbar.tsx)
Purpose:
- Ordinary content containers like the console sidebar
- Internally manages visibility, thumb size, drag, and dual-axis overflow detection
Current constraint:
- The scrollbar must be a floating overlay that does not participate in layout
- Should leave no visible trace when there is no overflow
- Real scrolling is still handled by the native container; only the visible layer and interaction layer are replaced
### 2. `ScrollbarOverlay`
File:
- [ScrollbarOverlay.tsx](/home/ray/dev/linkong/planet/frontend/src/components/Scrollbar/ScrollbarOverlay.tsx)
Purpose:
- Areas like Ant Table that already have an internal scroll container
- Does not take over scroll semantics; only adds a new scrollbar visible layer
Current usage:
- Data sources
- Collected data
- User management
- Settings page
- Alerts page
- BGP page
### 3. `TableScrollRegion`
File:
- [TableScrollRegion.tsx](/home/ray/dev/linkong/planet/frontend/src/components/Scrollbar/TableScrollRegion.tsx)
Purpose:
- Provides a unified wrapper for table scroll areas
- New table pages should reuse this rather than repeating the "table area + overlay scrollbar" boilerplate
### 4. `SegmentedControl`
Files:
- [SegmentedControl.tsx](/home/ray/dev/linkong/planet/frontend/src/components/SegmentedControl/SegmentedControl.tsx)
- [SegmentedControl.css](/home/ray/dev/linkong/planet/frontend/src/components/SegmentedControl/SegmentedControl.css)
Purpose:
- Segmented controls for language, theme, mode, or other 2 to 3 option settings
- Settings that need the shared animated slider, active state, and compact button layout
- The `/docs` footer language switcher and theme switcher already reuse it
Interface semantics:
- `options`: each option contains `value` and `label`, with optional `icon` and `title`
- `value`: current active value
- `onChange`: called when the selected option changes
- `ariaLabel`: accessible name for the control
- `className`: page-level hook for size or local style overrides
Current constraints:
- The component owns slider count, position, and spring-like transition
- Feature pages should only pass options and state, not recreate private slider DOM
- Prefer CSS variable overrides for colors instead of hard-coding theme colors in feature components
- Best for a small set of mutually exclusive choices; do not use it as a long list, navigation menu, or select replacement
### 5. `MarkdownRenderer`
File:
- [MarkdownRenderer.tsx](/home/ray/dev/linkong/planet/frontend/src/components/MarkdownRenderer/MarkdownRenderer.tsx)
Purpose:
- Renders Markdown content for `/docs`
- Supports headings, lists, blockquotes, code blocks, tables, and basic inline formatting
- Code blocks and tables reuse `Scrollbar` so horizontal content does not blow out the docs page
Current constraints:
- It is not a full GitHub Markdown engine; it only covers the syntax currently needed by project docs
- Internal document links should be converted to `/docs/:slug` through `transformLink`
- Heading anchors are injected through `getHeadingId`, keeping route state outside the renderer
### 6. `TableActions`
File:
- [TableActions.tsx](/home/ray/dev/linkong/planet/frontend/src/components/TableActions/TableActions.tsx)
Purpose:
- Shared action entry for table operation columns
- Shows inline actions when expanded
- Uses a more-actions dropdown when collapsed
Companion export:
- `actionCellProps`: for action-column `onCell`, preventing action buttons from being ellipsized or wrapped
## Current State Sources
### 1. Auth State
File:
- [auth.ts](/home/ray/dev/linkong/planet/frontend/src/stores/auth.ts)
Responsibilities:
- Token
- Current user
- Login / logout
`App.tsx` uses it to decide whether to redirect to the login page.
### 2. Business Data Gateway
AI / situational awareness related services are currently in:
- [http-gateway.ts](/home/ray/dev/linkong/planet/frontend/src/services/situational-awareness/http-gateway.ts)
- [port.ts](/home/ray/dev/linkong/planet/frontend/src/services/situational-awareness/port.ts)
- [types.ts](/home/ray/dev/linkong/planet/frontend/src/services/situational-awareness/types.ts)
Constraints:
- Pages must not scatter URL construction directly
- Define boundaries through port/types first
- Then implement via http/mock gateway
## Current Page Layer Recommendations
### 1. Dashboard and Summary Pages
Example:
- [Dashboard.tsx](/home/ray/dev/linkong/planet/frontend/src/pages/Dashboard/Dashboard.tsx)
Priority goals:
- Stable header
- Summary cards compact first
- Main work area occupies primary height
### 2. Table Pages
Examples:
- [DataSources.tsx](/home/ray/dev/linkong/planet/frontend/src/pages/DataSources/DataSources.tsx)
- [DataList.tsx](/home/ray/dev/linkong/planet/frontend/src/pages/DataList/DataList.tsx)
- [Users.tsx](/home/ray/dev/linkong/planet/frontend/src/pages/Users/Users.tsx)
- [Settings.tsx](/home/ray/dev/linkong/planet/frontend/src/pages/Settings/Settings.tsx)
Constraints:
- Prefer internal scrolling
- Do not let tables blow out the full page
- New table areas should reuse `TableScrollRegion` / `ScrollbarOverlay`
### 3. Complex Workspace Pages
Examples:
- [BGP.tsx](/home/ray/dev/linkong/planet/frontend/src/pages/BGP/BGP.tsx)
- [Playground.tsx](/home/ray/dev/linkong/planet/frontend/src/pages/Playground/Playground.tsx)
Constraints:
- Tab content must not share the same height logic
- Table tabs, Markdown tabs, and config tabs each need their own scroll responsibility
- AI result areas and long text areas should maintain a minimum readable height
## Current Layout Constraints
These principles have been repeatedly validated in the project:
1. Parent container height chain must close
2. `min-height: 0` must not be omitted
3. Overflow responsibility must be explicit
4. Do not use `overflow: hidden` to mask structural issues
5. Do not compress the main work area to make summary cards show completely
6. Custom scrollbars must be floating overlays; they must not squeeze content width
For detailed experience, see:
- [frontend-layout-guidelines.md](/home/ray/dev/linkong/planet/docs/technical/en/frontend-layout-guidelines.md)
## Recommended Change Approach
For future console page changes:
1. Confirm whether the page is a summary page, table page, or complex workspace
2. Integrate into the existing shell and scroll semantics first
3. Reuse shared scroll components
4. Handle visual and detail interactions last
Do not write local CSS patches first, then retrofit the structure.
## Current Clear Boundary
The console frontend and the Earth frontend are not the same system:
- Console frontend: React + Ant Design workbench
- Earth frontend: independent native HUD system under `public/earth`
Therefore:
- Do not move Earth's HUD / animations / state machine directly into the console
- Do not force the console's table / scroll strategy onto the Earth HUD
For Earth-related structure, see:
- [earth-frontend-context.md](/home/ray/dev/linkong/planet/docs/technical/en/earth-frontend-context.md)

View File

@@ -0,0 +1,309 @@
# Frontend Layout Guidelines
Admin pages in this project default to a "single-screen workspace" layout standard. The goal is not to prevent all overflow, but to ensure that under common desktop viewports:
- The main page structure is visible within one screen
- The user can simultaneously see the page header, summary area, and main workspace
- Overflow content scrolls within its module, rather than stretching the entire page vertically
Current recommended reference implementations:
- [frontend/src/pages/BGP/BGP.tsx](/home/ray/dev/linkong/planet/frontend/src/pages/BGP/BGP.tsx)
- [frontend/src/index.css](/home/ray/dev/linkong/planet/frontend/src/index.css)
## Core Principles
### 1. Pages Should Prioritize a Single-Screen Workspace
Admin pages default to:
- Header: title, description, main actions
- Main workspace: stats cards, tables, charts, lists, tabs
Recommended structure:
```tsx
<AppLayout>
<div className="page-shell">
<div className="page-shell__header">...</div>
<div className="page-shell__body">...</div>
</div>
</AppLayout>
```
Total page height should be bounded within the `AppLayout` content area, not allowed to grow naturally downward without limit.
### 2. Scrolling Should Happen Inside Modules
If tables, logs, long lists, or chart details overflow their space:
- Let the card scroll internally
- Let the table scroll internally
- Let the tab content area scroll internally
Do not rely on full-page scrolling to "solve" the space problem.
### 3. The Main Workspace Must Get the Most Space
The most important module on a page must be the visual and spatial lead. Typically ensure:
- Header always visible
- Summary area height controlled
- Main table / chart / analysis area occupies more than 50% of visible height
If a page has multiple large modules, priority order is:
1. First compress the description and summary areas
2. Then move secondary modules into tabs or switch views
3. Only then consider adding more full-page scrolling
### 4. Small Screens and High Zoom Must Enter Compact Mode
When window height is low, width is narrow, or system zoom is high, actively switch to a compact layout:
- Reduce card padding
- Reduce header and cell spacing
- Convert summary area to a more compact single-row or horizontal-scroll layout
- Move secondary modules into tabs, drawers, or collapsed areas
Compact mode goal: maintain usability, not just shrink all text and controls.
### 5. Overflow Responsibility Must Be Explicit
Large content blocks on the page must explicitly define:
- Who is responsible for filling remaining height
- Who is responsible for clipping
- Who is responsible for scrolling
Common requirements:
- Parent container chain needs `min-height: 0`
- Workspace containers typically need `display: flex`
- The real scroll node must explicitly use `overflow: auto`
### 6. Cards Must Not Be Compressed to Unreadable
Historical problems have not been "missing scrollbars," but:
- Cards compressed by `flex` to only a tiny visible area
- Text can render but cannot be read completely
- Content exists but is cut off by `overflow: hidden`
Future constraints:
- First ensure cards have a readable minimum height
- If further compression affects readability, switch to internal scrolling
- Do not compress body text, tables, or description areas into unreadable strips just to "maintain one screen"
### 7. Tabs Are Not Inherently Safe Layout Containers
Historical regressions with Tabs include:
- Hidden tab panes reappearing due to custom `display: flex`
- All tabs having the same height/overflow rules forced on them
- Table tabs work, but markdown / help / diagnostics tabs get crushed
Constraints:
- Each type of content inside `Tabs` must define its own layout strategy
- Table tab: "fixed height + internal scrolling"
- Docs/Markdown tab: better as "tab pane self-scrolls + content normal document flow"
- If overriding component library styles, verify the hidden state still holds
### 8. Summary Areas Should Enter Compact Mode First, Not Compress Body
Historical experience shows the top summary cards are most often mishandled:
- They frequently get forcibly narrowed to "fit everything"
- Then the body, tables, and AI result areas all lose their main space
Unified constraint:
- On small screens or high zoom, summary cards should first:
- Reduce padding
- Switch to horizontal scrolling
- Switch to a more compact grid
- Do not sacrifice the main workspace's visible area first
### 9. Long-Document Content Should Prioritize Reading Experience
Content like the following cannot directly apply "table workspace" logic:
- AI briefs
- Runtime logs
- Raw JSON
- Help text
- Multi-paragraph descriptive text
These areas should prioritize:
- Stable title and meta information visibility
- Body has a clear minimum readable height
- Body scroll strategy defined separately
- Support for Markdown tables, dividers, quotes, code blocks
### 10. Height Critical Paths Should Use Fewer Wrapper Layers
Many scroll problems historically were not in the component itself, but came from an extra wrapper layer:
- Height chain broken
- `min-height: 0` not passed down
- `overflow` responsibility absorbed
Therefore:
- For height-critical areas, prefer the most direct DOM structure
- When using `Space`, extra wrapper `div`, or third-party layout containers, verify they don't change scroll and height semantics
- If an area shows "content is there but only a sliver is visible," first suspect an intermediate wrapper layer
## Historical Pitfalls
From Earth, Playground, BGP, DataSources page bugfixes, several high-frequency pitfall types:
### 1. Using `overflow: hidden` to Mask Layout Problems
Superficially the page looks "clean," but actually causes:
- Content getting clipped
- Tab content reduced to a sliver
- Panel renders successfully but users can't see it
Correct approach:
- Let the real content node scroll
- Don't let upper containers unconditionally clip all child content
### 2. Treating All Tabs as the Same Content Type
Tables, Markdown, help cards, and log streams have completely different space requirements.
Correct approach:
- Table: fixed workspace + internal scrolling
- Document: normal flow content + pane-level scrolling
- Side description: content-driven height, not forced to fill
### 3. Only Doing Visual Shrinking, Not Space Reallocation
This causes:
- Card text truncated
- Table shows only 1-2 rows
- Buttons and filters crammed together
Correct approach:
- Compact mode prioritizes re-layout
- Summary area horizontal scrolling
- Collapse / hide secondary modules
### 4. Incomplete Parent Container Height Chain
This is the most common cause of internal scrolling failing.
Inspection order:
1. Does the outer layer actually have a determined height?
2. Does the flex parent have `min-height: 0`?
3. Does the real scroll node explicitly use `overflow: auto`?
4. Have intermediate wrapper layers silently changed layout semantics?
### 5. UI State and Display State Out of Sync
Repeated in Earth-related changes:
- Layer hidden, but hover/lock still active
- Tooltip still showing stale object
- Legend not switching with the state
These constraints also apply to admin pages:
- Hidden, unmounted, or switched-out content should not retain active interaction state
## Recommended Implementation Patterns
### Page Shell
Reuse existing common structures in the project:
- `.dashboard-content-inner`
- `.page-shell`
- `.page-shell__header`
- `.page-shell__body`
- `.table-scroll-region`
Do not invent a completely different height and scroll semantics for each page.
### Table Workspace
Recommended pattern:
```tsx
<Card>
<div className="table-scroll-region" ref={tableRegionRef}>
<Table
pagination={false}
scroll={{ x: 1200, y: tableHeight }}
/>
</div>
</Card>
```
Requirements:
- Tables should scroll inside their card
- `scroll.y` should come from actual available height calculation, not a completely static magic number
- Parent container chain must ensure header, body, content overflow all close inside the table
### Multi-Module Pages
If a page has:
- Summary cards
- Table
- Anomaly details
- Recent events
Do not simply stack all modules vertically. Prefer:
- Top summary + single main workspace at bottom
- Tab-switch multiple secondary data views
- Left-right split with each column scrolling independently
## Discouraged Patterns
The following patterns are considered non-compliant with this project's page standard:
- Relying on full-page vertical scrolling to display the main workspace
- Stacking 3-4 large cards vertically on one page, each wanting to display fully
- Table without internal scrolling, causing only 1-2 rows visible after zoom
- Parent container missing `min-height: 0`, causing internal scrolling to fail
- Only doing visual shrinking without addressing real space allocation
## Page Acceptance Checklist
Before submitting, check at minimum:
- Can page header, summary area, and main workspace appear simultaneously?
- Does the main workspace get the most height on the page?
- When table or detail overflows, does the scrollbar appear inside the module?
- Is the card compressed to the point where text doesn't display completely? If so, has it switched to internal scrolling?
- Is it still usable at browser zoom `125%` / `150%`?
- In a low-height window, is there still a reasonable number of visible content rows?
- Are Tabs, Card, Table still operable when overflowing?
- Do non-table tabs (Markdown, help text, logs) have their own independent and reasonable scroll strategy?
## Implementation Order
When adding or refactoring admin pages, design in this order:
1. Define the main workspace first
2. Determine which modules must always be visible
3. Then handle styling and visual hierarchy
Simply put:
- First ensure correct space allocation
- Then handle scroll boundaries
- Finally handle aesthetics

489
docs/technical/en/manual.md Normal file
View File

@@ -0,0 +1,489 @@
# Planet Manual
This manual is for daily use, demos, development integration, and local operations. It covers four core entry points:
- `planet.sh`: local start, stop, restart, health check, and log access
- Earth: public 3D situational awareness page
- Console: admin backend (login required)
- Docs: public developer documentation and manual
For the shortest path to getting started, see [quickstart.md](/home/ray/dev/linkong/planet/docs/technical/en/quickstart.md).
## Entry Overview
After a default startup, the common URLs are:
| Name | URL | Login Required | Description |
| --- | --- | --- | --- |
| Earth | `http://localhost:3000/earth` | No | 3D globe, layers, BGP, satellites, cables, news situational awareness |
| Docs | `http://localhost:3000/docs` | No | Developer docs, technical reference, usage manual |
| Console | `http://localhost:3000/admin` | Yes | Data, config, alerts, logs, and situational observation |
| AI Playground | `http://localhost:3000/playground` | Yes | AI Provider status and debugging |
| Backend API Docs | `http://localhost:8000/docs` | Depends on endpoint | FastAPI / OpenAPI documentation |
## planet.sh
`planet.sh` is the main control script for local development and demos. Use it to manage services rather than manually starting frontend, backend, database, and AI Provider separately.
### Start
```bash
./planet.sh start
```
Default behavior:
- Starts PostgreSQL and Redis
- Starts AI Provider
- Starts the backend API
- Starts the frontend Vite dev server
- Outputs Earth, console, Playground, and backend API doc URLs
Specify custom ports:
```bash
./planet.sh start -b 8001 -f 3001 -a 8101
```
Parameters:
| Flag | Meaning |
| --- | --- |
| `-b <port>` | Backend port |
| `-f <port>` | Frontend port |
| `-a <port>` | AI Provider port |
| `--allow-lan` | Enable LAN access |
| `--verbose` | Show more command output during execution |
### Stop
```bash
./planet.sh stop
```
Stops:
- Backend
- AI Provider
- Frontend
- PostgreSQL
- Redis
### Restart
Full restart:
```bash
./planet.sh restart
```
Per-module restart:
```bash
./planet.sh restart -b
./planet.sh restart -f
./planet.sh restart -a
./planet.sh restart -d
```
| Flag | Effect |
| --- | --- |
| `-b` | Backend only |
| `-f` | Frontend only |
| `-a` | AI Provider only |
| `-d` | Database only |
Per-module restarts are preferred during development — they avoid interrupting unrelated services.
### Create User
```bash
./planet.sh createuser
```
Used to create a console login account before first use. The script interactively prompts for username, password, and role.
### Health Check
```bash
./planet.sh health
```
Checks:
- `planet_*` container status
- Backend `/health`
- AI Provider `/health`
- Frontend reachability
If something shows offline, check the corresponding logs first.
### Logs
Recent logs:
```bash
./planet.sh log
```
Follow logs:
```bash
./planet.sh log -f
./planet.sh log -b
./planet.sh log -a
```
| Flag | Log source |
| --- | --- |
| `-f` / `--frontend` | `/tmp/planet_frontend.log` |
| `-b` / `--backend` | `/tmp/planet_backend.log` |
| `-a` / `--ai-provider` | `planet_aiprovider` container logs |
### LAN Access
```bash
./planet.sh start --allow-lan
```
Useful for:
- Starting in WSL, accessing from Windows browser
- Demos on phone or tablet
- Another machine on the same LAN accessing the same dev instance
After starting, check your firewall and WSL network forwarding if access fails.
## Earth
Earth is the public 3D situational awareness page, accessed at:
```text
http://localhost:3000/earth
```
It is a standalone frontend. The actual page lives at:
- `frontend/public/earth/index.html`
- `frontend/public/earth/js/`
- `frontend/public/earth/css/`
The React route `/earth` simply hosts it in an iframe.
### Main Uses
Earth is used to observe in a single globe view:
- BGP events, anomalies, and situational posture
- Satellites and orbital trails
- Submarine cables and landing points
- Compute centers
- Country borders, grid lines, HD texture, cloud layer, terrain
- Live news streams and situational news
- Search and focused object details
### Layer Control
The right-side layer panel toggles visualization layers on or off.
Common layers include:
- Grid lines
- Country borders
- HD texture
- Atmospheric cloud layer
- Submarine cables
- Compute centers
- BGP observation
- Satellites
- Orbital trails
- Terrain
Some layers have dependencies:
- Terrain requires HD texture
- Trails require Satellites
- When HD texture is off, the globe shows the base map and edge glow effect
### Search
Earth search finds current globe objects, such as:
- Submarine cables
- Landing points
- Satellites
- Compute centers
- BGP events
- BGP collectors
Search results can be used to quickly locate objects and open their details.
### Settings
The settings panel contains:
- Rotation mode / cruise mode
- Cruise modules: BGP, News
- Satellite display style: self-glow, real ground footprint
- Day/night mode
- Panel visibility toggles
- Globe default size
- Terrain opacity
- Reset settings
These settings are stored in browser local storage. They revert to defaults if you switch browsers or clear site data.
### Cruise Mode
Cruise mode makes Earth automatically cycle through focus targets.
Current cruise modules:
- BGP
- News
Suitable for demos, monitoring displays, or unattended presentations.
### Mobile
Earth has a mobile drawer layout. On small screens:
- Layer controls open in a mobile drawer
- Search, settings, and details use mobile panels
- Main interactions remain centered on globe object clicks, search, and layer toggles
### Common Issues
#### Earth Won't Open
Check whether the frontend is online:
```bash
./planet.sh health
./planet.sh log -f
```
If the frontend port is not `3000`, use the actual port shown at startup.
#### Layer Has No Data
Check the backend and data sources:
```bash
./planet.sh health
./planet.sh log -b
```
Then open the console and check:
- `/datasources`
- `/data`
- `/bgp`
#### Satellites, BGP, or Cables Load Slowly
These layers may depend on backend APIs, external data sources, or first-run collection tasks. Wait for startup tasks to finish before checking logs and console data source status.
## Console
Console entry point:
```text
http://localhost:3000/admin
```
The console requires login. Create a user first if this is your first time:
```bash
./planet.sh createuser
```
### Page Structure
The console uses React + Ant Design, with a left-side menu organized by work domain.
Common pages:
| Page | Route | Purpose |
| --- | --- | --- |
| Dashboard | `/admin` | System overview |
| Earth | `/earth` | Opens the public Earth page |
| Data Sources | `/datasources` | Manage data sources and trigger collection |
| Collected Data | `/data` | View collected data |
| BGP Observation | `/bgp` | BGP situational data |
| System Alerts | `/alerts/system` | System-level alerts |
| BGP Alerts | `/alerts/bgp` | BGP-related alerts |
| Situational Alerts | `/alerts/situational` | Situational assessment alerts |
| AI Playground | `/playground` | AI Provider debugging |
| System Logs | `/logs` | View system logs (typically super admin only) |
| Users | `/users` | User management |
| Settings | `/settings` | System config and TV live stream sources |
### Data Sources
`/datasources` shows and manages collection sources.
Common operations:
- View data source status
- Trigger collection
- View recent collection tasks
- Adjust configuration
If a category of objects is missing on Earth, start here to confirm the data source is available.
### Collected Data
`/data` shows the collected data table.
Useful for diagnosing:
- Whether data has entered the system
- Whether data update times match expectations
- Whether a data source produced valid records
### BGP Observation
`/bgp` is the BGP-focused page.
It complements the BGP layer on Earth:
- Earth emphasizes spatial posture and visual focus
- The console BGP page emphasizes lists, status, details, and assessment
### Alerts
Alert entry points:
- `/alerts/system`
- `/alerts/bgp`
- `/alerts/situational`
Used to view system, network, and situational alerts.
### System Settings
`/settings` manages system-level configuration.
Current common uses:
- System settings
- TV live stream source configuration
- Data source configuration entry points
Available configuration depends on the current user's role.
### System Logs
`/logs` views system logs. If the menu item is not visible, the current user likely lacks the required role.
Common troubleshooting sequence:
```bash
./planet.sh health
./planet.sh log
```
Then open `/logs` for more structured runtime information.
## Docs
Public documentation site:
```text
http://localhost:3000/docs
```
Current public content comes from:
```text
docs/technical/zh/ (Chinese)
docs/technical/en/ (English)
```
Docs supports:
- Category navigation
- Markdown rendering
- Tables and code blocks
- In-document table of contents
- Local search
- Internal links between technical documents
When adding a new technical document, check:
- Does it have a clear top-level heading
- Does it need to be added to the `/docs` manual category and ordering
- Does it contain information that should not be publicly displayed
## Development Command Conventions
Frontend commands must use Bun:
```bash
cd frontend
bun install
bun run dev
bun run build
```
Do not use `npm run ...`. The project uses Bun in WSL / Windows mixed environments to avoid Node/npm path compatibility issues.
Verify the frontend build:
```bash
source ~/.zshrc && bun run build
```
## Troubleshooting Order
When something goes wrong, follow this sequence:
1. Check service status:
```bash
./planet.sh health
```
2. Check recent logs:
```bash
./planet.sh log
```
3. Check per-module logs:
```bash
./planet.sh log -f
./planet.sh log -b
./planet.sh log -a
```
4. Restart only the affected module:
```bash
./planet.sh restart -f
./planet.sh restart -b
./planet.sh restart -a
```
5. If database or cache is abnormal, restart the database:
```bash
./planet.sh restart -d
```
6. If still unrecovered, do a full restart:
```bash
./planet.sh restart
```
## Related Docs
- [quickstart.md](/home/ray/dev/linkong/planet/docs/technical/en/quickstart.md)
- [frontend-admin-frontend-context.md](/home/ray/dev/linkong/planet/docs/technical/en/frontend-admin-frontend-context.md)
- [earth-frontend-context.md](/home/ray/dev/linkong/planet/docs/technical/en/earth-frontend-context.md)
- [earth-layer-style-reference.md](/home/ray/dev/linkong/planet/docs/technical/en/earth-layer-style-reference.md)
- [backend-system-service-control.md](/home/ray/dev/linkong/planet/docs/technical/en/backend-system-service-control.md)
- [backend-collectors.md](/home/ray/dev/linkong/planet/docs/technical/en/backend-collectors.md)

View File

@@ -0,0 +1,105 @@
# Docker + Compose + Buildx Upgrade Guide
Process: remove old version → install new version → verify
---
# 1. Remove Old Version
## Remove apt-installed packages
```bash
sudo apt remove -y docker.io docker-compose docker-compose-v2 docker-doc podman-docker containerd runc
```
---
## Remove system `docker-compose` (V1)
```bash
sudo rm -f "$(which docker-compose 2>/dev/null)"
```
---
## Find and remove manually installed Buildx plugin
```bash
docker info | sed -n '/Plugins:/,/^ Server:/p' | grep -A2 buildx
```
Get the `Path` from the output, then run:
```bash
rm -f <path to docker-buildx file>
```
---
## Clean up unused dependencies
```bash
sudo apt autoremove -y
```
---
# 2. Install Official Docker
Includes Docker Engine, Docker Compose plugin, and Docker Buildx plugin.
## Install dependencies
```bash
sudo apt update
sudo apt install -y ca-certificates curl gnupg
```
---
## Add Docker GPG key
```bash
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
```
---
## Add official repository
```bash
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```
---
## Install Docker + Compose + Buildx
```bash
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```
---
# 3. Verify Installation
```bash
docker --version
docker compose version
docker buildx version
```
---
# 4. Common Commands
```bash
docker compose up -d
docker compose down
docker buildx build .
```

View File

@@ -0,0 +1,193 @@
# Quickstart
This guide is for developers or demo operators starting Planet for the first time. The goal is to get services running via the shortest path and know which URLs to open.
## Prerequisites
Recommended: run in a WSL / Linux shell.
You need:
- Docker / Docker Compose available
- `uv` and `bun` accessible in the current shell
- Repository cloned locally
On a new machine, run the bootstrap script first:
```bash
./scripts/bootstrap-dev.sh
```
This script checks and syncs common dependencies, and generates if missing:
- `backend/.env`
- `aiprovider/.env`
- `frontend/.env.local`
## 1. Start Services
From the repository root:
```bash
./planet.sh start
```
After startup, the key URLs are:
| Entry | Default URL | Purpose |
| --- | --- | --- |
| Earth | `http://localhost:3000/earth` | Public 3D Earth visualization |
| Console | `http://localhost:3000/admin` | Admin console (login required) |
| Docs | `http://localhost:3000/docs` | Public developer docs and manual |
| AI Playground | `http://localhost:3000/playground` | AI debugging (login required) |
| Backend API Docs | `http://localhost:8000/docs` | FastAPI / OpenAPI interface docs |
If the default ports are taken, specify custom ports:
```bash
./planet.sh start -f 3001 -b 8001 -a 8101
```
## 2. Create a Login User
The console requires login. For first-time use:
```bash
./planet.sh createuser
```
Follow the prompts to enter username, password, and role.
## 3. Open Earth
Visit:
```text
http://localhost:3000/earth
```
Earth is a public page — no login required.
Once in, verify:
- The globe renders correctly
- The right-side layer panel can toggle layers on/off
- Search can find cables, satellites, compute centers, BGP events
- Settings panel can switch cruise mode, day/night mode, satellite display style
## 4. Open the Console
Visit:
```text
http://localhost:3000/admin
```
The console manages data sources, collected data, situational observation, alerts, system logs, and configuration.
First-time inspection checklist:
- `/datasources`: data source configuration and collection status
- `/data`: collected data
- `/bgp`: BGP situational view
- `/alerts/system`: system alerts
- `/settings`: system configuration
## 5. Check Service Health
```bash
./planet.sh health
```
This shows container status and checks:
- Backend
- AI Provider
- Frontend
## 6. View Logs
Recent logs:
```bash
./planet.sh log
```
Follow a specific service:
```bash
./planet.sh log -f
./planet.sh log -b
./planet.sh log -a
```
Flags:
- `-f`: frontend logs
- `-b`: backend logs
- `-a`: AI Provider logs
## 7. Common Restarts
Frontend only:
```bash
./planet.sh restart -f
```
Backend only:
```bash
./planet.sh restart -b
```
AI Provider only:
```bash
./planet.sh restart -a
```
Database only:
```bash
./planet.sh restart -d
```
Full restart:
```bash
./planet.sh restart
```
## 8. LAN Access
To allow a Windows browser, phone, or another device on the same network:
```bash
./planet.sh start --allow-lan
```
This makes the frontend and backend listen on a LAN-accessible address.
If access fails, check from the shell running Planet:
```bash
curl http://localhost:3000
curl http://localhost:8000/health
ss -ltnp | grep -E ':3000|:8000'
```
## 9. Stop Services
```bash
./planet.sh stop
```
This shuts down the frontend, backend, AI Provider, PostgreSQL, and Redis.
## Next Steps
- Full usage guide: [manual.md](/home/ray/dev/linkong/planet/docs/technical/en/manual.md)
- Console structure: [frontend-admin-frontend-context.md](/home/ray/dev/linkong/planet/docs/technical/en/frontend-admin-frontend-context.md)
- Earth structure: [earth-frontend-context.md](/home/ray/dev/linkong/planet/docs/technical/en/earth-frontend-context.md)
- Backend collectors: [backend-collectors.md](/home/ray/dev/linkong/planet/docs/technical/en/backend-collectors.md)