feat: add aiprovider service foundation

This commit is contained in:
linkong
2026-04-07 17:30:27 +08:00
parent 3f5505f03e
commit 9a50e72bd1
42 changed files with 1766 additions and 166 deletions

View File

@@ -1,100 +1,20 @@
import { useEffect, useState } from 'react'
import { Alert, Card, Col, Row, Space, Statistic, Table, Tag, Typography } from 'antd'
import axios from 'axios'
import AppLayout from '../../components/AppLayout/AppLayout'
import { formatDateTimeZhCN } from '../../utils/datetime'
import {
getSituationalAwarenessGateway,
type BGPAnomaly,
type BGPCollectorCoverage,
type BGPEvent,
type BGPIncident,
type CollectorSummary,
type EventSummary,
type Summary,
} from '../../services/situational-awareness'
const { Title, Text } = Typography
interface BGPAnomaly {
id: number
source: string
anomaly_type: string
severity: string
status: string
prefix: string | null
origin_asn: number | null
new_origin_asn: number | null
confidence: number
summary: string
created_at: string | null
}
interface BGPEvent {
id: number
collector: string | null
event_type: string
prefix: string | null
origin_asn: number | null
peer_asn: number | null
observed_at: string | null
}
interface BGPCollectorCoverage {
collector: string
city?: string | null
country?: string | null
observation_count: number
recent_24h_observation_count: number
recent_7d_observation_count: number
prefix_count: number
recent_24h_prefix_count: number
recent_7d_prefix_count: number
origin_asn_count: number
peer_asn_count: number
latest_observed_at: string | null
latest_event_type: string | null
baseline_scope: {
countries: string[]
cities: string[]
}
}
interface BGPIncident {
id: number
incident_type: string
title: string
summary: string
severity: string
status: string
confidence: number
affected_prefixes: string[]
affected_asns: number[]
affected_collectors: string[]
affected_regions: Array<{ country?: string; city?: string }>
related_cables: Array<{
landing_point?: string
city?: string
country?: string
distance_km?: number
cable_names?: string[]
}>
created_at: string | null
started_at: string | null
}
interface Summary {
total: number
by_type: Record<string, number>
by_severity: Record<string, number>
by_status: Record<string, number>
}
interface EventSummary {
total: number
collector_count: number
prefix_count: number
by_type: Record<string, number>
}
interface CollectorSummary {
total: number
active_collectors: number
observed_prefixes: number
observed_origins: number
recent_24h_events: number
recent_7d_events: number
}
const situationalAwarenessGateway = getSituationalAwarenessGateway()
function severityColor(severity: string) {
if (severity === 'critical') return 'red'
@@ -117,22 +37,20 @@ function BGP() {
const load = async () => {
setLoading(true)
try {
const [incidentsRes, incidentSummaryRes, anomaliesRes, eventsRes, eventSummaryRes, collectorsRes, collectorSummaryRes] = await Promise.all([
axios.get('/api/v1/bgp/incidents', { params: { page_size: 50 } }),
axios.get('/api/v1/bgp/incidents/summary'),
axios.get('/api/v1/bgp/anomalies', { params: { page_size: 100 } }),
axios.get('/api/v1/bgp/events', { params: { page_size: 20 } }),
axios.get('/api/v1/bgp/events/summary'),
axios.get('/api/v1/bgp/collectors'),
axios.get('/api/v1/bgp/collectors/summary'),
])
setIncidents(incidentsRes.data.data || [])
setIncidentSummary(incidentSummaryRes.data)
setAnomalies(anomaliesRes.data.data || [])
setEvents(eventsRes.data.data || [])
setEventSummary(eventSummaryRes.data)
setCollectors(collectorsRes.data.data || [])
setCollectorSummary(collectorSummaryRes.data)
const snapshot = await situationalAwarenessGateway.getBGPOverview({
incidentPageSize: 50,
anomalyPageSize: 100,
eventPageSize: 20,
})
setIncidents(snapshot.incidents)
setIncidentSummary(snapshot.incidentSummary)
setAnomalies(snapshot.anomalies)
setEvents(snapshot.events)
setEventSummary(snapshot.eventSummary)
setCollectors(snapshot.collectors)
setCollectorSummary(snapshot.collectorSummary)
} catch (error) {
console.error('Failed to load BGP overview:', error)
} finally {
setLoading(false)
}

View File

@@ -47,16 +47,22 @@ interface RestartTaskLogs {
lines: string[]
}
type RestartAction = 'restart-backend' | 'restart-database' | 'restart-system'
type RestartAction = 'restart-backend' | 'restart-ai-provider' | 'restart-database' | 'restart-system'
type RestartStage = 'confirming' | 'waiting_for_shutdown' | 'waiting_for_recovery' | 'recovered' | 'failed' | 'timeout'
const RESTART_ACTION_OPTIONS: Array<{ value: RestartAction; label: string; description: string; command: string }> = [
{
value: 'restart-backend',
label: '重启服务器',
label: '重启后端',
description: '只重启后端服务,页面通常会短暂失联后自动恢复。',
command: './planet.sh restart -b',
},
{
value: 'restart-ai-provider',
label: '重启 AI Provider',
description: '只重启 AI Provider 适配服务,前端页面通常保持在线。',
command: './planet.sh restart -a',
},
{
value: 'restart-database',
label: '重启数据库',
@@ -77,6 +83,11 @@ const RESTART_GUIDE_LINES: Record<RestartAction, string[]> = {
'[ctl] handing restart to detached runner',
'[ctl] waiting for backend health recovery',
],
'restart-ai-provider': [
'[ctl] preparing ai provider restart task',
'[ctl] handing restart to detached runner',
'[ctl] waiting for ai provider health recovery',
],
'restart-database': [
'[ctl] preparing database restart task',
'[ctl] restarting PostgreSQL and Redis containers',
@@ -94,6 +105,9 @@ const RESTART_GUIDE_LINES: Record<RestartAction, string[]> = {
let cachedDashboardStats: Stats | null = null
function getRestartConfirmMessage(action: RestartAction): string {
if (action === 'restart-ai-provider') {
return '将重启 AI Provider 适配服务,页面通常保持在线,但 AI 分析请求会短暂不可用。'
}
if (action === 'restart-database') {
return '将重启 PostgreSQL 和 Redis页面通常保持在线但相关请求可能短暂波动。'
}
@@ -200,6 +214,8 @@ function Dashboard() {
setRestartMessage(
restartAction === 'restart-system'
? '已发送完全重启指令,页面可能暂时失联,恢复后会自动刷新。'
: restartAction === 'restart-ai-provider'
? '已发送 AI Provider 重启指令,正在等待 AI 服务恢复。'
: '已发送重启指令,正在等待服务进入重启流程。'
)
setRestartLogs((current) => [...current, `任务已创建: ${res.data.task_id}`])