Ship live alerts, pool status, AI monitor, remote agent commands, build manager, and uninstall flow; wire Calibrate settings (WS ping, pool traffic log, retention limits) at runtime and exclude server/data from git.
124 lines
2.4 KiB
Go
124 lines
2.4 KiB
Go
package api
|
|
|
|
import (
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
)
|
|
|
|
type ServerInfo struct {
|
|
Port int `json:"port"`
|
|
Host string `json:"host"`
|
|
LocalIPs []string `json:"local_ips"`
|
|
SuggestedURL string `json:"suggested_url"`
|
|
DashboardURL string `json:"dashboard_url"`
|
|
WebSocketURL string `json:"websocket_url"`
|
|
}
|
|
|
|
func GetServerInfo(w http.ResponseWriter, r *http.Request, publicURLOverride string) {
|
|
host := r.Host
|
|
if idx := strings.Index(host, ":"); idx > 0 {
|
|
host = host[:idx]
|
|
}
|
|
|
|
localIPs := listLocalIPv4()
|
|
suggestedHost := host
|
|
if isLoopbackHost(host) && len(localIPs) > 0 {
|
|
suggestedHost = localIPs[0]
|
|
}
|
|
|
|
port := 8989
|
|
if idx := strings.LastIndex(r.Host, ":"); idx > 0 {
|
|
if p := r.Host[idx+1:]; p != "" {
|
|
port = parsePort(p)
|
|
}
|
|
}
|
|
|
|
suggestedURL := "http://" + net.JoinHostPort(suggestedHost, itoa(port))
|
|
if strings.TrimSpace(publicURLOverride) != "" {
|
|
suggestedURL = strings.TrimSpace(publicURLOverride)
|
|
suggestedHost = suggestedURL
|
|
if u, err := url.Parse(suggestedURL); err == nil && u.Hostname() != "" {
|
|
suggestedHost = u.Hostname()
|
|
}
|
|
}
|
|
info := ServerInfo{
|
|
Port: port,
|
|
Host: host,
|
|
LocalIPs: localIPs,
|
|
SuggestedURL: suggestedURL,
|
|
DashboardURL: suggestedURL,
|
|
WebSocketURL: strings.Replace(strings.Replace(suggestedURL, "https://", "wss://", 1), "http://", "ws://", 1) + "/ws/agent",
|
|
}
|
|
|
|
writeJSON(w, info)
|
|
}
|
|
|
|
func listLocalIPv4() []string {
|
|
var ips []string
|
|
ifaces, err := net.Interfaces()
|
|
if err != nil {
|
|
return ips
|
|
}
|
|
for _, iface := range ifaces {
|
|
if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 {
|
|
continue
|
|
}
|
|
addrs, err := iface.Addrs()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
for _, addr := range addrs {
|
|
var ip net.IP
|
|
switch v := addr.(type) {
|
|
case *net.IPNet:
|
|
ip = v.IP
|
|
case *net.IPAddr:
|
|
ip = v.IP
|
|
}
|
|
if ip == nil || ip.IsLoopback() {
|
|
continue
|
|
}
|
|
ip = ip.To4()
|
|
if ip == nil {
|
|
continue
|
|
}
|
|
ips = append(ips, ip.String())
|
|
}
|
|
}
|
|
return ips
|
|
}
|
|
|
|
func isLoopbackHost(host string) bool {
|
|
return host == "localhost" || host == "127.0.0.1" || host == "::1"
|
|
}
|
|
|
|
func parsePort(s string) int {
|
|
n := 0
|
|
for _, ch := range s {
|
|
if ch < '0' || ch > '9' {
|
|
return 8989
|
|
}
|
|
n = n*10 + int(ch-'0')
|
|
}
|
|
if n == 0 {
|
|
return 8989
|
|
}
|
|
return n
|
|
}
|
|
|
|
func itoa(n int) string {
|
|
if n == 0 {
|
|
return "0"
|
|
}
|
|
var buf [16]byte
|
|
i := len(buf)
|
|
for n > 0 {
|
|
i--
|
|
buf[i] = byte('0' + n%10)
|
|
n /= 10
|
|
}
|
|
return string(buf[i:])
|
|
}
|