xworkspace-console/api/service_probe.go
2026-06-13 07:41:47 +08:00

97 lines
2.7 KiB
Go

package main
import (
"net"
"strconv"
"time"
)
type ServiceProbe interface {
Probe() []Service
}
type serviceProbeConfig struct {
Name string
Units []string
Port int
URL string
}
type SystemServiceProbe struct {
services []serviceProbeConfig
}
func NewServiceProbe() ServiceProbe {
return SystemServiceProbe{
services: []serviceProbeConfig{
{Name: "XWorkspace Console", Units: []string{"xworkspace-console.service"}, Port: 17000, URL: "http://127.0.0.1:17000"},
{Name: "OpenClaw Gateway", Units: []string{"xworkspace-openclaw.service", "openclaw-gateway.service"}, Port: 18789, URL: "http://127.0.0.1:18789/channels"},
{Name: "XWorkmate Bridge", Units: []string{"xworkspace-bridge.service", "xworkmate-bridge.service"}, Port: 8787, URL: "http://127.0.0.1:8787/api/ping"},
{Name: "LiteLLM", Units: []string{"xworkspace-litellm.service", "litellm-proxy.service"}, Port: 4000, URL: "http://127.0.0.1:4000/ui"},
{Name: "Vault", Units: []string{"xworkspace-vault.service", "vault.service"}, Port: 8200, URL: "http://127.0.0.1:8200/ui/"},
{Name: "Terminal", Units: []string{"xworkspace-ttyd.service", "ttyd.service"}, Port: 7681, URL: "http://127.0.0.1:7681"},
},
}
}
func (p SystemServiceProbe) Probe() []Service {
services := make([]Service, 0, len(p.services))
for _, config := range p.services {
unit, state := firstSystemdUnit(config.Units)
portOpen := config.Port > 0 && isPortOpen("127.0.0.1", config.Port)
if state == "" && portOpen {
state = "active"
} else if state == "" {
state = "inactive"
}
services = append(services, Service{
Name: config.Name,
Unit: unit,
State: state,
Detail: probeDetail(unit, portOpen),
Port: config.Port,
URL: config.URL,
})
}
return services
}
func firstSystemdUnit(units []string) (string, string) {
for _, unit := range units {
if state := commandState("systemctl", "is-active", unit); state != "" && state != "unknown" {
return unit, state
}
if state := commandState("systemctl", "--user", "is-active", unit); state != "" && state != "unknown" {
return unit, state
}
}
for _, unit := range units {
if state := commandState("systemctl", "is-enabled", unit); state != "" && state != "unknown" {
return unit, ""
}
if state := commandState("systemctl", "--user", "is-enabled", unit); state != "" && state != "unknown" {
return unit, ""
}
}
return "", ""
}
func probeDetail(unit string, portOpen bool) string {
if unit == "" && portOpen {
return "port open"
}
if portOpen {
return "systemd + port open"
}
return "systemd"
}
func isPortOpen(host string, port int) bool {
conn, err := net.DialTimeout("tcp", net.JoinHostPort(host, strconv.Itoa(port)), 300*time.Millisecond)
if err != nil {
return false
}
_ = conn.Close()
return true
}