feat: add monitor data metrics in monitor info
This commit is contained in:
parent
b4f2170e51
commit
7bc245eb45
@ -5,12 +5,14 @@ import { HealthBar, HealthBarBeat, HealthBarProps } from '../HealthBar';
|
||||
import dayjs from 'dayjs';
|
||||
import { trpc } from '../../api/trpc';
|
||||
import { useCurrentWorkspaceId } from '../../store/user';
|
||||
import { useWatch } from '../../hooks/useWatch';
|
||||
|
||||
interface MonitorHealthBarProps {
|
||||
monitorId: string;
|
||||
count?: number;
|
||||
size?: HealthBarProps['size'];
|
||||
showCurrentStatus?: boolean;
|
||||
onBeatsItemUpdate?: (items: { value: number; createdAt: string }[]) => void;
|
||||
}
|
||||
export const MonitorHealthBar: React.FC<MonitorHealthBarProps> = React.memo(
|
||||
(props) => {
|
||||
@ -38,7 +40,8 @@ export const MonitorHealthBar: React.FC<MonitorHealthBarProps> = React.memo(
|
||||
);
|
||||
}, [newDataList, recent, count]);
|
||||
|
||||
const beats = items.map((item): HealthBarBeat => {
|
||||
const beats = useMemo(() => {
|
||||
return items.map((item): HealthBarBeat => {
|
||||
if (!item) {
|
||||
return {
|
||||
status: 'none',
|
||||
@ -61,6 +64,11 @@ export const MonitorHealthBar: React.FC<MonitorHealthBarProps> = React.memo(
|
||||
status: 'health',
|
||||
};
|
||||
});
|
||||
}, [items]);
|
||||
|
||||
useWatch([items], () => {
|
||||
props.onBeatsItemUpdate?.(items);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="flex justify-between items-center">
|
||||
|
@ -10,7 +10,8 @@ import { MonitorInfo as MonitorInfoType } from '../../../types';
|
||||
import { Area, AreaConfig } from '@ant-design/charts';
|
||||
import { MonitorHealthBar } from './MonitorHealthBar';
|
||||
import { useSocketSubscribeList } from '../../api/socketio';
|
||||
import { uniqBy } from 'lodash';
|
||||
import { last, uniqBy } from 'lodash-es';
|
||||
import { ErrorTip } from '../ErrorTip';
|
||||
|
||||
interface MonitorInfoProps {
|
||||
monitorId: string;
|
||||
@ -18,6 +19,7 @@ interface MonitorInfoProps {
|
||||
export const MonitorInfo: React.FC<MonitorInfoProps> = React.memo((props) => {
|
||||
const workspaceId = useCurrentWorkspaceId();
|
||||
const { monitorId } = props;
|
||||
const [currectResponse, setCurrentResponse] = useState(0);
|
||||
|
||||
const { data: monitorInfo, isLoading } = trpc.monitor.get.useQuery({
|
||||
workspaceId,
|
||||
@ -59,6 +61,16 @@ export const MonitorInfo: React.FC<MonitorInfoProps> = React.memo((props) => {
|
||||
count={40}
|
||||
size="large"
|
||||
showCurrentStatus={true}
|
||||
onBeatsItemUpdate={(items) => {
|
||||
setCurrentResponse(last(items)?.value ?? 0);
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<MonitorDataMetrics
|
||||
monitorId={monitorId}
|
||||
currectResponse={currectResponse}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
@ -71,6 +83,70 @@ export const MonitorInfo: React.FC<MonitorInfoProps> = React.memo((props) => {
|
||||
});
|
||||
MonitorInfo.displayName = 'MonitorInfo';
|
||||
|
||||
export const MonitorDataMetrics: React.FC<{
|
||||
monitorId: string;
|
||||
currectResponse: number;
|
||||
}> = React.memo((props) => {
|
||||
const workspaceId = useCurrentWorkspaceId();
|
||||
const { monitorId, currectResponse } = props;
|
||||
const { data, isLoading } = trpc.monitor.dataMetrics.useQuery({
|
||||
workspaceId,
|
||||
monitorId,
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
return <Loading />;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
return <ErrorTip />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex justify-between text-center">
|
||||
<div>
|
||||
<div className="font-bold mb-0.5">Response</div>
|
||||
<div className="text-gray-500">(Current)</div>
|
||||
<div>{currectResponse} ms</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold mb-0.5">Avg. Response</div>
|
||||
<div className="text-gray-500">(24 hour)</div>
|
||||
<div>{parseFloat(data.recent1DayAvg.toFixed(0))} ms</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold mb-0.5">Uptime</div>
|
||||
<div className="text-gray-500 mb-2 text-xs">(24 hour)</div>
|
||||
<div>
|
||||
{parseFloat(
|
||||
(
|
||||
(data.recent1DayOnlineCount /
|
||||
(data.recent1DayOnlineCount + data.recent1DayOfflineCount)) *
|
||||
100
|
||||
).toFixed(2)
|
||||
)}
|
||||
%
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="font-bold mb-0.5">Uptime</div>
|
||||
<div className="text-gray-500">(30 days)</div>
|
||||
<div>
|
||||
{parseFloat(
|
||||
(
|
||||
(data.recent30DayOnlineCount /
|
||||
(data.recent30DayOnlineCount + data.recent30DayOfflineCount)) *
|
||||
100
|
||||
).toFixed(2)
|
||||
)}
|
||||
%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
MonitorDataMetrics.displayName = 'MonitorDataMetrics';
|
||||
|
||||
const MonitorDataChart: React.FC<{ monitorId: string }> = React.memo(
|
||||
(props) => {
|
||||
const workspaceId = useCurrentWorkspaceId();
|
||||
|
12
src/client/hooks/useWatch.ts
Normal file
12
src/client/hooks/useWatch.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { DependencyList, useLayoutEffect } from 'react';
|
||||
import { useEvent } from './useEvent';
|
||||
|
||||
/**
|
||||
* 监听变更并触发回调
|
||||
*/
|
||||
export function useWatch(deps: DependencyList, cb: () => void) {
|
||||
const memoizedFn = useEvent(cb);
|
||||
useLayoutEffect(() => {
|
||||
memoizedFn();
|
||||
}, deps);
|
||||
}
|
@ -3,6 +3,7 @@ import { prisma } from '../../model/_client';
|
||||
import { z } from 'zod';
|
||||
import { monitorManager } from '../../model/monitor';
|
||||
import { MonitorInfo } from '../../../types';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export const monitorRouter = router({
|
||||
all: workspaceProcedure.query(async ({ input }) => {
|
||||
@ -58,7 +59,7 @@ export const monitorRouter = router({
|
||||
|
||||
return monitor;
|
||||
}),
|
||||
data: workspaceOwnerProcedure
|
||||
data: workspaceProcedure
|
||||
.input(
|
||||
z.object({
|
||||
monitorId: z.string().cuid2(),
|
||||
@ -86,7 +87,7 @@ export const monitorRouter = router({
|
||||
},
|
||||
});
|
||||
}),
|
||||
recentData: workspaceOwnerProcedure
|
||||
recentData: workspaceProcedure
|
||||
.input(
|
||||
z.object({
|
||||
monitorId: z.string().cuid2(),
|
||||
@ -107,4 +108,89 @@ export const monitorRouter = router({
|
||||
},
|
||||
});
|
||||
}),
|
||||
dataMetrics: workspaceProcedure
|
||||
.input(
|
||||
z.object({
|
||||
monitorId: z.string().cuid2(),
|
||||
})
|
||||
)
|
||||
.query(async ({ input }) => {
|
||||
const { monitorId } = input;
|
||||
const now = dayjs();
|
||||
|
||||
const [
|
||||
recent1DayAvg,
|
||||
recent1DayOnlineCount,
|
||||
recent1DayOfflineCount,
|
||||
recent30DayOnlineCount,
|
||||
recent30DayOfflineCount,
|
||||
] = await Promise.all([
|
||||
prisma.monitorData
|
||||
.aggregate({
|
||||
_avg: {
|
||||
value: true,
|
||||
},
|
||||
where: {
|
||||
monitorId,
|
||||
createdAt: {
|
||||
lte: now.toDate(),
|
||||
gte: now.subtract(1, 'day').toDate(),
|
||||
},
|
||||
},
|
||||
})
|
||||
.then((res) => res._avg.value ?? -1),
|
||||
prisma.monitorData.count({
|
||||
where: {
|
||||
monitorId,
|
||||
createdAt: {
|
||||
lte: now.toDate(),
|
||||
gte: now.subtract(1, 'day').toDate(),
|
||||
},
|
||||
value: {
|
||||
gte: 0,
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.monitorData.count({
|
||||
where: {
|
||||
monitorId,
|
||||
createdAt: {
|
||||
lte: now.toDate(),
|
||||
gte: now.subtract(1, 'day').toDate(),
|
||||
},
|
||||
value: -1,
|
||||
},
|
||||
}),
|
||||
prisma.monitorData.count({
|
||||
where: {
|
||||
monitorId,
|
||||
createdAt: {
|
||||
lte: now.toDate(),
|
||||
gte: now.subtract(30, 'day').toDate(),
|
||||
},
|
||||
value: {
|
||||
gte: 0,
|
||||
},
|
||||
},
|
||||
}),
|
||||
prisma.monitorData.count({
|
||||
where: {
|
||||
monitorId,
|
||||
createdAt: {
|
||||
lte: now.toDate(),
|
||||
gte: now.subtract(30, 'day').toDate(),
|
||||
},
|
||||
value: -1,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
return {
|
||||
recent1DayAvg,
|
||||
recent1DayOnlineCount,
|
||||
recent1DayOfflineCount,
|
||||
recent30DayOnlineCount,
|
||||
recent30DayOfflineCount,
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user