feat: add monitor bind website
This commit is contained in:
parent
df7d18c2a0
commit
194103eb2d
@ -63,6 +63,7 @@
|
||||
"request-ip": "^3.3.0",
|
||||
"socket.io": "^4.7.2",
|
||||
"socket.io-client": "^4.7.2",
|
||||
"str2int": "^1.1.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"uuid": "^9.0.0",
|
||||
"vite-express": "^0.10.0",
|
||||
|
@ -142,6 +142,9 @@ dependencies:
|
||||
socket.io-client:
|
||||
specifier: ^4.7.2
|
||||
version: 4.7.2
|
||||
str2int:
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0
|
||||
ts-node:
|
||||
specifier: ^10.9.1
|
||||
version: 10.9.1(@types/node@18.17.12)(typescript@5.2.2)
|
||||
@ -6263,6 +6266,10 @@ packages:
|
||||
engines: {node: '>= 0.8'}
|
||||
dev: false
|
||||
|
||||
/str2int@1.1.0:
|
||||
resolution: {integrity: sha512-Eb9eGdySBmA2cQyJaxsiB9a6IJ7t1RCQK6o3pUZf6dTI3CVq9TkMcUCVNoWeonv7mNza3Hcp3cf1j73vEhL4ag==}
|
||||
dev: false
|
||||
|
||||
/streamx@2.15.1:
|
||||
resolution: {integrity: sha512-fQMzy2O/Q47rgwErk/eGeLu/roaFWV0jVsogDmrszM9uIw8L5OA+t+V93MgYlufNptfjmYR1tOMWhei/Eh7TQA==}
|
||||
dependencies:
|
||||
|
@ -54,16 +54,18 @@ model WorkspacesOnUsers {
|
||||
|
||||
model Website {
|
||||
id String @id @unique @default(cuid()) @db.VarChar(30)
|
||||
workspaceId String @db.VarChar(30)
|
||||
name String @db.VarChar(100)
|
||||
domain String? @db.VarChar(500)
|
||||
shareId String? @unique @db.VarChar(50)
|
||||
resetAt DateTime? @db.Timestamptz(6)
|
||||
workspaceId String @db.VarChar(30)
|
||||
monitorId String? @db.VarChar(30)
|
||||
createdAt DateTime @default(now()) @db.Timestamptz(6)
|
||||
updatedAt DateTime @updatedAt @db.Timestamptz(6)
|
||||
deletedAt DateTime? @db.Timestamptz(6)
|
||||
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id], onUpdate: Cascade, onDelete: Cascade)
|
||||
monitor Monitor? @relation(fields: [monitorId], references: [id], onUpdate: Cascade, onDelete: Cascade)
|
||||
|
||||
sessions WebsiteSession[]
|
||||
eventData WebsiteEventData[]
|
||||
@ -242,6 +244,7 @@ model Monitor {
|
||||
|
||||
workspace Workspace @relation(fields: [workspaceId], references: [id])
|
||||
|
||||
websites Website[]
|
||||
notifications Notification[]
|
||||
events MonitorEvent[]
|
||||
datas MonitorData[]
|
||||
|
@ -3,18 +3,9 @@ import { DateUnit } from '../../utils/date';
|
||||
import { queryClient } from '../cache';
|
||||
import { request } from '../request';
|
||||
import { getUserTimezone } from './user';
|
||||
import { Website as WebsiteInfo } from '@prisma/client';
|
||||
|
||||
export interface WebsiteInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
domain: string | null;
|
||||
shareId: string | null;
|
||||
resetAt: string | null;
|
||||
workspaceId: string;
|
||||
createdAt: string | null;
|
||||
updatedAt: string | null;
|
||||
deletedAt: string | null;
|
||||
}
|
||||
export type { WebsiteInfo };
|
||||
|
||||
export async function getWorkspaceWebsites(
|
||||
workspaceId: string
|
||||
|
29
src/client/components/ColorTag.tsx
Normal file
29
src/client/components/ColorTag.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
import { Tag } from 'antd';
|
||||
import React, { useMemo } from 'react';
|
||||
import str2int from 'str2int';
|
||||
|
||||
const builtinColors = [
|
||||
'magenta',
|
||||
'red',
|
||||
'volcano',
|
||||
'orange',
|
||||
'gold',
|
||||
'lime',
|
||||
'green',
|
||||
'cyan',
|
||||
'blue',
|
||||
'geekblue',
|
||||
'purple',
|
||||
];
|
||||
|
||||
export const ColorTag: React.FC<{ label: string; colors?: string[] }> =
|
||||
React.memo((props) => {
|
||||
const { label, colors = builtinColors } = props;
|
||||
const color = useMemo(
|
||||
() => colors[str2int(label) % colors.length],
|
||||
[label]
|
||||
);
|
||||
|
||||
return <Tag color={color}>{label}</Tag>;
|
||||
});
|
||||
ColorTag.displayName = 'ColorTag';
|
@ -10,6 +10,7 @@ import { useCurrentWorkspaceId } from '../store/user';
|
||||
import { ErrorTip } from './ErrorTip';
|
||||
import { Loading } from './Loading';
|
||||
import { NoWorkspaceTip } from './NoWorkspaceTip';
|
||||
import { MonitorPicker } from './monitor/MonitorPicker';
|
||||
import { defaultErrorHandler, defaultSuccessHandler, trpc } from '../api/trpc';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useEvent } from '../hooks/useEvent';
|
||||
@ -35,12 +36,13 @@ export const WebsiteInfo: React.FC = React.memo(() => {
|
||||
});
|
||||
|
||||
const handleSave = useEvent(
|
||||
async (values: { name: string; domain: string }) => {
|
||||
async (values: { name: string; domain: string; monitorId: string }) => {
|
||||
await updateMutation.mutateAsync({
|
||||
workspaceId,
|
||||
websiteId: websiteId!,
|
||||
name: values.name,
|
||||
domain: values.domain,
|
||||
monitorId: values.monitorId,
|
||||
});
|
||||
}
|
||||
);
|
||||
@ -84,6 +86,7 @@ export const WebsiteInfo: React.FC = React.memo(() => {
|
||||
id: website.id,
|
||||
name: website.name,
|
||||
domain: website.domain,
|
||||
monitorId: website.monitorId,
|
||||
}}
|
||||
onFinish={handleSave}
|
||||
>
|
||||
@ -101,6 +104,10 @@ export const WebsiteInfo: React.FC = React.memo(() => {
|
||||
<Input size="large" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label="Monitor" name="monitorId">
|
||||
<MonitorPicker size="large" allowClear={true} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Button size="large" htmlType="submit">
|
||||
Save
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Card, Select, Space, Tag } from 'antd';
|
||||
import { Card, Select, Space } from 'antd';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { trpc } from '../../api/trpc';
|
||||
@ -12,6 +12,7 @@ import { MonitorHealthBar } from './MonitorHealthBar';
|
||||
import { useSocketSubscribeList } from '../../api/socketio';
|
||||
import { last, uniqBy } from 'lodash-es';
|
||||
import { ErrorTip } from '../ErrorTip';
|
||||
import { ColorTag } from '../ColorTag';
|
||||
|
||||
interface MonitorInfoProps {
|
||||
monitorId: string;
|
||||
@ -42,7 +43,7 @@ export const MonitorInfo: React.FC<MonitorInfoProps> = React.memo((props) => {
|
||||
<div className="text-2xl">{monitorInfo.name}</div>
|
||||
|
||||
<div>
|
||||
<Tag color="cyan">{monitorInfo.type}</Tag>
|
||||
<ColorTag label={monitorInfo.type} />
|
||||
<span>
|
||||
{getMonitorLink(monitorInfo as any as MonitorInfoType)}
|
||||
</span>
|
||||
|
27
src/client/components/monitor/MonitorPicker.tsx
Normal file
27
src/client/components/monitor/MonitorPicker.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { Select, SelectProps } from 'antd';
|
||||
import React from 'react';
|
||||
import { trpc } from '../../api/trpc';
|
||||
import { useCurrentWorkspaceId } from '../../store/user';
|
||||
import { ColorTag } from '../ColorTag';
|
||||
|
||||
interface MonitorPickerProps extends SelectProps<string> {}
|
||||
export const MonitorPicker: React.FC<MonitorPickerProps> = React.memo(
|
||||
(props) => {
|
||||
const workspaceId = useCurrentWorkspaceId();
|
||||
const { data: allMonitor = [] } = trpc.monitor.all.useQuery({
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
return (
|
||||
<Select {...props}>
|
||||
{allMonitor.map((m) => (
|
||||
<Select.Option key={m.id} value={m.id}>
|
||||
<ColorTag label={m.type} />
|
||||
{m.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
);
|
||||
MonitorPicker.displayName = 'MonitorPicker';
|
@ -24,6 +24,7 @@ import { formatNumber, formatShortTime } from '../../utils/common';
|
||||
import { useTheme } from '../../hooks/useTheme';
|
||||
import { WebsiteOnlineCount } from '../WebsiteOnlineCount';
|
||||
import { useGlobalRangeDate } from '../../hooks/useGlobalRangeDate';
|
||||
import { MonitorHealthBar } from '../monitor/MonitorHealthBar';
|
||||
|
||||
export const WebsiteOverview: React.FC<{
|
||||
website: WebsiteInfo;
|
||||
@ -81,11 +82,9 @@ export const WebsiteOverview: React.FC<{
|
||||
{props.website.name}
|
||||
</span>
|
||||
|
||||
<HealthBar
|
||||
beats={Array.from({ length: 13 }).map(() => ({
|
||||
status: 'health',
|
||||
}))}
|
||||
/>
|
||||
{props.website.monitorId && (
|
||||
<MonitorHealthBar monitorId={props.website.monitorId} />
|
||||
)}
|
||||
|
||||
<div className="ml-4 text-base font-normal">
|
||||
<WebsiteOnlineCount
|
||||
|
@ -157,13 +157,14 @@ export const websiteRouter = router({
|
||||
updateInfo: workspaceOwnerProcedure
|
||||
.input(
|
||||
z.object({
|
||||
websiteId: z.string().cuid(),
|
||||
websiteId: z.string().cuid2(),
|
||||
name: z.string().max(100),
|
||||
domain: z.union([z.string().max(500).url(), z.string().max(500).ip()]),
|
||||
monitorId: z.string().cuid2(),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ input }) => {
|
||||
const { workspaceId, websiteId, name, domain } = input;
|
||||
const { workspaceId, websiteId, name, domain, monitorId } = input;
|
||||
|
||||
const websiteInfo = await prisma.website.update({
|
||||
where: {
|
||||
@ -173,6 +174,7 @@ export const websiteRouter = router({
|
||||
data: {
|
||||
name,
|
||||
domain,
|
||||
monitorId,
|
||||
},
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user