mirror of https://github.com/allinssl/allinssl
256 lines
6.1 KiB
Go
256 lines
6.1 KiB
Go
package siteMonitor
|
||
|
||
import (
|
||
"ALLinSSL/backend/public"
|
||
"crypto/tls"
|
||
"fmt"
|
||
"net"
|
||
"net/http"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
// SSLInfo 定义结果结构体
|
||
type SSLInfo struct {
|
||
Target string
|
||
HTTPStatus int
|
||
HTTPStatusText string
|
||
Domains []string
|
||
Issuer string
|
||
NotBefore string
|
||
NotAfter string
|
||
DaysRemaining int
|
||
CertificateOK bool
|
||
CertificateNote string
|
||
}
|
||
|
||
func GetSqlite() (*public.Sqlite, error) {
|
||
s, err := public.NewSqlite("data/site_monitor.db", "")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
s.TableName = "site_monitor"
|
||
return s, nil
|
||
}
|
||
|
||
func GetList(search string, p, limit int64) ([]map[string]any, int, error) {
|
||
var data []map[string]any
|
||
var count int64
|
||
s, err := GetSqlite()
|
||
if err != nil {
|
||
return data, 0, err
|
||
}
|
||
defer s.Close()
|
||
|
||
var limits []int64
|
||
if p >= 0 && limit >= 0 {
|
||
limits = []int64{0, limit}
|
||
if p > 1 {
|
||
limits[0] = (p - 1) * limit
|
||
limits[1] = limit
|
||
}
|
||
}
|
||
|
||
if search != "" {
|
||
count, err = s.Where("name like ? or site_domain like ?", []interface{}{"%" + search + "%", "%" + search + "%"}).Count()
|
||
data, err = s.Where("name like ? or site_domain like ?", []interface{}{"%" + search + "%", "%" + search + "%"}).Order("update_time", "desc").Limit(limits).Select()
|
||
} else {
|
||
count, err = s.Count()
|
||
data, err = s.Order("update_time", "desc").Limit(limits).Select()
|
||
}
|
||
if err != nil {
|
||
return data, 0, err
|
||
}
|
||
for _, v := range data {
|
||
v["domain"] = v["site_domain"]
|
||
}
|
||
|
||
return data, int(count), nil
|
||
}
|
||
|
||
func AddMonitor(name, domain, reportType string, cycle int) error {
|
||
s, err := GetSqlite()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer s.Close()
|
||
info, err := CheckWebsite(domain)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
_, err = s.Insert(map[string]any{
|
||
"name": name,
|
||
"site_domain": domain,
|
||
"report_type": reportType,
|
||
"cycle": cycle,
|
||
"state": info.HTTPStatusText,
|
||
"ca": info.Issuer,
|
||
"cert_domain": strings.Join(info.Domains, ","),
|
||
"end_time": info.NotAfter,
|
||
"end_day": info.DaysRemaining,
|
||
"create_time": time.Now().Format("2006-01-02 15:04:05"),
|
||
"update_time": time.Now().Format("2006-01-02 15:04:05"),
|
||
"last_time": time.Now().Format("2006-01-02 15:04:05"),
|
||
"active": 1,
|
||
})
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func UpdMonitor(id, name, domain, reportType string, cycle int) error {
|
||
s, err := GetSqlite()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer s.Close()
|
||
|
||
info, err := CheckWebsite(domain)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
_, err = s.Where("id=?", []interface{}{id}).Update(map[string]any{
|
||
"name": name,
|
||
"site_domain": domain,
|
||
"report_type": reportType,
|
||
"cycle": cycle,
|
||
"state": info.HTTPStatusText,
|
||
"ca": info.Issuer,
|
||
"cert_domain": strings.Join(info.Domains, ","),
|
||
"end_time": info.NotAfter,
|
||
"end_day": info.DaysRemaining,
|
||
"update_time": time.Now().Format("2006-01-02 15:04:05"),
|
||
"last_time": time.Now().Format("2006-01-02 15:04:05"),
|
||
"active": 1,
|
||
})
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func DelMonitor(id string) error {
|
||
s, err := GetSqlite()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer s.Close()
|
||
_, err = s.Where("id=?", []interface{}{id}).Delete()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func SetMonitor(id string, active int) error {
|
||
s, err := GetSqlite()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer s.Close()
|
||
_, err = s.Where("id=?", []interface{}{id}).Update(map[string]any{
|
||
"active": active,
|
||
"update_time": time.Now().Format("2006-01-02 15:04:05"),
|
||
})
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func UpdInfo(id, domain string, s *public.Sqlite, reportType string) error {
|
||
info, errCheck := CheckWebsite(domain)
|
||
now := time.Now()
|
||
updateData := map[string]any{
|
||
"state": info.HTTPStatusText,
|
||
"ca": info.Issuer,
|
||
"cert_domain": strings.Join(info.Domains, ","),
|
||
"end_time": info.NotAfter,
|
||
"end_day": info.DaysRemaining,
|
||
"last_time": now.Format("2006-01-02 15:04:05"),
|
||
"except_end_time": now.Format("2006-01-02 15:04:05"),
|
||
}
|
||
if errCheck != nil {
|
||
updateData["state"] = "异常"
|
||
// return err
|
||
} else {
|
||
if info.HTTPStatus != 0 && info.CertificateOK != false {
|
||
delete(updateData, "except_end_time")
|
||
} else {
|
||
errCheck = fmt.Errorf("证书异常")
|
||
}
|
||
}
|
||
_, err := s.Where("id=?", []interface{}{id}).Update(updateData)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return errCheck
|
||
}
|
||
|
||
// CheckWebsite 实际检测函数
|
||
func CheckWebsite(target string) (*SSLInfo, error) {
|
||
result := &SSLInfo{Target: target}
|
||
|
||
// 拆分 host 和 port
|
||
host, port, err := net.SplitHostPort(target)
|
||
if err != nil {
|
||
// 没有显式端口,默认 443
|
||
host = target
|
||
port = "443"
|
||
}
|
||
|
||
// 验证格式是否是 IP 或域名
|
||
if net.ParseIP(host) == nil {
|
||
if _, err := net.LookupHost(host); err != nil {
|
||
return result, fmt.Errorf("无效域名或 IP:%v", err)
|
||
}
|
||
}
|
||
|
||
// TLS 连接(支持所有 TLS 版本)
|
||
conn, err := tls.Dial("tcp", net.JoinHostPort(host, port), &tls.Config{
|
||
InsecureSkipVerify: true,
|
||
MinVersion: tls.VersionTLS10, // 显式支持所有版本
|
||
MaxVersion: tls.VersionTLS13,
|
||
})
|
||
if err != nil {
|
||
return result, fmt.Errorf("目标不支持 HTTPS:%v", err)
|
||
}
|
||
defer conn.Close()
|
||
|
||
// HTTP 状态检测(构造 URL,保留端口)
|
||
url := fmt.Sprintf("https://%s", net.JoinHostPort(host, port))
|
||
resp, err := http.Get(url)
|
||
if err != nil {
|
||
result.HTTPStatus = 0
|
||
result.HTTPStatusText = "异常"
|
||
} else {
|
||
result.HTTPStatus = resp.StatusCode
|
||
result.HTTPStatusText = "正常"
|
||
resp.Body.Close()
|
||
}
|
||
|
||
// 获取证书
|
||
cert := conn.ConnectionState().PeerCertificates[0]
|
||
result.Domains = cert.DNSNames
|
||
result.Issuer = cert.Issuer.CommonName
|
||
result.NotBefore = cert.NotBefore.Format("2006-01-02 15:04:05")
|
||
result.NotAfter = cert.NotAfter.Format("2006-01-02 15:04:05")
|
||
result.DaysRemaining = int(cert.NotAfter.Sub(time.Now()).Hours() / 24)
|
||
|
||
now := time.Now()
|
||
switch {
|
||
case now.Before(cert.NotBefore):
|
||
result.CertificateOK = false
|
||
result.CertificateNote = "尚未生效"
|
||
case now.After(cert.NotAfter):
|
||
result.CertificateOK = false
|
||
result.CertificateNote = "已过期"
|
||
default:
|
||
result.CertificateOK = true
|
||
result.CertificateNote = "有效"
|
||
}
|
||
|
||
return result, nil
|
||
}
|