From 99610cffaeb6da469ce66396d0b6cd6347087d37 Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Mon, 25 Mar 2024 01:00:20 +0800 Subject: [PATCH] feat: add DNS monitor #45 --- .../components/monitor/provider/dns.tsx | 97 +++++++++++++++++++ .../components/monitor/provider/index.ts | 2 + .../monitor/provider/__tests__/dns.spec.ts | 27 ++++++ src/server/model/monitor/provider/dns.ts | 60 ++++++++++++ src/server/model/monitor/provider/index.ts | 2 + 5 files changed, 188 insertions(+) create mode 100644 src/client/components/monitor/provider/dns.tsx create mode 100644 src/server/model/monitor/provider/__tests__/dns.spec.ts create mode 100644 src/server/model/monitor/provider/dns.ts diff --git a/src/client/components/monitor/provider/dns.tsx b/src/client/components/monitor/provider/dns.tsx new file mode 100644 index 0000000..490863b --- /dev/null +++ b/src/client/components/monitor/provider/dns.tsx @@ -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 ( + <> + + + + + + + + + + + + + + ); +}); +MonitorDNS.displayName = 'MonitorDNS'; + +export const dnsProvider: MonitorProvider = { + label: 'DNS', + name: 'dns', + link: (info) => ( + + [{info.payload.rrtype}] + {info.payload.hostname} + + ), + form: MonitorDNS, +}; diff --git a/src/client/components/monitor/provider/index.ts b/src/client/components/monitor/provider/index.ts index c728ab1..43cb93d 100644 --- a/src/client/components/monitor/provider/index.ts +++ b/src/client/components/monitor/provider/index.ts @@ -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 diff --git a/src/server/model/monitor/provider/__tests__/dns.spec.ts b/src/server/model/monitor/provider/__tests__/dns.spec.ts new file mode 100644 index 0000000..26243e3 --- /dev/null +++ b/src/server/model/monitor/provider/__tests__/dns.spec.ts @@ -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); + }); +}); diff --git a/src/server/model/monitor/provider/dns.ts b/src/server/model/monitor/provider/dns.ts new file mode 100644 index 0000000..6169cff --- /dev/null +++ b/src/server/model/monitor/provider/dns.ts @@ -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((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); + } + }); + } + }); +} diff --git a/src/server/model/monitor/provider/index.ts b/src/server/model/monitor/provider/index.ts index f740942..468fa7f 100644 --- a/src/server/model/monitor/provider/index.ts +++ b/src/server/model/monitor/provider/index.ts @@ -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> = { ping, http, tcp, + dns, openai, custom, };