fix(media): resolve public image URLs from request and configurable base#623
Merged
Merged
Conversation
…is empty When deployed behind a reverse proxy or in Docker, generated image URLs returned to clients contained the internal loopback address (http://127.0.0.1:8000/...), making them unusable from the public network. The operator had to manually set frontend.publicApiBaseURL to work around this. This change introduces a two-layer resolution strategy: 1. Request-derived base URL: the HTTP handler now derives the public base from the incoming request (honouring X-Forwarded-Proto / X-Forwarded-Host) and threads it through the gateway, provider, and web adapter layers down to PublicImageURL. This makes URLs correct out of the box without any config change. 2. Fallback chain: PublicImageURL resolves the base in priority order: request-derived URL -> hot-updatable config publicApiBaseURL -> "http://127.0.0.1:8000" last-resort default. The config field is now optional (empty is valid); when set it still takes priority over the request-derived value, allowing operators to pin a specific public endpoint. Video generation is unaffected because it uses hardcoded CDN URLs (https://assets.grok.com/...). Tests updated: - protocol_test.go: stub signature + imageDataItem calls adjusted for the new publicBaseURL parameter. - service_test.go: PublicImageURL call updated to two-arg signature. - config_test.go: empty string removed from the invalid publicApiBaseURL values now that empty is accepted.
…eference Expose frontend.publicApiBaseURL and frontend.preferRequestBaseURL through the admin settings API and UI so operators can control image/public URL generation without editing YAML or restarting the process. Resolution priority is now switch-aware: 1. request-derived base when preferRequestBaseURL is enabled 2. configured publicApiBaseURL when set 3. request-derived base as fallback 4. http://127.0.0.1:8000 as last resort Also hot-update /api/admin/v1/system so docs examples reflect the live configured base URL after settings changes.
# Conflicts: # backend/internal/application/gateway/service.go # backend/internal/application/media/service.go # frontend/src/shared/i18n/index.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
That URL is unusable for clients calling the service through LAN IP, reverse proxy, Docker host mapping, or a public domain. Operators had to hand-edit
frontend.publicApiBaseURLand restart, which is easy to miss in multi-environment deployments.This PR solves that in two tightly related commits on the same branch:
0363483— derive public image base URL from the incoming request9b661ea— make public base URL + request-priority switch configurable in admin SettingsVideo generation is intentionally unchanged. Video assets already use upstream CDN URLs such as
https://assets.grok.com/..., so they do not suffer from the localhost media URL problem.Problem
Before
http://127.0.0.1:8000After
Final user-facing effect
Image generation response
When a client calls the API via a reachable host, the returned media URL uses that host instead of loopback.
Example request:
Example response after this PR (with request priority enabled, default):
{ "created": 1784006494, "data": [ { "mime_type": "image/jpeg", "revised_prompt": "", "url": "http://192.168.2.103:8000/v1/media/images/img_xxx" } ] }If the operator configures a fixed public base and disables request priority, the response becomes:
{ "data": [ { "url": "https://api.example.com/v1/media/images/img_xxx" } ] }Admin Settings UI
In Settings → Media storage, two new controls are available:
Public API base URL
Prefer request base URL
trueBoth fields include short helper text in Chinese and English.
Docs / system info
GET /api/admin/v1/systemnow returns the live configuredpublicApiBaseURL.That means:
URL resolution priority
Image public URL base is resolved in this order:
preferRequestBaseURL=trueand request host is availablepublicApiBaseURL, if non-emptyhttp://127.0.0.1:8000as last-resort fallbackRequest-derived base honors:
X-Forwarded-ProtoX-Forwarded-HostBackend changes
Request-derived public base
PublicBaseURLthrough:PublicImageURLfrontend.publicApiBaseURLis now valid configurationMedia URL generation
media.Service.PublicImageURLnow uses switch-aware priority resolutionPreferRequestBaseURLto media runtime configSettings / config model
publicApiBaseURLpreferRequestBaseURLpreferRequestBaseURLdefaults totrueSystem endpoint
/api/admin/v1/systemno longer freezes the startup-time public base URLTests
Frontend changes
Settings form
SettingsFielddescription supporthttp/httpsAPI model / mapping
frontend.publicApiBaseURLfrontend.preferRequestBaseURLi18n
Why this is one PR, not two
These two commits are one product fix:
Splitting them would create incomplete review context and overlapping changes in:
media/service.goOut of scope
Test plan
tsc -b/api/admin/v1/systemwithout restart127.0.0.1192.168.x.xX-Forwarded-*Commits
0363483fix(media): derive image URL base from request when publicApiBaseURL is empty9b661eafeat(settings): make public API base URL configurable with request preference(https://github.com/chenyme/grok2api/compare/main...hamburg888:codex/fix-media-url-localhost?expand=1)