Add config show_built_with_reflex

This config option is available for authenticated users on various plan tiers
This commit is contained in:
Masen Furer 2025-01-24 00:05:54 -08:00
parent 87c1df35cf
commit bb7619ebea
No known key found for this signature in database
GPG Key ID: B0008AD22B3B3A95
6 changed files with 80 additions and 4 deletions

View File

@ -64,6 +64,7 @@ from reflex.components.core.client_side_routing import (
Default404Page,
wait_for_client_redirect,
)
from reflex.components.core.sticky import sticky
from reflex.components.core.upload import Upload, get_upload_dir
from reflex.components.radix import themes
from reflex.config import environment, get_config
@ -858,6 +859,15 @@ class App(MiddlewareMixin, LifespanMixin):
continue
self._pages[k] = self._add_error_boundary_to_component(component)
def _setup_sticky_badge(self):
"""Add the sticky badge to the app."""
for k, component in self._pages.items():
# Would be nice to share single sticky_badge across all pages, but
# it bungles the StatefulComponent compile step.
sticky_badge = sticky()
sticky_badge._add_style_recursive({})
self._pages[k] = Fragment.create(sticky_badge, component)
def _apply_decorated_pages(self):
"""Add @rx.page decorated pages to the app.
@ -946,10 +956,16 @@ class App(MiddlewareMixin, LifespanMixin):
if not should_compile:
return
# Get the env mode.
config = get_config()
self._validate_var_dependencies()
self._setup_overlay_component()
self._setup_error_boundary()
if config.show_built_with_reflex:
self._setup_sticky_badge()
# Create a progress bar.
progress = Progress(
*Progress.get_default_columns()[:-1],
@ -968,9 +984,6 @@ class App(MiddlewareMixin, LifespanMixin):
+ adhoc_steps_without_executor,
)
# Get the env mode.
config = get_config()
# Store the compile results.
compile_results = []

View File

@ -6,6 +6,7 @@ from reflex.components.core.cond import color_mode_cond
from reflex.components.el.elements.media import Path, Rect, Svg
from reflex.components.radix.themes.layout.box import Box
from reflex.components.radix.themes.typography.text import Text
from reflex.event import redirect
from reflex.style import Style
@ -85,6 +86,7 @@ class StickyBadge(Box):
return super().create(
StickyLogo.create(),
StickyLabel.create(),
on_click=redirect("https://reflex.dev"),
width="auto",
padding="0.375rem",
align="center",

View File

@ -700,6 +700,9 @@ class Config(Base):
# Path to file containing key-values pairs to override in the environment; Dotenv format.
env_file: Optional[str] = None
# Whether to display the sticky "Built with Reflex" badge on all pages.
show_built_with_reflex: bool = True
def __init__(self, *args, **kwargs):
"""Initialize the config values.

View File

@ -182,6 +182,14 @@ def _run(
prerequisites.check_latest_package_version(constants.Reflex.MODULE_NAME)
if frontend:
if not config.show_built_with_reflex:
# The sticky badge may be disabled at runtime for team/enterprise tiers.
prerequisites.check_config_option_in_tier(
option_name="show_built_with_reflex",
allowed_tiers=["team", "enterprise"],
fallback_value=True,
)
# Get the app module.
prerequisites.get_compiled_app()
@ -514,6 +522,14 @@ def deploy(
check_version()
if not config.show_built_with_reflex:
# The sticky badge may be disabled on deploy for pro/team/enterprise tiers.
prerequisites.check_config_option_in_tier(
option_name="show_built_with_reflex",
allowed_tiers=["pro", "team", "enterprise"],
fallback_value=True,
)
# Set the log level.
console.set_log_level(loglevel)

View File

@ -55,6 +55,13 @@ def export(
console.rule("[bold]Compiling production app and preparing for export.")
if frontend:
if not config.show_built_with_reflex:
# The sticky badge may be disabled on export for team/enterprise tiers.
prerequisites.check_config_option_in_tier(
option_name="show_built_with_reflex",
allowed_tiers=["team", "enterprise"],
fallback_value=False,
)
# Ensure module can be imported and app.compile() is called.
prerequisites.get_compiled_app(export=True)
# Set up .web directory and install frontend dependencies.

View File

@ -23,7 +23,7 @@ import zipfile
from datetime import datetime
from pathlib import Path
from types import ModuleType
from typing import Callable, List, NamedTuple, Optional
from typing import Any, Callable, List, NamedTuple, Optional
import httpx
import typer
@ -1967,3 +1967,38 @@ def is_generation_hash(template: str) -> bool:
True if the template is composed of 32 or more hex characters.
"""
return re.match(r"^[0-9a-f]{32,}$", template) is not None
def check_config_option_in_tier(
option_name: str,
allowed_tiers: list[str],
fallback_value: Any,
):
"""Check if a config option is allowed for the authenticated user's current tier.
Args:
option_name: The name of the option to check.
allowed_tiers: The tiers that are allowed to use the option.
fallback_value: The fallback value if the option is not allowed.
"""
from reflex_cli.v2.utils import hosting
config = get_config()
authenticated_token = hosting.authenticated_token()
the_remedy = []
if not authenticated_token:
the_remedy.append(
"You are currently logged out. Run `reflex login` to access this option."
)
current_tier = "anonymous"
else:
current_tier = authenticated_token[1].get("tier", "").lower()
the_remedy.append(
f"Your current subscription tier is `{current_tier}`. Please upgrade to {allowed_tiers} to access this option."
)
if current_tier not in allowed_tiers:
console.warn(
f"Config option `{option_name}` is restricted. {'\n'.join(the_remedy)}"
)
setattr(config, option_name, fallback_value)
config._set_persistent(**{option_name: fallback_value})