mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-09-13 13:19:08 +02:00
fix: startup banner hardcodes 127.0.0.1 regardless of server.host (#307)
The Web UI startup banner always printed 127.0.0.1, even when server.host was bound to 0.0.0.0 or a specific interface. This made users believe the host setting was ignored (the actual listen binding was correct). Pass the configured host into the banner and expand wildcard binds to the machine's real addresses so LAN URLs are shown. Fixes #301 Co-authored-by: sycpro <sycpro@sycprodeMacBook-Pro.local>
This commit is contained in:
@@ -86,6 +86,7 @@ func main() {
|
||||
}
|
||||
termout.PrintStartupWebUI(termout.StartupWebUIOptions{
|
||||
Scheme: scheme,
|
||||
Host: cfg.Server.Host,
|
||||
Port: port,
|
||||
SelfSigned: scheme == "https" && cfg.Server.TLSAutoSelfSign,
|
||||
HTTPRedirect: scheme == "https" && config.ServerHTTPRedirectEnabled(&cfg.Server),
|
||||
|
||||
@@ -2,13 +2,17 @@ package termout
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// StartupWebUIOptions configures the startup Web UI banner.
|
||||
type StartupWebUIOptions struct {
|
||||
Scheme string
|
||||
Host string
|
||||
Port int
|
||||
SelfSigned bool
|
||||
HTTPRedirect bool
|
||||
@@ -22,9 +26,46 @@ func PrintConfigCreated() {
|
||||
s.BlankLine()
|
||||
}
|
||||
|
||||
// startupHosts 返回横幅应展示的访问地址。通配地址(含空 host)展开为回环地址
|
||||
// 加本机非回环 IPv4;显式 host 原样展示。横幅此前硬编码 127.0.0.1,导致
|
||||
// 绑定 0.0.0.0 的用户误以为 server.host 配置未生效(issue #301)。
|
||||
func startupHosts(host string) []string {
|
||||
host = strings.TrimSpace(host)
|
||||
if host != "" && host != "0.0.0.0" && host != "::" && host != "[::]" {
|
||||
return []string{host}
|
||||
}
|
||||
hosts := []string{"127.0.0.1"}
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return hosts
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
ipNet, ok := addr.(*net.IPNet)
|
||||
if !ok || ipNet.IP.IsLoopback() || ipNet.IP.To4() == nil {
|
||||
continue
|
||||
}
|
||||
ip := ipNet.IP.String()
|
||||
dup := false
|
||||
for _, h := range hosts {
|
||||
if h == ip {
|
||||
dup = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !dup {
|
||||
hosts = append(hosts, ip)
|
||||
}
|
||||
}
|
||||
return hosts
|
||||
}
|
||||
|
||||
// PrintStartupWebUI prints a colored startup banner for the Web UI.
|
||||
func PrintStartupWebUI(opts StartupWebUIOptions) {
|
||||
s := New(os.Stdout)
|
||||
printStartupWebUI(os.Stdout, opts)
|
||||
}
|
||||
|
||||
func printStartupWebUI(out io.Writer, opts StartupWebUIOptions) {
|
||||
s := New(out)
|
||||
scheme := opts.Scheme
|
||||
if scheme == "" {
|
||||
scheme = "http"
|
||||
@@ -33,17 +74,23 @@ func PrintStartupWebUI(opts StartupWebUIOptions) {
|
||||
if port <= 0 {
|
||||
port = 8080
|
||||
}
|
||||
url := fmt.Sprintf("%s://127.0.0.1:%d/", scheme, port)
|
||||
hosts := startupHosts(opts.Host)
|
||||
urlFor := func(h string) string {
|
||||
return scheme + "://" + net.JoinHostPort(h, strconv.Itoa(port)) + "/"
|
||||
}
|
||||
|
||||
s.BlankLine()
|
||||
s.Println(s.Bold(s.Cyan("CYBERSTRIKE AI")) + s.Dim(" / secure workspace"))
|
||||
s.Println(s.Dim(strings.Repeat("─", 60)))
|
||||
s.Println(s.Green("● ONLINE") + " " + s.Bold(s.White(url)))
|
||||
s.Println(s.Green("● ONLINE") + " " + s.Bold(s.White(urlFor(hosts[0]))))
|
||||
for _, h := range hosts[1:] {
|
||||
s.Println(s.Dim(" Network ") + s.Bold(s.White(urlFor(h))))
|
||||
}
|
||||
if opts.SelfSigned {
|
||||
s.Println(s.Dim(" TLS ") + s.Yellow("self-signed") + s.Dim(" · accept the browser warning once"))
|
||||
}
|
||||
if opts.HTTPRedirect {
|
||||
s.Println(s.Dim(" Redirect ") + fmt.Sprintf("http://127.0.0.1:%d/ → HTTPS", port))
|
||||
s.Println(s.Dim(" Redirect ") + fmt.Sprintf("http://%s/ → HTTPS", net.JoinHostPort(hosts[0], strconv.Itoa(port))))
|
||||
}
|
||||
s.BlankLine()
|
||||
}
|
||||
|
||||
@@ -1,10 +1,104 @@
|
||||
package termout
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStartupHostsExplicitHost(t *testing.T) {
|
||||
for _, host := range []string{"192.168.1.5", "127.0.0.1", "10.0.0.8"} {
|
||||
got := startupHosts(host)
|
||||
if len(got) != 1 || got[0] != host {
|
||||
t.Fatalf("startupHosts(%q) = %v, want [%q]", host, got, host)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartupHostsWildcardExpandsToLocalAddresses(t *testing.T) {
|
||||
for _, host := range []string{"", "0.0.0.0", "::", "[::]"} {
|
||||
got := startupHosts(host)
|
||||
if len(got) == 0 || got[0] != "127.0.0.1" {
|
||||
t.Fatalf("startupHosts(%q) = %v, want first entry 127.0.0.1", host, got)
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for _, h := range got {
|
||||
if seen[h] {
|
||||
t.Fatalf("startupHosts(%q) contains duplicate %q", host, h)
|
||||
}
|
||||
seen[h] = true
|
||||
ip := net.ParseIP(h)
|
||||
if ip == nil {
|
||||
t.Fatalf("startupHosts(%q) returned non-IP %q", host, h)
|
||||
}
|
||||
if ip.IsLoopback() && h != "127.0.0.1" {
|
||||
t.Fatalf("startupHosts(%q) returned unexpected loopback %q", host, h)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintStartupWebUIReflectsConfiguredHost(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
printStartupWebUI(&buf, StartupWebUIOptions{
|
||||
Scheme: "http",
|
||||
Host: "192.168.1.5",
|
||||
Port: 8080,
|
||||
})
|
||||
out := buf.String()
|
||||
if !strings.Contains(out, "http://192.168.1.5:8080/") {
|
||||
t.Fatalf("banner should show configured host, got:\n%s", out)
|
||||
}
|
||||
if strings.Contains(out, "127.0.0.1") {
|
||||
t.Fatalf("banner should not fall back to 127.0.0.1 for explicit host, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintStartupWebUIWildcardShowsLoopbackFirst(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
printStartupWebUI(&buf, StartupWebUIOptions{
|
||||
Scheme: "https",
|
||||
Host: "0.0.0.0",
|
||||
Port: 8443,
|
||||
})
|
||||
out := buf.String()
|
||||
if !strings.Contains(out, "https://127.0.0.1:8443/") {
|
||||
t.Fatalf("wildcard banner should include loopback URL, got:\n%s", out)
|
||||
}
|
||||
for _, h := range startupHosts("0.0.0.0")[1:] {
|
||||
if !strings.Contains(out, "https://"+h+":8443/") {
|
||||
t.Fatalf("wildcard banner should include network URL for %s, got:\n%s", h, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintStartupWebUIIPv6HostBracketed(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
printStartupWebUI(&buf, StartupWebUIOptions{
|
||||
Scheme: "http",
|
||||
Host: "::1",
|
||||
Port: 8080,
|
||||
})
|
||||
if !strings.Contains(buf.String(), "http://[::1]:8080/") {
|
||||
t.Fatalf("IPv6 host should be bracketed in URL, got:\n%s", buf.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintStartupWebUIRedirectUsesConfiguredHost(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
printStartupWebUI(&buf, StartupWebUIOptions{
|
||||
Scheme: "https",
|
||||
Host: "10.1.2.3",
|
||||
Port: 8080,
|
||||
HTTPRedirect: true,
|
||||
})
|
||||
out := buf.String()
|
||||
if !strings.Contains(out, "http://10.1.2.3:8080/") || !strings.Contains(out, "https://10.1.2.3:8080/") {
|
||||
t.Fatalf("redirect line should use configured host, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisplayWidthEmoji(t *testing.T) {
|
||||
if got := displayWidth("🚀"); got != 2 {
|
||||
t.Fatalf("displayWidth(emoji) = %d, want 2", got)
|
||||
|
||||
Reference in New Issue
Block a user