Skip to content

Commit

Permalink
feat: add tcp port monitor
Browse files Browse the repository at this point in the history
  • Loading branch information
moonrailgun committed Jan 10, 2024
1 parent e6f0267 commit 9892d3a
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 7 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
"socket.io-client": "^4.7.2",
"str2int": "^1.1.0",
"swagger-ui-express": "^5.0.0",
"tcp-ping": "^0.1.1",
"trpc-openapi": "^1.2.0",
"ts-node": "^10.9.1",
"uuid": "^9.0.0",
Expand Down Expand Up @@ -116,6 +117,7 @@
"@types/request-ip": "^0.0.38",
"@types/swagger-ui-express": "^4.1.5",
"@types/tar": "^6.1.5",
"@types/tcp-ping": "^0.1.5",
"@vitejs/plugin-react": "^4.0.4",
"autoprefixer": "^10.4.15",
"cross-env": "^7.0.3",
Expand Down
14 changes: 14 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions src/client/components/monitor/MonitorInfoEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useMemo } from 'react';
import type { Monitor } from '@prisma/client';
import { Button, Form, Input, InputNumber, Select } from 'antd';
import { getMonitorProvider, monitorProviders } from './provider';
import { useEvent } from '../../hooks/useEvent';
import { useEvent, useEventWithLoading } from '../../hooks/useEvent';
import { NotificationPicker } from '../notification/NotificationPicker';

export type MonitorInfoEditorValues = Omit<
Expand All @@ -23,7 +23,7 @@ const defaultValues: Omit<MonitorInfoEditorValues, 'payload'> = {

interface MonitorInfoEditorProps {
initialValues?: MonitorInfoEditorValues;
onSave: (value: MonitorInfoEditorValues) => void;
onSave: (value: MonitorInfoEditorValues) => Promise<void>;
}
export const MonitorInfoEditor: React.FC<MonitorInfoEditorProps> = React.memo(
(props) => {
Expand All @@ -46,8 +46,8 @@ export const MonitorInfoEditor: React.FC<MonitorInfoEditorProps> = React.memo(
return <Component />;
}, [provider]);

const handleSubmit = useEvent((values) => {
props.onSave({
const [handleSubmit, isLoading] = useEventWithLoading(async (values) => {
await props.onSave({
...values,
active: true,
});
Expand Down Expand Up @@ -96,7 +96,7 @@ export const MonitorInfoEditor: React.FC<MonitorInfoEditorProps> = React.memo(
<NotificationPicker allowClear={true} mode="multiple" />
</Form.Item>

<Button type="primary" htmlType="submit">
<Button type="primary" htmlType="submit" loading={isLoading}>
Save
</Button>
</Form>
Expand Down
4 changes: 3 additions & 1 deletion src/client/components/monitor/provider/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import { httpProvider } from './http';
import { MonitorProvider } from './types';
import { openaiProvider } from './openai';
import { customProvider } from './custom';
import { tcpProvider } from './tcp';

export const monitorProviders: MonitorProvider[] = [
pingProvider, // ping
tcpProvider, // tcp
httpProvider, // http
openaiProvider, // http
openaiProvider, // openai
customProvider, // custom node script
];

Expand Down
43 changes: 43 additions & 0 deletions src/client/components/monitor/provider/tcp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Form, Input, InputNumber } from 'antd';
import React from 'react';
import { MonitorProvider } from './types';
import { hostnameValidator, portValidator } from '../../../utils/validator';

export const MonitorTCP: React.FC = React.memo(() => {
return (
<>
<Form.Item
label="Host"
name={['payload', 'hostname']}
rules={[
{ required: true },
{
validator: hostnameValidator,
},
]}
>
<Input placeholder="example.com or 1.2.3.4" />
</Form.Item>
<Form.Item
label="Host"
name={['payload', 'port']}
rules={[
{ required: true },
{
validator: portValidator,
},
]}
>
<InputNumber placeholder="80" min={1} max={65535} />
</Form.Item>
</>
);
});
MonitorTCP.displayName = 'MonitorTCP';

export const tcpProvider: MonitorProvider = {
label: 'TCP Port',
name: 'tcp',
link: (info) => `${info.payload.hostname}:${info.payload.port}`,
form: MonitorTCP,
};
22 changes: 21 additions & 1 deletion src/client/hooks/useEvent.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo, useRef } from 'react';
import { useMemo, useRef, useState } from 'react';

// From https://github.com/alibaba/hooks/blob/master/packages/hooks/src/useMemoizedFn/index.ts

Expand Down Expand Up @@ -33,3 +33,23 @@ export function useEvent<T extends Noop>(fn: T) {

return memoizedFn.current as T;
}

/**
* Same with useEvent but return loading state
*/
export function useEventWithLoading<T extends (...args: any[]) => Promise<any>>(
fn: T
): [T, boolean] {
const [isLoading, setIsLoading] = useState(false);

const _fn = useEvent(async (...args: Parameters<T>) => {
setIsLoading(true);
try {
return await fn(...args);
} finally {
setIsLoading(false);
}
}) as T;

return [_fn as T, isLoading];
}
9 changes: 9 additions & 0 deletions src/client/utils/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,12 @@ export const urlSlugValidator: Validator = (rule, value, callback) => {
callback('Not valid slug');
}
};

export const portValidator: Validator = (rule, value, callback) => {
try {
z.number().min(1).max(65535).parse(value);
callback();
} catch (err) {
callback('Not valid port, it should be 1 ~ 65535');
}
};
2 changes: 2 additions & 0 deletions src/server/model/monitor/provider/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { ping } from './ping';
import { openai } from './openai';
import type { MonitorProvider } from './type';
import { custom } from './custom';
import { tcp } from './tcp';

export const monitorProviders: Record<string, MonitorProvider<any>> = {
ping,
http,
tcp,
openai,
custom,
};
42 changes: 42 additions & 0 deletions src/server/model/monitor/provider/tcp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { MonitorProvider } from './type';
import tcpp from 'tcp-ping';

export const tcp: MonitorProvider<{
hostname: string;
port: number;
}> = {
run: async (monitor) => {
if (typeof monitor.payload !== 'object') {
throw new Error('monitor.payload should be object');
}

const { hostname, port } = monitor.payload;

const res = await pingAction(hostname, port);

return res;
},
};

function pingAction(hostname: string, port: number) {
return new Promise<number>((resolve, reject) => {
tcpp.ping(
{
address: hostname,
port,
attempts: 1,
},
(err, result) => {
if (err) {
reject(err);
} else {
if (result.results.length >= 1 && result.results[0].err) {
reject(result.results[0].err);
}

resolve(Math.round(result.max));
}
}
);
});
}

0 comments on commit 9892d3a

Please sign in to comment.