Files
monibuca/plugin/debug/envcheck.go
2025-06-07 21:07:28 +08:00

220 lines
6.8 KiB
Go

package plugin_debug
import (
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"net/url"
"time"
"google.golang.org/protobuf/types/known/timestamppb"
"gopkg.in/yaml.v3"
"m7s.live/v5/pb"
"m7s.live/v5/pkg/util"
)
type EnvCheckResult struct {
Message string `json:"message"`
Type string `json:"type"` // info, success, error, complete
}
// 自定义系统信息响应结构体,用于 JSON 解析
type SysInfoResponseJSON struct {
Code int32 `json:"code"`
Message string `json:"message"`
Data struct {
StartTime string `json:"startTime"`
LocalIP string `json:"localIP"`
PublicIP string `json:"publicIP"`
Version string `json:"version"`
GoVersion string `json:"goVersion"`
OS string `json:"os"`
Arch string `json:"arch"`
CPUs int32 `json:"cpus"`
Plugins []struct {
Name string `json:"name"`
PushAddr []string `json:"pushAddr"`
PlayAddr []string `json:"playAddr"`
Description map[string]string `json:"description"`
} `json:"plugins"`
} `json:"data"`
}
// 插件配置响应结构体
type PluginConfigResponse struct {
Code int32 `json:"code"`
Message string `json:"message"`
Data struct {
File string `json:"file"`
Modified string `json:"modified"`
Merged string `json:"merged"`
} `json:"data"`
}
// TCP 配置结构体
type TCPConfig struct {
ListenAddr string `yaml:"listenaddr"`
ListenAddrTLS string `yaml:"listenaddrtls"`
}
// 插件配置结构体
type PluginConfig struct {
TCP TCPConfig `yaml:"tcp"`
}
func (p *DebugPlugin) EnvCheck(w http.ResponseWriter, r *http.Request) {
// Get target URL from query parameter
targetURL := r.URL.Query().Get("target")
if targetURL == "" {
r.URL.Path = "/static/envcheck.html"
staticFSHandler.ServeHTTP(w, r)
return
}
// Create SSE connection
util.NewSSE(w, r.Context(), func(sse *util.SSE) {
// Function to send SSE messages
sendMessage := func(message string, msgType string) {
result := EnvCheckResult{
Message: message,
Type: msgType,
}
sse.WriteJSON(result)
}
// Parse target URL
_, err := url.Parse(targetURL)
if err != nil {
sendMessage(fmt.Sprintf("Invalid URL: %v", err), "error")
return
}
// Check if we can connect to the target server
sendMessage(fmt.Sprintf("Checking connection to %s...", targetURL), "info")
// Get system info from target server
resp, err := http.Get(fmt.Sprintf("%s/api/sysinfo", targetURL))
if err != nil {
sendMessage(fmt.Sprintf("Failed to connect to target server: %v", err), "error")
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
sendMessage(fmt.Sprintf("Target server returned status code: %d", resp.StatusCode), "error")
return
}
// Read and parse system info
body, err := io.ReadAll(resp.Body)
if err != nil {
sendMessage(fmt.Sprintf("Failed to read response: %v", err), "error")
return
}
var sysInfoJSON SysInfoResponseJSON
if err := json.Unmarshal(body, &sysInfoJSON); err != nil {
sendMessage(fmt.Sprintf("Failed to parse system info: %v", err), "error")
return
}
// Convert JSON response to protobuf response
sysInfo := &pb.SysInfoResponse{
Code: sysInfoJSON.Code,
Message: sysInfoJSON.Message,
Data: &pb.SysInfoData{
LocalIP: sysInfoJSON.Data.LocalIP,
PublicIP: sysInfoJSON.Data.PublicIP,
Version: sysInfoJSON.Data.Version,
GoVersion: sysInfoJSON.Data.GoVersion,
Os: sysInfoJSON.Data.OS,
Arch: sysInfoJSON.Data.Arch,
Cpus: sysInfoJSON.Data.CPUs,
},
}
// Parse start time
if startTime, err := time.Parse(time.RFC3339, sysInfoJSON.Data.StartTime); err == nil {
sysInfo.Data.StartTime = timestamppb.New(startTime)
}
// Convert plugins
for _, pluginJSON := range sysInfoJSON.Data.Plugins {
plugin := &pb.PluginInfo{
Name: pluginJSON.Name,
PushAddr: pluginJSON.PushAddr,
PlayAddr: pluginJSON.PlayAddr,
Description: pluginJSON.Description,
}
sysInfo.Data.Plugins = append(sysInfo.Data.Plugins, plugin)
}
// Check each plugin's configuration
for _, plugin := range sysInfo.Data.Plugins {
// Get plugin configuration
configResp, err := http.Get(fmt.Sprintf("%s/api/config/get/%s", targetURL, plugin.Name))
if err != nil {
sendMessage(fmt.Sprintf("Failed to get configuration for plugin %s: %v", plugin.Name, err), "error")
continue
}
defer configResp.Body.Close()
if configResp.StatusCode != http.StatusOK {
sendMessage(fmt.Sprintf("Failed to get configuration for plugin %s: status code %d", plugin.Name, configResp.StatusCode), "error")
continue
}
var configRespJSON PluginConfigResponse
if err := json.NewDecoder(configResp.Body).Decode(&configRespJSON); err != nil {
sendMessage(fmt.Sprintf("Failed to parse configuration for plugin %s: %v", plugin.Name, err), "error")
continue
}
// Parse YAML configuration
var config PluginConfig
if err := yaml.Unmarshal([]byte(configRespJSON.Data.Merged), &config); err != nil {
sendMessage(fmt.Sprintf("Failed to parse YAML configuration for plugin %s: %v", plugin.Name, err), "error")
continue
}
// Check TCP configuration
if config.TCP.ListenAddr != "" {
host, port, err := net.SplitHostPort(config.TCP.ListenAddr)
if err != nil {
sendMessage(fmt.Sprintf("Invalid listenaddr format for plugin %s: %v", plugin.Name, err), "error")
} else {
sendMessage(fmt.Sprintf("Checking TCP listenaddr %s for plugin %s...", config.TCP.ListenAddr, plugin.Name), "info")
// Try to establish TCP connection
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%s", host, port), 5*time.Second)
if err != nil {
sendMessage(fmt.Sprintf("TCP listenaddr %s for plugin %s is not accessible: %v", config.TCP.ListenAddr, plugin.Name, err), "error")
} else {
conn.Close()
sendMessage(fmt.Sprintf("TCP listenaddr %s for plugin %s is accessible", config.TCP.ListenAddr, plugin.Name), "success")
}
}
}
if config.TCP.ListenAddrTLS != "" {
host, port, err := net.SplitHostPort(config.TCP.ListenAddrTLS)
if err != nil {
sendMessage(fmt.Sprintf("Invalid listenaddrtls format for plugin %s: %v", plugin.Name, err), "error")
} else {
sendMessage(fmt.Sprintf("Checking TCP TLS listenaddr %s for plugin %s...", config.TCP.ListenAddrTLS, plugin.Name), "info")
// Try to establish TCP connection
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%s", host, port), 5*time.Second)
if err != nil {
sendMessage(fmt.Sprintf("TCP TLS listenaddr %s for plugin %s is not accessible: %v", config.TCP.ListenAddrTLS, plugin.Name, err), "error")
} else {
conn.Close()
sendMessage(fmt.Sprintf("TCP TLS listenaddr %s for plugin %s is accessible", config.TCP.ListenAddrTLS, plugin.Name), "success")
}
}
}
}
sendMessage("Environment check completed", "complete")
})
}