diff --git a/.env.example b/.env.example index 019eea9..71cbc60 100644 --- a/.env.example +++ b/.env.example @@ -22,4 +22,4 @@ WHITELIST_EMAILS=user1@example.com,user2@example.com # === Optional === IMAP_PROXY_URL= LISTEN_ADDR=:8080 -CALLBACK_BASE_URL=http://localhost +CALLBACK_BASE_URL=http://localhost:8080 diff --git a/README.md b/README.md index 07837e9..014ad0c 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ docker logs -f claw-email-bridge | `WHITELIST_EMAILS` | | Danh sách email được phép, phân cách bằng dấu phẩy | | `IMAP_PROXY_URL` | | SOCKS5 proxy cho IMAP (vd: `socks5://user:pass@host:port`) | | `LISTEN_ADDR` | | Địa chỉ bind HTTP server cục bộ (mặc định `:8080`) | -| `CALLBACK_BASE_URL` | | Base URL public để tạo `callback_url` gửi OpenClaw (mặc định `http://localhost`) | +| `CALLBACK_BASE_URL` | | Base URL public để tạo `callback_url` gửi OpenClaw (mặc định theo `LISTEN_ADDR`, ví dụ `http://localhost:8080`) | ## Health Checks diff --git a/internal/ai_client/client.go b/internal/ai_client/client.go index b4e82b3..1e1705c 100644 --- a/internal/ai_client/client.go +++ b/internal/ai_client/client.go @@ -133,7 +133,7 @@ func (d *Dispatcher) Dispatch(task *database.Task) { func buildCallbackURL(base string) string { trimmed := strings.TrimRight(base, "/") if trimmed == "" { - trimmed = "http://localhost" + trimmed = "http://localhost:8080" } return trimmed + "/callback" } diff --git a/internal/config/config.go b/internal/config/config.go index 881a4b6..7cbe3d1 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -2,6 +2,7 @@ package config import ( "fmt" + "net" "os" "strings" @@ -77,7 +78,7 @@ func Load() (*Config, error) { cfg.ListenAddr = ":8080" } if cfg.CallbackBaseURL == "" { - cfg.CallbackBaseURL = "http://localhost" + cfg.CallbackBaseURL = defaultCallbackBaseURL(cfg.ListenAddr) } if err := cfg.validate(); err != nil { @@ -87,6 +88,17 @@ func Load() (*Config, error) { return cfg, nil } +func defaultCallbackBaseURL(listenAddr string) string { + host, port, err := net.SplitHostPort(listenAddr) + if err != nil || port == "" { + return "http://localhost:8080" + } + if host == "" || host == "0.0.0.0" || host == "::" { + host = "localhost" + } + return fmt.Sprintf("http://%s:%s", host, port) +} + // validate checks that all required configuration values are present. func (c *Config) validate() error { required := map[string]string{ diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 0d8b6c8..dd702a2 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -30,8 +30,23 @@ func TestLoad_DefaultCallbackBaseURL_WhenUnset(t *testing.T) { t.Fatalf("load config: %v", err) } - if cfg.CallbackBaseURL != "http://localhost" { - t.Fatalf("expected default callback base URL http://localhost, got %q", cfg.CallbackBaseURL) + if cfg.CallbackBaseURL != "http://localhost:8080" { + t.Fatalf("expected default callback base URL http://localhost:8080, got %q", cfg.CallbackBaseURL) + } +} + +func TestLoad_DefaultCallbackBaseURL_UsesListenPort(t *testing.T) { + setRequiredEnv(t) + t.Setenv("CALLBACK_BASE_URL", "") + t.Setenv("LISTEN_ADDR", ":9090") + + cfg, err := Load() + if err != nil { + t.Fatalf("load config: %v", err) + } + + if cfg.CallbackBaseURL != "http://localhost:9090" { + t.Fatalf("expected callback base URL to follow LISTEN_ADDR port, got %q", cfg.CallbackBaseURL) } }