feat: add DNS monitor #45
This commit is contained in:
parent
90e8292b30
commit
99610cffae
97
src/client/components/monitor/provider/dns.tsx
Normal file
97
src/client/components/monitor/provider/dns.tsx
Normal file
@ -0,0 +1,97 @@
|
||||
import { Form, Input, InputNumber, Select } from 'antd';
|
||||
import React from 'react';
|
||||
import { MonitorProvider } from './types';
|
||||
import { hostnameValidator, portValidator } from '../../../utils/validator';
|
||||
import { useTranslation } from '@i18next-toolkit/react';
|
||||
|
||||
const rrtypeList = [
|
||||
'A',
|
||||
'AAAA',
|
||||
'CAA',
|
||||
'CNAME',
|
||||
'MX',
|
||||
'NS',
|
||||
'RTP',
|
||||
'SOA',
|
||||
'SRV',
|
||||
'TXT',
|
||||
];
|
||||
|
||||
export const MonitorDNS: React.FC = React.memo(() => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
label={t('Host')}
|
||||
name={['payload', 'hostname']}
|
||||
rules={[
|
||||
{ required: true },
|
||||
{
|
||||
validator: hostnameValidator,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input placeholder="example.com or 1.2.3.4" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t('Resolver Server')}
|
||||
name={['payload', 'resolverServer']}
|
||||
initialValue="1.1.1.1"
|
||||
rules={[
|
||||
{ required: true },
|
||||
{
|
||||
validator: hostnameValidator,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input placeholder="example.com or 1.2.3.4" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t('Resolver Port')}
|
||||
name={['payload', 'resolverPort']}
|
||||
initialValue={53}
|
||||
rules={[
|
||||
{ required: true },
|
||||
{
|
||||
validator: portValidator,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<InputNumber min={1} max={65535} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t('Resouce Record Type')}
|
||||
name={['payload', 'rrtype']}
|
||||
initialValue="CNAME"
|
||||
rules={[
|
||||
{ required: true },
|
||||
{
|
||||
enum: rrtypeList,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Select>
|
||||
{rrtypeList.map((type) => (
|
||||
<Select.Option key={type} value={type}>
|
||||
{type}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
});
|
||||
MonitorDNS.displayName = 'MonitorDNS';
|
||||
|
||||
export const dnsProvider: MonitorProvider = {
|
||||
label: 'DNS',
|
||||
name: 'dns',
|
||||
link: (info) => (
|
||||
<span>
|
||||
<b className="mr-1">[{info.payload.rrtype}]</b>
|
||||
{info.payload.hostname}
|
||||
</span>
|
||||
),
|
||||
form: MonitorDNS,
|
||||
};
|
@ -6,10 +6,12 @@ import { MonitorProvider } from './types';
|
||||
import { openaiProvider } from './openai';
|
||||
import { customProvider } from './custom';
|
||||
import { tcpProvider } from './tcp';
|
||||
import { dnsProvider } from './dns';
|
||||
|
||||
export const monitorProviders: MonitorProvider[] = [
|
||||
pingProvider, // ping
|
||||
tcpProvider, // tcp
|
||||
dnsProvider, // tcp
|
||||
httpProvider, // http
|
||||
openaiProvider, // openai
|
||||
customProvider, // custom node script
|
||||
|
27
src/server/model/monitor/provider/__tests__/dns.spec.ts
Normal file
27
src/server/model/monitor/provider/__tests__/dns.spec.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { dns } from '../dns';
|
||||
|
||||
describe('dns', () => {
|
||||
test('run', async () => {
|
||||
const res = await dns.run({
|
||||
id: '',
|
||||
workspaceId: '',
|
||||
name: '',
|
||||
type: '',
|
||||
active: true,
|
||||
interval: 0,
|
||||
maxRetries: 0,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
payload: {
|
||||
hostname: 'tianji.msgbyte.com',
|
||||
resolverServer: '1.1.1.1',
|
||||
resolverPort: 53,
|
||||
rrtype: 'CNAME',
|
||||
},
|
||||
});
|
||||
|
||||
expect(typeof res).toBe('number');
|
||||
expect(res).not.toBe(-1);
|
||||
});
|
||||
});
|
60
src/server/model/monitor/provider/dns.ts
Normal file
60
src/server/model/monitor/provider/dns.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { MonitorProvider } from './type';
|
||||
import { Resolver } from 'dns';
|
||||
|
||||
export const dns: MonitorProvider<{
|
||||
hostname: string;
|
||||
resolverServer: string;
|
||||
resolverPort: number;
|
||||
rrtype: string;
|
||||
}> = {
|
||||
run: async (monitor) => {
|
||||
if (typeof monitor.payload !== 'object') {
|
||||
throw new Error('monitor.payload should be object');
|
||||
}
|
||||
|
||||
const { hostname, resolverServer, resolverPort, rrtype } = monitor.payload;
|
||||
|
||||
const res = await dnsResolve(
|
||||
hostname,
|
||||
resolverServer,
|
||||
resolverPort,
|
||||
rrtype
|
||||
);
|
||||
|
||||
return res;
|
||||
},
|
||||
};
|
||||
|
||||
function dnsResolve(
|
||||
hostname: string,
|
||||
resolverServer: string,
|
||||
resolverPort: number,
|
||||
rrtype: string
|
||||
) {
|
||||
const start = Date.now();
|
||||
return new Promise<number>((resolve, reject) => {
|
||||
const resolver = new Resolver();
|
||||
// Remove brackets from IPv6 addresses so we can re-add them to
|
||||
// prevent issues with ::1:5300 (::1 port 5300)
|
||||
resolverServer = resolverServer.replace('[', '').replace(']', '');
|
||||
resolver.setServers([`[${resolverServer}]:${resolverPort}`]);
|
||||
|
||||
if (rrtype === 'PTR') {
|
||||
resolver.reverse(hostname, (err, records) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(Date.now() - start);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
resolver.resolve(hostname, rrtype, (err, records) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(Date.now() - start);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
@ -4,11 +4,13 @@ import { openai } from './openai';
|
||||
import type { MonitorProvider } from './type';
|
||||
import { custom } from './custom';
|
||||
import { tcp } from './tcp';
|
||||
import { dns } from './dns';
|
||||
|
||||
export const monitorProviders: Record<string, MonitorProvider<any>> = {
|
||||
ping,
|
||||
http,
|
||||
tcp,
|
||||
dns,
|
||||
openai,
|
||||
custom,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user