Skip to content

Commit 0b848fa

Browse files
milochen0418masenfclaude
authored
feat: add vite_allowed_hosts config option for Vite dev server (#6147)
* feat: add vite_allowed_hosts config option for Vite dev server Add a configurable `vite_allowed_hosts` field to `rx.Config` that controls whether `allowedHosts: true` is set in the Vite dev server configuration. This allows users running Reflex in Docker, Codespaces, or behind a reverse proxy to opt-in to allowing all hosts, preventing 403 Forbidden errors from Vite's host header validation. Default is `False` (original behavior, Vite only allows localhost). Users can enable it in rxconfig.py: config = rx.Config(app_name="myapp", vite_allowed_hosts=True) Or via environment variable: REFLEX_VITE_ALLOWED_HOSTS=true reflex run * refactor: support bool | list[str] for vite_allowed_hosts * feat: support union types in interpret_env_var_value Instead of raising an error for union types, try each type in the union in order and return the first successful interpretation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Masen Furer <m_github@0x26.net> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 72ecbe5 commit 0b848fa

5 files changed

Lines changed: 36 additions & 7 deletions

File tree

reflex/compiler/templates.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,7 @@ def vite_config_template(
502502
force_full_reload: bool,
503503
experimental_hmr: bool,
504504
sourcemap: bool | Literal["inline", "hidden"],
505+
allowed_hosts: bool | list[str] = False,
505506
):
506507
"""Template for vite.config.js.
507508
@@ -511,10 +512,17 @@ def vite_config_template(
511512
force_full_reload: Whether to force a full reload on changes.
512513
experimental_hmr: Whether to enable experimental HMR features.
513514
sourcemap: The sourcemap configuration.
515+
allowed_hosts: Allow all hosts (True), specific hosts (list of strings), or only localhost (False).
514516
515517
Returns:
516518
Rendered vite.config.js content as string.
517519
"""
520+
if allowed_hosts is True:
521+
allowed_hosts_line = "\n allowedHosts: true,"
522+
elif isinstance(allowed_hosts, list) and allowed_hosts:
523+
allowed_hosts_line = f"\n allowedHosts: {json.dumps(allowed_hosts)},"
524+
else:
525+
allowed_hosts_line = ""
518526
return rf"""import {{ fileURLToPath, URL }} from "url";
519527
import {{ reactRouter }} from "@react-router/dev/vite";
520528
import {{ defineConfig }} from "vite";
@@ -586,7 +594,7 @@ def vite_config_template(
586594
hmr: {"true" if experimental_hmr else "false"},
587595
}},
588596
server: {{
589-
port: process.env.PORT,
597+
port: process.env.PORT,{allowed_hosts_line}
590598
hmr: {"true" if hmr else "false"},
591599
watch: {{
592600
ignored: [

reflex/config.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,11 @@ class BaseConfig:
213213
dataclasses.field(default=("*",))
214214
)
215215

216+
# Allowed hosts for the Vite dev server. Set to True to allow all hosts,
217+
# or provide a list of hostnames (e.g. ["myservice.local"]) to allow specific ones.
218+
# Prevents 403 errors in Docker, Codespaces, reverse proxies, etc.
219+
vite_allowed_hosts: bool | list[str] = False
220+
216221
# Whether to use React strict mode.
217222
react_strict_mode: bool = True
218223

reflex/environment.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,14 @@ def interpret_env_var_value(
243243
field_type = value_inside_optional(field_type)
244244

245245
if is_union(field_type):
246-
msg = f"Union types are not supported for environment variables: {field_name}."
247-
raise ValueError(msg)
246+
errors = []
247+
for arg in (union_types := get_args(field_type)):
248+
try:
249+
return interpret_env_var_value(value, arg, field_name)
250+
except (ValueError, EnvironmentVarValueError) as e: # noqa: PERF203
251+
errors.append(e)
252+
msg = f"Could not interpret {value!r} for {field_name} as any of {union_types}: {errors}"
253+
raise EnvironmentVarValueError(msg)
248254

249255
value = value.strip()
250256

reflex/utils/frontend_skeleton.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ def _compile_vite_config(config: Config):
197197
force_full_reload=environment.VITE_FORCE_FULL_RELOAD.get(),
198198
experimental_hmr=environment.VITE_EXPERIMENTAL_HMR.get(),
199199
sourcemap=environment.VITE_SOURCEMAP.get(),
200+
allowed_hosts=config.vite_allowed_hosts,
200201
)
201202

202203

tests/units/test_environment.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,10 +190,19 @@ def test_interpret_enum(self):
190190
result = interpret_env_var_value("value1", _TestEnum, "TEST_FIELD")
191191
assert result == _TestEnum.VALUE1
192192

193-
def test_interpret_union_error(self):
194-
"""Test that union types raise an error."""
195-
with pytest.raises(ValueError, match="Union types are not supported"):
196-
interpret_env_var_value("test", int | str, "TEST_FIELD")
193+
def test_interpret_union_tries_each_type(self):
194+
"""Test that union types try each type in order."""
195+
# str matches first
196+
assert interpret_env_var_value("hello", int | str, "TEST_FIELD") == "hello"
197+
# int matches first
198+
assert interpret_env_var_value("42", int | str, "TEST_FIELD") == 42
199+
# bool matches before str
200+
assert interpret_env_var_value("true", bool | str, "TEST_FIELD") is True
201+
202+
def test_interpret_union_no_match(self):
203+
"""Test that union types raise an error if no type matches."""
204+
with pytest.raises(EnvironmentVarValueError, match="Could not interpret"):
205+
interpret_env_var_value("not_a_number", int | bool, "TEST_FIELD")
197206

198207
def test_interpret_unsupported_type(self):
199208
"""Test unsupported type raises an error."""

0 commit comments

Comments
 (0)