feat(siteMonitor): add support for custom ports

pull/193/head
daxi 2025-06-05 16:07:31 +08:00
parent 29a7579743
commit 497b45de83
3 changed files with 93 additions and 7 deletions

View File

@ -8,6 +8,7 @@ import (
"net/http"
"strings"
"time"
"strconv"
)
// SSLInfo 定义结果结构体
@ -188,18 +189,48 @@ func UpdInfo(id, domain string, s *public.Sqlite, reportType string) error {
return errCheck
}
// ExtractHostPort 从 target 中提取 host 和 port 并校验,若无 port 则使用默认值
func ExtractAndValidateHostPort(target, defaultPort string) (host string, port string, err error) {
host = target
port = defaultPort
// 处理带端口情况
if strings.Contains(target, ":") {
h, p, splitErr := net.SplitHostPort(target)
if splitErr == nil {
host, port = h, p
} else if !(strings.Count(target, ":") >= 2) || strings.Contains(target, "]") {
return "", "", fmt.Errorf("地址格式错误:%v", splitErr)
}
}
// 验证端口是数字且在有效范围内
portNum, convErr := strconv.Atoi(port)
if convErr != nil || portNum < 1 || portNum > 65535 {
return "", "", fmt.Errorf("端口号非法:%s", port)
}
return host, port, nil
}
// CheckWebsite 实际检测函数
func CheckWebsite(target string) (*SSLInfo, error) {
result := &SSLInfo{Target: target}
host, port, err := ExtractAndValidateHostPort(target, "443")
if err != nil {
return nil, err
}
// 验证格式是否是 IP 或域名
if net.ParseIP(target) == nil {
if _, err := net.LookupHost(target); err != nil {
if net.ParseIP(host) == nil {
if _, err := net.LookupHost(host); err != nil {
return result, fmt.Errorf("无效域名或 IP%v", err)
}
}
hostPort := net.JoinHostPort(target, "443")
hostPort := net.JoinHostPort(host, port)
// result := &SSLInfo{Target: target}
@ -213,7 +244,7 @@ func CheckWebsite(target string) (*SSLInfo, error) {
defer conn.Close()
// 发送 HTTPS 请求检测状态
resp, err := http.Get("https://" + target)
resp, err := http.Get("https://" + hostPort)
if err != nil {
result.HTTPStatus = 0
result.HTTPStatusText = "异常"

View File

@ -6,7 +6,7 @@ import (
)
func Test(t *testing.T) {
site := "bt.cn" // 只传域名或 IP不要 http://
site := "bt.cn:443" // 只传域名或 IP不要 http://
result, err := CheckWebsite(site)
if err != nil {
fmt.Printf("❌ 检测失败: %v\n", err)

View File

@ -14,7 +14,7 @@ import {
useLoadingMask,
} from '@baota/naive-ui/hooks'
import { useError } from '@baota/hooks/error'
import { isDomain } from '@baota/utils/business'
import { isDomain, isIp, isPort } from '@baota/utils/business'
import { $t } from '@locales/index'
// Store和组件
@ -335,6 +335,61 @@ export const useMonitorFormController = (data: UpdateSiteMonitorParams | null =
}),
])
/**
* IP
*/
function isValidHost(host: string): boolean {
if (!host?.trim()) return false;
const trimmedHost = host.trim();
let hostPart: string;
let portPart: string | undefined;
// 分离主机和端口部分
if (trimmedHost.startsWith('[')) {
// IPv6 地址(可能带端口)
const closingBracketIndex = trimmedHost.indexOf(']');
// 缺少闭合括号
if (closingBracketIndex === -1) {
return false;
}
// 去掉 []
hostPart = trimmedHost.slice(1, closingBracketIndex);
const rest = trimmedHost.slice(closingBracketIndex + 1);
// 检查剩余部分(只能是 :端口 或空)
if (rest) {
// 非端口部分
if (!rest.startsWith(':')) {
return false;
}
portPart = rest.slice(1);
}
} else {
// IPv4 或域名(可能带端口)
const lastColonIndex = trimmedHost.lastIndexOf(':');
if (lastColonIndex !== -1) {
hostPart = trimmedHost.slice(0, lastColonIndex);
portPart = trimmedHost.slice(lastColonIndex + 1);
} else {
hostPart = trimmedHost;
}
}
// 检查主机部分IPv4/IPv6/域名)
const isHostValid = isIp(hostPart) || isDomain(hostPart);
if (!isHostValid) {
return false;
}
// 检查端口部分
if (portPart !== undefined) {
return isPort(portPart);
}
return true;
}
/**
*
*/
@ -345,7 +400,7 @@ export const useMonitorFormController = (data: UpdateSiteMonitorParams | null =
message: '请输入正确的域名',
trigger: 'input',
validator: (rule: any, value: any, callback: any) => {
if (!isDomain(value)) {
if (!isValidHost(value)) {
callback(new Error('请输入正确的域名'))
} else {
callback()