diff --git a/core/cli/run.go b/core/cli/run.go index 75a521572..fd7ba8cd9 100644 --- a/core/cli/run.go +++ b/core/cli/run.go @@ -140,7 +140,7 @@ type RunCMD struct { OIDCIssuer string `env:"LOCALAI_OIDC_ISSUER" help:"OIDC issuer URL for auto-discovery" group:"auth"` OIDCClientID string `env:"LOCALAI_OIDC_CLIENT_ID" help:"OIDC Client ID (auto-enables auth)" group:"auth"` OIDCClientSecret string `env:"LOCALAI_OIDC_CLIENT_SECRET" help:"OIDC Client Secret" group:"auth"` - ExternalBaseURL string `env:"LOCALAI_BASE_URL" help:"External base URL of this instance (e.g. https://localhost:8080). Used for OAuth callbacks and self-referential links (generated images/videos, job status). When unset, derived from X-Forwarded-Proto/Host or Forwarded headers." group:"auth"` + ExternalBaseURL string `env:"LOCALAI_BASE_URL" help:"External base URL of this instance (e.g. https://localhost:8080). Used for OAuth callbacks and self-referential links (generated images/videos, job status). When unset, derived from X-Forwarded-Proto/Host or Forwarded headers." group:"api"` AuthAdminEmail string `env:"LOCALAI_ADMIN_EMAIL" help:"Email address to auto-promote to admin role" group:"auth"` AuthRegistrationMode string `env:"LOCALAI_REGISTRATION_MODE" default:"open" help:"Registration mode: 'open' (default), 'approval', or 'invite' (invite code required)" group:"auth"` DisableLocalAuth bool `env:"LOCALAI_DISABLE_LOCAL_AUTH" default:"false" help:"Disable local email/password registration and login (use with OAuth/OIDC-only setups)" group:"auth"` diff --git a/core/http/middleware/baseurl_test.go b/core/http/middleware/baseurl_test.go index be38865af..6a132514b 100644 --- a/core/http/middleware/baseurl_test.go +++ b/core/http/middleware/baseurl_test.go @@ -227,4 +227,46 @@ var _ = Describe("BaseURL", func() { Expect(actualURL).To(Equal("http://example.com/")) }) }) + + Context("parseForwarded helper", func() { + It("parses unquoted proto and host", func() { + proto, host := parseForwarded("for=192.0.2.1;proto=https;host=h.example") + Expect(proto).To(Equal("https")) + Expect(host).To(Equal("h.example")) + }) + + It("strips quotes around values", func() { + proto, host := parseForwarded(`proto="https";host="h.example"`) + Expect(proto).To(Equal("https")) + Expect(host).To(Equal("h.example")) + }) + + It("uses only the first element of a multi-element header", func() { + proto, host := parseForwarded("proto=https;host=first.example, proto=http;host=second.example") + Expect(proto).To(Equal("https")) + Expect(host).To(Equal("first.example")) + }) + + It("returns empty strings for an empty header", func() { + proto, host := parseForwarded("") + Expect(proto).To(BeEmpty()) + Expect(host).To(BeEmpty()) + }) + + It("skips directives without a value", func() { + proto, host := parseForwarded("proto;host=h.example") + Expect(proto).To(BeEmpty()) + Expect(host).To(Equal("h.example")) + }) + }) + + Context("firstToken helper", func() { + It("returns the whole trimmed string when there is no comma", func() { + Expect(firstToken(" https ")).To(Equal("https")) + }) + + It("returns the first trimmed token when there is a comma", func() { + Expect(firstToken("https , http")).To(Equal("https")) + }) + }) })