feat(gateway): 网关健康状态——后台定时探活只保留最近一次结果并在列表展示
- 迁移 1043:gateway 表加 health_status/last_check_reason/last_checked_at - Store.RecordGatewayCheck 覆盖写最近一次探活结果,Create/Update/List/Get 全量返回健康字段 - api.RecordGatewayHealthChecks 并发探 /healthz:连接失败或 5xx 判离线,其余判在线(401 等异常写入原因供排查) - workers 每 30 秒一轮,app 启动接线 - 网关管理列表新增健康状态列:在线/离线/未检查 Tag,原因与检查时间收进 Tooltip
This commit is contained in:
@@ -1,15 +1,30 @@
|
||||
// 网关管理:语义对齐 web.archived GatewaysPage.jsx(注册/编辑 Modal、令牌显隐复制、删除)。
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { Alert, App, Button, Card, Flex, Form, Input, Modal, Popconfirm, Space, Table, Typography } from 'antd';
|
||||
import { Alert, App, Button, Card, Flex, Form, Input, Modal, Popconfirm, Space, Table, Tag, Tooltip, Typography } from 'antd';
|
||||
import { CopyOutlined, EyeInvisibleOutlined, EyeOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import { create, getList, remove, update } from '@/services/api';
|
||||
import { conflictMessage } from '@/utils/helpers';
|
||||
import { conflictMessage, dateTime } from '@/utils/helpers';
|
||||
import { fixedLeft, fixedRight, tablePagination, tableScroll, useOverflowGrid } from '@/utils/table';
|
||||
|
||||
const namePattern = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
||||
const tokenPattern = /^[A-Za-z0-9][A-Za-z0-9._-]{15,127}$/;
|
||||
|
||||
// 健康状态列:只展示后台定时探活的最近一次结果,不保留历史。
|
||||
function HealthCell({ status, reason, checkedAt }: { status?: string; reason?: string; checkedAt?: string }) {
|
||||
const label = status === 'healthy' ? '在线' : status === 'unhealthy' ? '离线' : '未检查';
|
||||
const color = status === 'healthy' ? 'success' : status === 'unhealthy' ? 'error' : 'default';
|
||||
const details = [
|
||||
checkedAt ? `最近检查:${dateTime(checkedAt)}` : null,
|
||||
reason || null,
|
||||
].filter(Boolean).join(';');
|
||||
return (
|
||||
<Tooltip title={details || '尚未执行健康检查'}>
|
||||
<Tag color={color}>{label}</Tag>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
function GatewayFormModal({
|
||||
open,
|
||||
initial,
|
||||
@@ -146,6 +161,12 @@ export default function Page() {
|
||||
const columns: ColumnsType<any> = [
|
||||
{ title: '名称', dataIndex: 'name', ...fixedLeft<any>({}), render: (value: string) => <Typography.Text strong>{value}</Typography.Text> },
|
||||
{ title: 'Endpoint', dataIndex: 'endpoint', render: (value: string) => <Typography.Text code style={{ fontSize: 12 }} ellipsis>{value}</Typography.Text> },
|
||||
{
|
||||
title: '健康状态',
|
||||
dataIndex: 'health_status',
|
||||
width: 110,
|
||||
render: (_, gateway) => <HealthCell status={gateway.health_status} reason={gateway.last_check_reason} checkedAt={gateway.last_checked_at} />,
|
||||
},
|
||||
{
|
||||
title: '令牌',
|
||||
dataIndex: 'token',
|
||||
|
||||
Reference in New Issue
Block a user