feat: add aiprovider service foundation
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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}`])
|
||||
|
||||
Reference in New Issue
Block a user