You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

91 lines
2.9 KiB

package console
import (
"context"
"database/sql"
"net/http"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
_ "github.com/go-sql-driver/mysql" // 注册 database/sql 的 "mysql" 驱动
_ "github.com/jackc/pgx/v5/stdlib" // 注册 database/sql 的 "pgx" 驱动
)
// appsTest 注册应用前的连通性自检:
// - 应用地址(base_url):HTTP 探测,用于事件通知通道(配置更新等)是否可达;
// - 业务库 DSN(service_dsn):连库 Ping,用于读取业务数据是否可访问。
//
// 两项都通过,前端才放行保存。
func (this *serverComp) appsTest(c *gin.Context) {
var req struct {
BaseUrl string `json:"base_url"`
ServiceDsn string `json:"service_dsn"`
}
_ = c.ShouldBindJSON(&req)
apiOk, apiMsg := testAPIReachable(req.BaseUrl)
dbOk, dbMsg := testDBReachable(req.ServiceDsn)
writeOK(c, gin.H{
"ok": apiOk && dbOk,
"api_ok": apiOk,
"api_msg": apiMsg,
"db_ok": dbOk,
"db_msg": dbMsg,
})
}
// checkAppConn 保存前服务端复检:应用地址可达 + 业务库可连,任一不过返回原因;空串表示通过。
// 与前端「测试连接」同一套逻辑,防止绕过前端直接调接口写入不可用的注册项。
func checkAppConn(m *AppRegistry) string {
if ok, msg := testAPIReachable(m.BaseUrl); !ok {
return "应用地址检测失败: " + msg
}
if ok, msg := testDBReachable(m.ServiceDsn); !ok {
return "业务库检测失败: " + msg
}
return ""
}
// testAPIReachable HTTP 探测应用地址:能建立连接拿到任意响应即视为可达(含 4xx/5xx,
// 说明服务在线,只是该路径不一定接 GET)。仅判断「主机/服务是否在线」。
func testAPIReachable(rawurl string) (bool, string) {
rawurl = strings.TrimSpace(rawurl)
if rawurl == "" {
return false, "应用地址为空"
}
if !strings.HasPrefix(rawurl, "http://") && !strings.HasPrefix(rawurl, "https://") {
return false, "地址需以 http:// 或 https:// 开头"
}
client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Get(rawurl)
if err != nil {
return false, "不可达: " + err.Error()
}
defer resp.Body.Close()
return true, "可达 (HTTP " + strconv.Itoa(resp.StatusCode) + ")"
}
// testDBReachable 用 database/sql 连业务库并 Ping(5s 超时),测完即关、不留连接。
func testDBReachable(dsn string) (bool, string) {
dsn = strings.TrimSpace(dsn)
if dsn == "" {
return false, "DSN 为空"
}
driver := "mysql"
if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") {
driver = "pgx"
}
db, err := sql.Open(driver, dsn)
if err != nil {
return false, "DSN 解析失败: " + err.Error()
}
defer db.Close()
db.SetMaxOpenConns(1)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := db.PingContext(ctx); err != nil {
return false, "连接失败: " + err.Error()
}
return true, "连接成功"
}