Skip to content

Commit 0a33cc3

Browse files
authored
auto hide badge for pro+ users for cloud deployments (#4819)
* auto hide badge for pro+ users for cloud deployments * update integrations tests * fix integrations for real
1 parent 946b7bc commit 0a33cc3

File tree

8 files changed

+92
-36
lines changed

8 files changed

+92
-36
lines changed

reflex/app.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@
111111
prerequisites,
112112
types,
113113
)
114-
from reflex.utils.exec import is_prod_mode, is_testing_env
114+
from reflex.utils.exec import get_compile_context, is_prod_mode, is_testing_env
115115
from reflex.utils.imports import ImportVar
116116

117117
if TYPE_CHECKING:
@@ -201,14 +201,17 @@ def default_overlay_component() -> Component:
201201
Returns:
202202
The default overlay_component, which is a connection_modal.
203203
"""
204-
config = get_config()
205204
from reflex.components.component import memo
206205

207206
def default_overlay_components():
208207
return Fragment.create(
209208
connection_pulser(),
210209
connection_toaster(),
211-
*([backend_disabled()] if config.is_reflex_cloud else []),
210+
*(
211+
[backend_disabled()]
212+
if get_compile_context() == constants.CompileContext.DEPLOY
213+
else []
214+
),
212215
*codespaces.codespaces_auto_redirect(),
213216
)
214217

@@ -1136,6 +1139,16 @@ def memoized_toast_provider():
11361139

11371140
self._validate_var_dependencies()
11381141
self._setup_overlay_component()
1142+
1143+
if config.show_built_with_reflex is None:
1144+
if (
1145+
get_compile_context() == constants.CompileContext.DEPLOY
1146+
and prerequisites.get_user_tier() in ["pro", "team", "enterprise"]
1147+
):
1148+
config.show_built_with_reflex = False
1149+
else:
1150+
config.show_built_with_reflex = True
1151+
11391152
if is_prod_mode() and config.show_built_with_reflex:
11401153
self._setup_sticky_badge()
11411154

reflex/config.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,11 @@ def submit(
589589
class EnvironmentVariables:
590590
"""Environment variables class to instantiate environment variables."""
591591

592+
# Indicate the current command that was invoked in the reflex CLI.
593+
REFLEX_COMPILE_CONTEXT: EnvVar[constants.CompileContext] = env_var(
594+
constants.CompileContext.UNDEFINED, internal=True
595+
)
596+
592597
# Whether to use npm over bun to install frontend packages.
593598
REFLEX_USE_NPM: EnvVar[bool] = env_var(False)
594599

@@ -636,7 +641,7 @@ class EnvironmentVariables:
636641
REFLEX_COMPILE_THREADS: EnvVar[Optional[int]] = env_var(None)
637642

638643
# The directory to store reflex dependencies.
639-
REFLEX_DIR: EnvVar[Path] = env_var(Path(constants.Reflex.DIR))
644+
REFLEX_DIR: EnvVar[Path] = env_var(constants.Reflex.DIR)
640645

641646
# Whether to print the SQL queries if the log level is INFO or lower.
642647
SQLALCHEMY_ECHO: EnvVar[bool] = env_var(False)
@@ -844,7 +849,7 @@ class Config: # pyright: ignore [reportIncompatibleVariableOverride]
844849
env_file: Optional[str] = None
845850

846851
# Whether to display the sticky "Built with Reflex" badge on all pages.
847-
show_built_with_reflex: bool = True
852+
show_built_with_reflex: Optional[bool] = None
848853

849854
# Whether the app is running in the reflex cloud environment.
850855
is_reflex_cloud: bool = False

reflex/constants/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from .compiler import (
2626
NOCOMPILE_FILE,
2727
SETTER_PREFIX,
28+
CompileContext,
2829
CompileVars,
2930
ComponentName,
3031
Ext,
@@ -65,6 +66,7 @@
6566
ColorMode,
6667
Config,
6768
COOKIES,
69+
CompileContext,
6870
ComponentName,
6971
CustomComponents,
7072
DefaultPage,

reflex/constants/compiler.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,15 @@ def zip(self):
111111
return self.value.lower() + Ext.ZIP
112112

113113

114+
class CompileContext(str, Enum):
115+
"""The context in which the compiler is running."""
116+
117+
RUN = "run"
118+
EXPORT = "export"
119+
DEPLOY = "deploy"
120+
UNDEFINED = "undefined"
121+
122+
114123
class Imports(SimpleNamespace):
115124
"""Common sets of import vars."""
116125

reflex/reflex.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ def _run(
193193
prerequisites.check_latest_package_version(constants.Reflex.MODULE_NAME)
194194

195195
if frontend:
196-
if not config.show_built_with_reflex:
196+
if config.show_built_with_reflex is False:
197197
# The sticky badge may be disabled at runtime for team/enterprise tiers.
198198
prerequisites.check_config_option_in_tier(
199199
option_name="show_built_with_reflex",
@@ -303,6 +303,8 @@ def run(
303303
if frontend and backend:
304304
console.error("Cannot use both --frontend-only and --backend-only options.")
305305
raise typer.Exit(1)
306+
307+
environment.REFLEX_COMPILE_CONTEXT.set(constants.CompileContext.RUN)
306308
environment.REFLEX_BACKEND_ONLY.set(backend)
307309
environment.REFLEX_FRONTEND_ONLY.set(frontend)
308310

@@ -349,17 +351,19 @@ def export(
349351
from reflex.utils import export as export_utils
350352
from reflex.utils import prerequisites
351353

354+
environment.REFLEX_COMPILE_CONTEXT.set(constants.CompileContext.EXPORT)
355+
352356
frontend, backend = prerequisites.check_running_mode(frontend, backend)
353357

354358
if prerequisites.needs_reinit(frontend=frontend or not backend):
355359
_init(name=config.app_name, loglevel=loglevel)
356360

357-
if frontend and not config.show_built_with_reflex:
361+
if frontend and config.show_built_with_reflex is False:
358362
# The sticky badge may be disabled on export for team/enterprise tiers.
359363
prerequisites.check_config_option_in_tier(
360364
option_name="show_built_with_reflex",
361365
allowed_tiers=["team", "enterprise"],
362-
fallback_value=False,
366+
fallback_value=True,
363367
help_link=SHOW_BUILT_WITH_REFLEX_INFO,
364368
)
365369

@@ -557,6 +561,8 @@ def deploy(
557561

558562
check_version()
559563

564+
environment.REFLEX_COMPILE_CONTEXT.set(constants.CompileContext.DEPLOY)
565+
560566
if not config.show_built_with_reflex:
561567
# The sticky badge may be disabled on deploy for pro/team/enterprise tiers.
562568
prerequisites.check_config_option_in_tier(

reflex/utils/exec.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,3 +584,12 @@ def is_prod_mode() -> bool:
584584
"""
585585
current_mode = environment.REFLEX_ENV_MODE.get()
586586
return current_mode == constants.Env.PROD
587+
588+
589+
def get_compile_context() -> constants.CompileContext:
590+
"""Check if the app is compiled for deploy.
591+
592+
Returns:
593+
Whether the app is being compiled for deploy.
594+
"""
595+
return environment.REFLEX_COMPILE_CONTEXT.get()

reflex/utils/prerequisites.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2001,6 +2001,22 @@ def is_generation_hash(template: str) -> bool:
20012001
return re.match(r"^[0-9a-f]{32,}$", template) is not None
20022002

20032003

2004+
def get_user_tier():
2005+
"""Get the current user's tier.
2006+
2007+
Returns:
2008+
The current user's tier.
2009+
"""
2010+
from reflex_cli.v2.utils import hosting
2011+
2012+
authenticated_token = hosting.authenticated_token()
2013+
return (
2014+
authenticated_token[1].get("tier", "").lower()
2015+
if authenticated_token[0]
2016+
else "anonymous"
2017+
)
2018+
2019+
20042020
def check_config_option_in_tier(
20052021
option_name: str,
20062022
allowed_tiers: list[str],
@@ -2015,23 +2031,21 @@ def check_config_option_in_tier(
20152031
fallback_value: The fallback value if the option is not allowed.
20162032
help_link: The help link to show to a user that is authenticated.
20172033
"""
2018-
from reflex_cli.v2.utils import hosting
2019-
20202034
config = get_config()
2021-
authenticated_token = hosting.authenticated_token()
2022-
if not authenticated_token[0]:
2035+
current_tier = get_user_tier()
2036+
2037+
if current_tier == "anonymous":
20232038
the_remedy = (
20242039
"You are currently logged out. Run `reflex login` to access this option."
20252040
)
2026-
current_tier = "anonymous"
20272041
else:
2028-
current_tier = authenticated_token[1].get("tier", "").lower()
20292042
the_remedy = (
20302043
f"Your current subscription tier is `{current_tier}`. "
20312044
f"Please upgrade to {allowed_tiers} to access this option. "
20322045
)
20332046
if help_link:
20342047
the_remedy += f"See {help_link} for more information."
2048+
20352049
if current_tier not in allowed_tiers:
20362050
console.warn(f"Config option `{option_name}` is restricted. {the_remedy}")
20372051
setattr(config, option_name, fallback_value)

tests/integration/test_connection_banner.py

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,19 @@
77
from selenium.common.exceptions import NoSuchElementException
88
from selenium.webdriver.common.by import By
99

10+
from reflex import constants
11+
from reflex.config import environment
1012
from reflex.testing import AppHarness, WebDriver
1113

1214
from .utils import SessionStorage
1315

1416

15-
def ConnectionBanner(is_reflex_cloud: bool = False):
16-
"""App with a connection banner.
17-
18-
Args:
19-
is_reflex_cloud: The value for config.is_reflex_cloud.
20-
"""
17+
def ConnectionBanner():
18+
"""App with a connection banner."""
2119
import asyncio
2220

2321
import reflex as rx
2422

25-
# Simulate reflex cloud deploy
26-
rx.config.get_config().is_reflex_cloud = is_reflex_cloud
27-
2823
class State(rx.State):
2924
foo: int = 0
3025

@@ -49,42 +44,45 @@ def index():
4944

5045

5146
@pytest.fixture(
52-
params=[False, True], ids=["reflex_cloud_disabled", "reflex_cloud_enabled"]
47+
params=[constants.CompileContext.RUN, constants.CompileContext.DEPLOY],
48+
ids=["compile_context_run", "compile_context_deploy"],
5349
)
54-
def simulate_is_reflex_cloud(request) -> bool:
50+
def simulate_compile_context(request) -> constants.CompileContext:
5551
"""Fixture to simulate reflex cloud deployment.
5652
5753
Args:
5854
request: pytest request fixture.
5955
6056
Returns:
61-
True if reflex cloud is enabled, False otherwise.
57+
The context to run the app with.
6258
"""
6359
return request.param
6460

6561

6662
@pytest.fixture()
6763
def connection_banner(
6864
tmp_path,
69-
simulate_is_reflex_cloud: bool,
65+
simulate_compile_context: constants.CompileContext,
7066
) -> Generator[AppHarness, None, None]:
7167
"""Start ConnectionBanner app at tmp_path via AppHarness.
7268
7369
Args:
7470
tmp_path: pytest tmp_path fixture
75-
simulate_is_reflex_cloud: Whether is_reflex_cloud is set for the app.
71+
simulate_compile_context: Which context to run the app with.
7672
7773
Yields:
7874
running AppHarness instance
7975
"""
76+
environment.REFLEX_COMPILE_CONTEXT.set(simulate_compile_context)
77+
8078
with AppHarness.create(
8179
root=tmp_path,
82-
app_source=functools.partial(
83-
ConnectionBanner, is_reflex_cloud=simulate_is_reflex_cloud
80+
app_source=functools.partial(ConnectionBanner),
81+
app_name=(
82+
"connection_banner_reflex_cloud"
83+
if simulate_compile_context == constants.CompileContext.DEPLOY
84+
else "connection_banner"
8485
),
85-
app_name="connection_banner_reflex_cloud"
86-
if simulate_is_reflex_cloud
87-
else "connection_banner",
8886
) as harness:
8987
yield harness
9088

@@ -194,13 +192,13 @@ async def test_connection_banner(connection_banner: AppHarness):
194192

195193
@pytest.mark.asyncio
196194
async def test_cloud_banner(
197-
connection_banner: AppHarness, simulate_is_reflex_cloud: bool
195+
connection_banner: AppHarness, simulate_compile_context: constants.CompileContext
198196
):
199197
"""Test that the connection banner is displayed when the websocket drops.
200198
201199
Args:
202200
connection_banner: AppHarness instance.
203-
simulate_is_reflex_cloud: Whether is_reflex_cloud is set for the app.
201+
simulate_compile_context: Which context to set for the app.
204202
"""
205203
assert connection_banner.app_instance is not None
206204
assert connection_banner.backend is not None
@@ -213,7 +211,7 @@ async def test_cloud_banner(
213211

214212
driver.add_cookie({"name": "backend-enabled", "value": "false"})
215213
driver.refresh()
216-
if simulate_is_reflex_cloud:
214+
if simulate_compile_context == constants.CompileContext.DEPLOY:
217215
assert connection_banner._poll_for(lambda: has_cloud_banner(driver))
218216
else:
219217
_assert_token(connection_banner, driver)

0 commit comments

Comments
 (0)