Skip to content

fix(web): load public API URL from runtime config in Docker#1219

Open
tinsever wants to merge 1 commit into
mainfrom
fix/1218-docker-api-url-mixed-content
Open

fix(web): load public API URL from runtime config in Docker#1219
tinsever wants to merge 1 commit into
mainfrom
fix/1218-docker-api-url-mixed-content

Conversation

@tinsever
Copy link
Copy Markdown
Member

@tinsever tinsever commented May 2, 2026

Description

Adds a small runtime env-config.js generated at container startup from KANEO_API_URL and KANEO_CLIENT_URL (base64-encoded in env.sh so URLs cannot break the shell), and loads it before the SPA bundle. The web app and @kaneo/libs Hono client now resolve VITE_API_URL / VITE_CLIENT_URL via resolvePublicEnvVar(), which prefers window.__KANEO_RUNTIME_CONFIG__ over build-time placeholders. This avoids relying on sed inside hashed JS chunks for HTTPS reverse-proxy setups where the browser was still calling http://<internal host>:1337 (mixed content).

Related Issue(s)

Fixes #1218

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement
  • Test addition or update
  • Other (please describe):

How Has This Been Tested?

  • Unit tests (@kaneo/libsruntime-public-env.test.ts; existing api-url tests)
  • Integration tests
  • Manual testing (production pnpm web build; simulated env-config.js generation / atob decode)
  • Other (please describe):

Screenshots (if applicable)

N/A

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published

Summary by CodeRabbit

  • Chores

    • Infrastructure now supports configurable environment settings at deployment time, enabling faster environment setup without rebuilding the application for different deployment targets.
  • Tests

    • Added test coverage for runtime environment variable resolution.

Issue #1218: HTTPS deployments could call auth/API over HTTP when
in-bundle sed substitution missed chunks or used the wrong value.

Write env-config.js at container start from KANEO_API_URL and
KANEO_CLIENT_URL (base64) and read it before the SPA bundle so the
Better Auth client and fetchers use the public URL reliably.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 2, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b3e36047-a973-47f7-b4ff-45f56cfbf1dc

📥 Commits

Reviewing files that changed from the base of the PR and between 340a930 and 70a6a0a.

📒 Files selected for processing (10)
  • apps/web/env.sh
  • apps/web/index.html
  • apps/web/public/env-config.js
  • apps/web/src/fetchers/get-api-url.ts
  • apps/web/src/lib/auth-client.ts
  • apps/web/src/routes/auth/sign-in.tsx
  • packages/libs/src/hono.ts
  • packages/libs/src/index.ts
  • packages/libs/src/runtime-public-env.test.ts
  • packages/libs/src/runtime-public-env.ts

📝 Walkthrough

Walkthrough

This PR implements runtime environment variable resolution for API URLs. A container startup script generates a JavaScript configuration file with base64-encoded KANEO_API_URL and KANEO_CLIENT_URL values, which is loaded before the React app initializes. A new library function resolves these values at runtime, falling back to build-time environment variables when unavailable.

Changes

Runtime Environment Variable Resolution

Layer / File(s) Summary
Type Definitions & Global Augmentation
packages/libs/src/runtime-public-env.ts (lines 1–10)
Defines KaneoRuntimePublicConfig type with optional VITE_API_URL and VITE_CLIENT_URL fields; augments Window global with __KANEO_RUNTIME_CONFIG__ property.
Resolution Implementation
packages/libs/src/runtime-public-env.ts (lines 12–30)
Implements resolvePublicEnvVar(key, buildValue) that reads from window.__KANEO_RUNTIME_CONFIG__, trimming whitespace, and falls back to the provided build-time value when runtime config is unavailable or empty.
Library Exports
packages/libs/src/index.ts
Re-exports KaneoRuntimePublicConfig type and resolvePublicEnvVar function from the new runtime-public-env module.
Container Startup Configuration
apps/web/env.sh
Adds write_runtime_config_js() function that reads KANEO_API_URL and KANEO_CLIENT_URL environment variables, base64-encodes them for safety, and writes /usr/share/nginx/html/env-config.js with window.__KANEO_RUNTIME_CONFIG__ using atob() decoding.
Frontend Initialization
apps/web/public/env-config.js, apps/web/index.html
Placeholder file initializes window.__KANEO_RUNTIME_CONFIG__ as empty object; HTML loads /env-config.js before the React app entrypoint.
Fetcher Integration
apps/web/src/fetchers/get-api-url.ts, apps/web/src/lib/auth-client.ts
Both modules update base URL resolution to use resolvePublicEnvVar("VITE_API_URL", ...) instead of direct import.meta.env.VITE_API_URL, maintaining existing fallback and normalization logic.
Sign-in Route Integration
apps/web/src/routes/auth/sign-in.tsx
Adds getClientBaseUrl() helper using resolvePublicEnvVar("VITE_CLIENT_URL", ...) and updates OAuth error callback URLs (custom, Google, GitHub, Discord) to use the resolved client base URL.
Hono Client Integration
packages/libs/src/hono.ts
Updates API client initialization to resolve VITE_API_URL via resolvePublicEnvVar() before passing to resolveApiBaseUrl().
Tests
packages/libs/src/runtime-public-env.test.ts
Comprehensive Vitest suite validates: (1) fallback to build-time value when window is undefined, (2) runtime value preference when available in __KANEO_RUNTIME_CONFIG__, (3) fallback when runtime value is whitespace-only.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Suggested reviewers

  • andrejsshell
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(web): load public API URL from runtime config in Docker' directly and clearly describes the main change: loading API URLs from runtime config instead of build-time values in Docker deployments.
Linked Issues check ✅ Passed Changes implement runtime config loading to resolve mixed-content issues where frontend called internal Docker IP instead of public KANEO_API_URL/KANEO_CLIENT_URL, directly addressing the root cause described in issue #1218.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing runtime environment variable resolution: adding env.sh config generation, runtime config initialization, and updating URL resolution across the codebase to use the runtime values.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1218-docker-api-url-mixed-content

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@qodo-free-for-open-source-projects
Copy link
Copy Markdown

Review Summary by Qodo

Load public API/client URLs from runtime config in Docker to fix mixed-content issues

🐞 Bug fix 🧪 Tests

Grey Divider

Walkthroughs

Description
• Implement runtime config loading to resolve API/client URLs at browser startup
• Add resolvePublicEnvVar() utility to prefer runtime config over build-time values
• Generate env-config.js at container startup from base64-encoded environment variables
• Update all API/client URL references to use runtime resolution for HTTPS proxy compatibility
• Add comprehensive unit tests for runtime environment variable resolution
Diagram
flowchart LR
  A["env.sh at startup"] -->|"base64 encode KANEO_API_URL/KANEO_CLIENT_URL"| B["Generate env-config.js"]
  B -->|"window.__KANEO_RUNTIME_CONFIG__"| C["Browser loads SPA"]
  C -->|"resolvePublicEnvVar()"| D["API/Client URLs resolved at runtime"]
  D -->|"Use public HTTPS URL"| E["Reverse proxy compatibility"]
Loading

Grey Divider

File Changes

1. packages/libs/src/runtime-public-env.ts ✨ Enhancement +30/-0

New runtime environment variable resolution utility

packages/libs/src/runtime-public-env.ts


2. packages/libs/src/runtime-public-env.test.ts 🧪 Tests +32/-0

Unit tests for runtime config resolution

packages/libs/src/runtime-public-env.test.ts


3. packages/libs/src/index.ts ✨ Enhancement +2/-0

Export runtime config types and resolver

packages/libs/src/index.ts


View more (7)
4. packages/libs/src/hono.ts 🐞 Bug fix +4/-1

Use runtime-resolved API URL for Hono client

packages/libs/src/hono.ts


5. apps/web/src/fetchers/get-api-url.ts 🐞 Bug fix +4/-1

Resolve API URL from runtime config

apps/web/src/fetchers/get-api-url.ts


6. apps/web/src/lib/auth-client.ts 🐞 Bug fix +4/-1

Resolve API URL from runtime config for auth

apps/web/src/lib/auth-client.ts


7. apps/web/src/routes/auth/sign-in.tsx 🐞 Bug fix +10/-5

Resolve client URL from runtime config

apps/web/src/routes/auth/sign-in.tsx


8. apps/web/public/env-config.js ⚙️ Configuration changes +1/-0

Placeholder runtime config file

apps/web/public/env-config.js


9. apps/web/env.sh ⚙️ Configuration changes +21/-0

Generate runtime config from environment variables

apps/web/env.sh


10. apps/web/index.html ✨ Enhancement +1/-0

Load runtime config before SPA bundle

apps/web/index.html


Grey Divider

Qodo Logo

@tinsever tinsever requested a review from andrejsshell May 2, 2026 10:58
@qodo-free-for-open-source-projects
Copy link
Copy Markdown

qodo-free-for-open-source-projects Bot commented May 2, 2026

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📎 Requirement gaps (1)

Context used

Grey Divider


Action required

1. getApiUrl() uses HTTP fallback 📎 Requirement gap ⛨ Security
Description
getApiUrl() still falls back to http://localhost:1337 when VITE_API_URL is missing, which can
reintroduce non-HTTPS API calls in HTTPS deployments (Mixed Content). This violates the requirement
to respect the configured public KANEO_API_URL without HTTP/Docker-network fallbacks.
Code

apps/web/src/fetchers/get-api-url.ts[R5-6]

+    resolvePublicEnvVar("VITE_API_URL", import.meta.env.VITE_API_URL) ||
+    "http://localhost:1337"
Evidence
Compliance ID 1 forbids browser API calls falling back to non-HTTPS/internal endpoints. The changed
code still hardcodes an HTTP base URL fallback (http://localhost:1337).

Frontend must use configured KANEO_API_URL (no fallback to Docker IP / HTTP)
apps/web/src/fetchers/get-api-url.ts[5-6]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`getApiUrl()` falls back to `http://localhost:1337`, which can cause Mixed Content when the page is served over HTTPS and `VITE_API_URL` is not correctly populated.

## Issue Context
This PR’s objective is to ensure the frontend always uses the configured public API URL from runtime config (`KANEO_API_URL` -> `window.__KANEO_RUNTIME_CONFIG__`). Keeping an HTTP fallback undermines that requirement.

## Fix Focus Areas
- apps/web/src/fetchers/get-api-url.ts[5-6]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. import.meta.env crashes Node 🐞 Bug ☼ Reliability
Description
packages/libs/src/hono.ts evaluates import.meta.env.VITE_API_URL while initializing the exported
Hono client, and in runtimes where import.meta.env is undefined (plain Node ESM/SSR without Vite
injection) this throws before resolvePublicEnvVar() can run. This makes importing @kaneo/libs
crash at module load time in those contexts.
Code

packages/libs/src/hono.ts[R8-10]

+const apiUrl = resolveApiBaseUrl(
+  resolvePublicEnvVar("VITE_API_URL", import.meta.env.VITE_API_URL),
+);
Evidence
resolvePublicEnvVar() only guards access to window, but the call site still dereferences
import.meta.env.VITE_API_URL to compute the argument; if import.meta.env is missing, the
dereference throws before the function can apply its fallback behavior.

packages/libs/src/hono.ts[8-10]
packages/libs/src/runtime-public-env.ts[12-18]
packages/libs/src/index.ts[1-4]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`resolvePublicEnvVar()` is meant to be safe when `window` is unavailable, but current call sites still access `import.meta.env.VITE_API_URL` eagerly. In non-Vite Node/SSR environments, `import.meta.env` may be undefined, causing a module-load crash before `resolvePublicEnvVar()` can return a fallback.

### Issue Context
This is most impactful because `@kaneo/libs` exports `client` from `hono.ts` via the package entry, so any import of the package in Node/SSR can trigger evaluation.

### Fix Focus Areas
- packages/libs/src/hono.ts[8-10]

### Suggested fix
- Change the build-time read to a safe optional access, e.g. `(import.meta as any).env?.VITE_API_URL`, and pass that value into `resolvePublicEnvVar()`.
- (Optional hardening) Consider making the exported `client` lazy-initialized to avoid module-load side effects when consumers only need non-browser utilities.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. env-config missing no-store 🐞 Bug ☼ Reliability
Description
The app now always loads /env-config.js, which is generated at container startup and can change
between deployments, but nginx serves it without explicit Cache-Control: no-store/no-cache.
Without an explicit header, browsers/intermediaries can reuse a cached env-config.js across
redeploys and keep calling an old API URL, undermining the runtime-config approach.
Code

apps/web/index.html[57]

+  <script src="/env-config.js"></script>
Evidence
env.sh overwrites /usr/share/nginx/html/env-config.js on container startup, and index.html
loads it unconditionally. The nginx config serves static files from /usr/share/nginx/html but does
not set any cache-control policy specific to env-config.js, so it is not protected from caching
the way a runtime-varying config file typically should be.

apps/web/index.html[55-58]
apps/web/env.sh[4-24]
apps/web/nginx.conf[33-38]
apps/web/Dockerfile[52-61]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`/env-config.js` is runtime-generated and can change between container starts/deployments, but it is served like a normal static JS file. To ensure configuration changes take effect immediately and predictably, it should not be cached.

### Issue Context
The HTML now loads `/env-config.js` before the SPA module bundle, and `env.sh` writes the file at container startup.

### Fix Focus Areas
- apps/web/index.html[55-58]
- apps/web/env.sh[4-24]
- apps/web/nginx.conf[33-38]

### Suggested fix
In `apps/web/nginx.conf`, add a dedicated location block:
- `location = /env-config.js { add_header Cache-Control "no-store, no-cache, must-revalidate" always; }`
Optionally also add `expires -1;` and/or disable etags for this file if desired.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines +5 to +6
resolvePublicEnvVar("VITE_API_URL", import.meta.env.VITE_API_URL) ||
"http://localhost:1337"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. getapiurl() uses http fallback 📎 Requirement gap ⛨ Security

getApiUrl() still falls back to http://localhost:1337 when VITE_API_URL is missing, which can
reintroduce non-HTTPS API calls in HTTPS deployments (Mixed Content). This violates the requirement
to respect the configured public KANEO_API_URL without HTTP/Docker-network fallbacks.
Agent Prompt
## Issue description
`getApiUrl()` falls back to `http://localhost:1337`, which can cause Mixed Content when the page is served over HTTPS and `VITE_API_URL` is not correctly populated.

## Issue Context
This PR’s objective is to ensure the frontend always uses the configured public API URL from runtime config (`KANEO_API_URL` -> `window.__KANEO_RUNTIME_CONFIG__`). Keeping an HTTP fallback undermines that requirement.

## Fix Focus Areas
- apps/web/src/fetchers/get-api-url.ts[5-6]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: KANEO_API_URL refers to docker IP address

1 participant