Browse Source

Clean up config parameters (#1591)

Nikhil Rao 1 năm trước cách đây
mục cha
commit
0beb7a409f

+ 0 - 1
pyproject.toml

@@ -43,7 +43,6 @@ watchdog = "^2.3.1"
 watchfiles = "^0.19.0"
 watchfiles = "^0.19.0"
 websockets = "^10.4"
 websockets = "^10.4"
 starlette-admin = "^0.9.0"
 starlette-admin = "^0.9.0"
-python-dotenv = "^0.13.0"
 importlib-metadata = {version = "^6.7.0", python = ">=3.7, <3.8"}
 importlib-metadata = {version = "^6.7.0", python = ">=3.7, <3.8"}
 alembic = "^1.11.1"
 alembic = "^1.11.1"
 platformdirs = "^3.10.0"
 platformdirs = "^3.10.0"

+ 0 - 1
reflex/__init__.py

@@ -18,7 +18,6 @@ from .components.graphing.victory import data as data
 from .config import Config as Config
 from .config import Config as Config
 from .config import DBConfig as DBConfig
 from .config import DBConfig as DBConfig
 from .constants import Env as Env
 from .constants import Env as Env
-from .constants import Transports as Transports
 from .event import EVENT_ARG as EVENT_ARG
 from .event import EVENT_ARG as EVENT_ARG
 from .event import EventChain as EventChain
 from .event import EventChain as EventChain
 from .event import FileUpload as upload_files
 from .event import FileUpload as upload_files

+ 11 - 10
reflex/app.py

@@ -144,10 +144,10 @@ class App(Base):
         self.sio = AsyncServer(
         self.sio = AsyncServer(
             async_mode="asgi",
             async_mode="asgi",
             cors_allowed_origins="*"
             cors_allowed_origins="*"
-            if config.cors_allowed_origins == constants.CORS_ALLOWED_ORIGINS
+            if config.cors_allowed_origins == ["*"]
             else config.cors_allowed_origins,
             else config.cors_allowed_origins,
-            cors_credentials=config.cors_credentials,
-            max_http_buffer_size=config.polling_max_http_buffer_size,
+            cors_credentials=True,
+            max_http_buffer_size=constants.POLLING_MAX_HTTP_BUFFER_SIZE,
             ping_interval=constants.PING_INTERVAL,
             ping_interval=constants.PING_INTERVAL,
             ping_timeout=constants.PING_TIMEOUT,
             ping_timeout=constants.PING_TIMEOUT,
         )
         )
@@ -438,13 +438,14 @@ class App(Base):
 
 
     def setup_admin_dash(self):
     def setup_admin_dash(self):
         """Setup the admin dash."""
         """Setup the admin dash."""
-        # Get the config.
-        config = get_config()
-        if config.admin_dash and config.admin_dash.models:
+        # Get the admin dash.
+        admin_dash = self.admin_dash
+
+        if admin_dash and admin_dash.models:
             # Build the admin dashboard
             # Build the admin dashboard
             admin = (
             admin = (
-                config.admin_dash.admin
-                if config.admin_dash.admin
+                admin_dash.admin
+                if admin_dash.admin
                 else Admin(
                 else Admin(
                     engine=Model.get_db_engine(),
                     engine=Model.get_db_engine(),
                     title="Reflex Admin Dashboard",
                     title="Reflex Admin Dashboard",
@@ -452,8 +453,8 @@ class App(Base):
                 )
                 )
             )
             )
 
 
-            for model in config.admin_dash.models:
-                view = config.admin_dash.view_overrides.get(model, ModelView)
+            for model in admin_dash.models:
+                view = admin_dash.view_overrides.get(model, ModelView)
                 admin.add_view(view(model))
                 admin.add_view(view(model))
 
 
             admin.mount_to(self.api)
             admin.mount_to(self.api)

+ 0 - 1
reflex/compiler/compiler.py

@@ -113,7 +113,6 @@ def _compile_page(
         state_name=state.get_name(),
         state_name=state.get_name(),
         hooks=component.get_hooks(),
         hooks=component.get_hooks(),
         render=component.render(),
         render=component.render(),
-        transports=constants.Transports.POLLING_WEBSOCKET.get_transports(),
         err_comp=connect_error_component.render() if connect_error_component else None,
         err_comp=connect_error_component.render() if connect_error_component else None,
     )
     )
 
 

+ 0 - 1
reflex/compiler/templates.py

@@ -39,7 +39,6 @@ class ReflexJinjaEnvironment(Environment):
             "toggle_color_mode": constants.TOGGLE_COLOR_MODE,
             "toggle_color_mode": constants.TOGGLE_COLOR_MODE,
             "use_color_mode": constants.USE_COLOR_MODE,
             "use_color_mode": constants.USE_COLOR_MODE,
             "hydrate": constants.HYDRATE,
             "hydrate": constants.HYDRATE,
-            "db_url": constants.DB_URL,
         }
         }
 
 
 
 

+ 91 - 88
reflex/config.py

@@ -8,11 +8,9 @@ import sys
 import urllib.parse
 import urllib.parse
 from typing import Any, Dict, List, Optional
 from typing import Any, Dict, List, Optional
 
 
-from dotenv import load_dotenv
-
 from reflex import constants
 from reflex import constants
-from reflex.admin import AdminDash
 from reflex.base import Base
 from reflex.base import Base
+from reflex.utils import console
 
 
 
 
 class DBConfig(Base):
 class DBConfig(Base):
@@ -126,130 +124,135 @@ class DBConfig(Base):
 class Config(Base):
 class Config(Base):
     """A Reflex config."""
     """A Reflex config."""
 
 
+    class Config:
+        """Pydantic config for the config."""
+
+        validate_assignment = True
+
     # The name of the app.
     # The name of the app.
     app_name: str
     app_name: str
 
 
-    # The username.
-    username: Optional[str] = None
+    # The log level to use.
+    loglevel: constants.LogLevel = constants.LogLevel.INFO
 
 
-    # The frontend port.
-    frontend_port: str = constants.FRONTEND_PORT
+    # The port to run the frontend on.
+    frontend_port: int = 3000
 
 
-    # The backend port.
-    backend_port: str = constants.BACKEND_PORT
+    # The port to run the backend on.
+    backend_port: int = 8000
 
 
-    # The backend host.
-    backend_host: str = constants.BACKEND_HOST
+    # The backend url the frontend will connect to.
+    api_url: str = f"http://localhost:{backend_port}"
 
 
-    # The backend API url.
-    api_url: str = constants.API_URL
+    # The url the frontend will be hosted on.
+    deploy_url: Optional[str] = f"http://localhost:{frontend_port}"
 
 
-    # The deploy url.
-    deploy_url: Optional[str] = constants.DEPLOY_URL
+    # The url the backend will be hosted on.
+    backend_host: str = "0.0.0.0"
 
 
     # The database url.
     # The database url.
-    db_url: Optional[str] = constants.DB_URL
-
-    # The database config.
-    db_config: Optional[DBConfig] = None
+    db_url: Optional[str] = "sqlite:///reflex.db"
 
 
     # The redis url.
     # The redis url.
-    redis_url: Optional[str] = constants.REDIS_URL
+    redis_url: Optional[str] = None
 
 
     # Telemetry opt-in.
     # Telemetry opt-in.
     telemetry_enabled: bool = True
     telemetry_enabled: bool = True
 
 
-    # The rxdeploy url.
-    rxdeploy_url: Optional[str] = None
-
-    # The environment mode.
-    env: constants.Env = constants.Env.DEV
-
-    # Additional frontend packages to install.
-    frontend_packages: List[str] = []
-
     # The bun path
     # The bun path
-    bun_path: str = constants.BUN_PATH
-
-    # The Admin Dash.
-    admin_dash: Optional[AdminDash] = None
-
-    # Backend transport methods.
-    backend_transports: Optional[
-        constants.Transports
-    ] = constants.Transports.WEBSOCKET_POLLING
+    bun_path: str = constants.DEFAULT_BUN_PATH
 
 
     # List of origins that are allowed to connect to the backend API.
     # List of origins that are allowed to connect to the backend API.
-    cors_allowed_origins: Optional[list] = constants.CORS_ALLOWED_ORIGINS
-
-    # Whether credentials (cookies, authentication) are allowed in requests to the backend API.
-    cors_credentials: Optional[bool] = True
-
-    # The maximum size of a message when using the polling backend transport.
-    polling_max_http_buffer_size: Optional[int] = constants.POLLING_MAX_HTTP_BUFFER_SIZE
-
-    # Dotenv file path.
-    env_path: Optional[str] = constants.DOT_ENV_FILE
-
-    # Whether to override OS environment variables.
-    override_os_envs: Optional[bool] = True
+    cors_allowed_origins: List[str] = ["*"]
 
 
     # Tailwind config.
     # Tailwind config.
     tailwind: Optional[Dict[str, Any]] = None
     tailwind: Optional[Dict[str, Any]] = None
 
 
-    # Timeout when launching the gunicorn server.
-    timeout: int = constants.TIMEOUT
+    # Timeout when launching the gunicorn server. TODO(rename this to backend_timeout?)
+    timeout: int = 120
 
 
     # Whether to enable or disable nextJS gzip compression.
     # Whether to enable or disable nextJS gzip compression.
     next_compression: bool = True
     next_compression: bool = True
 
 
     # The event namespace for ws connection
     # The event namespace for ws connection
-    event_namespace: Optional[str] = constants.EVENT_NAMESPACE
+    event_namespace: Optional[str] = None
+
+    # Params to remove eventually.
+    # Additional frontend packages to install. (TODO: these can be inferred from the imports)
+    frontend_packages: List[str] = []
+
+    # For rest are for deploy only.
+    # The rxdeploy url.
+    rxdeploy_url: Optional[str] = None
+
+    # The username.
+    username: Optional[str] = None
 
 
     def __init__(self, *args, **kwargs):
     def __init__(self, *args, **kwargs):
         """Initialize the config values.
         """Initialize the config values.
 
 
-        If db_url is not provided gets it from db_config.
-
         Args:
         Args:
             *args: The args to pass to the Pydantic init method.
             *args: The args to pass to the Pydantic init method.
             **kwargs: The kwargs to pass to the Pydantic init method.
             **kwargs: The kwargs to pass to the Pydantic init method.
         """
         """
-        if "db_url" not in kwargs and "db_config" in kwargs:
-            kwargs["db_url"] = kwargs["db_config"].get_url()
-
         super().__init__(*args, **kwargs)
         super().__init__(*args, **kwargs)
 
 
-        # set overriden class attribute values as os env variables to avoid losing them
-        for key, value in dict(self).items():
-            key = key.upper()
-            if (
-                key.startswith("_")
-                or key in os.environ
-                or (value is None and key != "DB_URL")
-            ):
-                continue
-            os.environ[key] = str(value)
-
-        # Avoid overriding if env_path is not provided or does not exist
-        if self.env_path is not None and os.path.isfile(self.env_path):
-            load_dotenv(self.env_path, override=self.override_os_envs)  # type: ignore
-            # Recompute constants after loading env variables
-            importlib.reload(constants)
-            # Recompute instance attributes
-            self.recompute_field_values()
-
-    def recompute_field_values(self):
-        """Recompute instance field values to reflect new values after reloading
-        constant values.
+        # Check for deprecated values.
+        self.check_deprecated_values(**kwargs)
+
+        # Update the config from environment variables.
+        self.update_from_env()
+
+    @staticmethod
+    def check_deprecated_values(**kwargs):
+        """Check for deprecated config values.
+
+        Args:
+            **kwargs: The kwargs passed to the config.
+
+        Raises:
+            ValueError: If a deprecated config value is found.
+        """
+        if "db_config" in kwargs:
+            raise ValueError("db_config is deprecated - use db_url instead")
+        if "admin_dash" in kwargs:
+            raise ValueError(
+                "admin_dash is deprecated in the config - pass it as a param to rx.App instead"
+            )
+        if "env_path" in kwargs:
+            raise ValueError(
+                "env_path is deprecated - use environment variables instead"
+            )
+
+    def update_from_env(self):
+        """Update the config from environment variables.
+
+
+        Raises:
+            ValueError: If an environment variable is set to an invalid type.
         """
         """
-        for field in self.get_fields():
-            try:
-                if field.startswith("_"):
-                    continue
-                setattr(self, field, getattr(constants, f"{field.upper()}"))
-            except AttributeError:
-                pass
+        # Iterate over the fields.
+        for key, field in self.__fields__.items():
+            # The env var name is the key in uppercase.
+            env_var = os.environ.get(key.upper())
+
+            # If the env var is set, override the config value.
+            if env_var is not None:
+                console.info(
+                    f"Overriding config value {key} with env var {key.upper()}={env_var}"
+                )
+
+                # Convert the env var to the expected type.
+                try:
+                    env_var = field.type_(env_var)
+                except ValueError:
+                    console.error(
+                        f"Could not convert {key.upper()}={env_var} to type {field.type_}"
+                    )
+                    raise
+
+                # Set the value.
+                setattr(self, key, env_var)
 
 
     def get_event_namespace(self) -> Optional[str]:
     def get_event_namespace(self) -> Optional[str]:
         """Get the websocket event namespace.
         """Get the websocket event namespace.

+ 2 - 90
reflex/constants.py

@@ -6,7 +6,6 @@ import platform
 import re
 import re
 from enum import Enum
 from enum import Enum
 from types import SimpleNamespace
 from types import SimpleNamespace
-from typing import Any, Type
 
 
 from platformdirs import PlatformDirs
 from platformdirs import PlatformDirs
 
 
@@ -19,31 +18,6 @@ except ImportError:
 IS_WINDOWS = platform.system() == "Windows"
 IS_WINDOWS = platform.system() == "Windows"
 
 
 
 
-def get_value(key: str, default: Any = None, type_: Type = str) -> Type:
-    """Get the value for the constant.
-    Obtain os env value and cast non-string types into
-    their original types.
-
-    Args:
-        key: constant name.
-        default: default value if key doesn't exist.
-        type_: the type of the constant.
-
-    Returns:
-        the value of the constant in its designated type
-    """
-    value = os.getenv(key, default)
-    try:
-        if value and type_ != str:
-            value = eval(value)
-    except Exception:
-        pass
-    finally:
-        # Special case for db_url expects None to be a valid input when
-        # user explicitly overrides db_url as None
-        return value if value != "None" else None  # noqa B012
-
-
 # App names and versions.
 # App names and versions.
 # The name of the Reflex package.
 # The name of the Reflex package.
 MODULE_NAME = "reflex"
 MODULE_NAME = "reflex"
@@ -85,8 +59,6 @@ MIN_BUN_VERSION = "0.7.0"
 BUN_ROOT_PATH = os.path.join(REFLEX_DIR, ".bun")
 BUN_ROOT_PATH = os.path.join(REFLEX_DIR, ".bun")
 # Default bun path.
 # Default bun path.
 DEFAULT_BUN_PATH = os.path.join(BUN_ROOT_PATH, "bin", "bun")
 DEFAULT_BUN_PATH = os.path.join(BUN_ROOT_PATH, "bin", "bun")
-# The bun path.
-BUN_PATH = get_value("BUN_PATH", DEFAULT_BUN_PATH)
 # URL to bun install script.
 # URL to bun install script.
 BUN_INSTALL_URL = "https://bun.sh/install"
 BUN_INSTALL_URL = "https://bun.sh/install"
 
 
@@ -161,29 +133,6 @@ REFLEX_JSON = os.path.join(WEB_DIR, "reflex.json")
 # The env json file.
 # The env json file.
 ENV_JSON = os.path.join(WEB_DIR, "env.json")
 ENV_JSON = os.path.join(WEB_DIR, "env.json")
 
 
-# Commands to run the app.
-DOT_ENV_FILE = ".env"
-# The frontend default port.
-FRONTEND_PORT = get_value("FRONTEND_PORT", "3000")
-# The backend default port.
-BACKEND_PORT = get_value("BACKEND_PORT", "8000")
-# The backend api url.
-API_URL = get_value("API_URL", "http://localhost:8000")
-# The deploy url
-DEPLOY_URL = get_value("DEPLOY_URL")
-# Default host in dev mode.
-BACKEND_HOST = get_value("BACKEND_HOST", "0.0.0.0")
-# The default timeout when launching the gunicorn server.
-TIMEOUT = get_value("TIMEOUT", 120, type_=int)
-# The command to run the backend in production mode.
-RUN_BACKEND_PROD = f"gunicorn --worker-class uvicorn.workers.UvicornH11Worker --preload --timeout {TIMEOUT} --log-level critical".split()
-RUN_BACKEND_PROD_WINDOWS = f"uvicorn --timeout-keep-alive {TIMEOUT}".split()
-# Socket.IO web server
-PING_INTERVAL = 25
-PING_TIMEOUT = 120
-# flag to make the engine print all the SQL statements it executes
-SQLALCHEMY_ECHO = get_value("SQLALCHEMY_ECHO", False, type_=bool)
-
 # Compiler variables.
 # Compiler variables.
 # The extension for compiled Javascript files.
 # The extension for compiled Javascript files.
 JS_EXT = ".js"
 JS_EXT = ".js"
@@ -223,12 +172,6 @@ SETTER_PREFIX = "set_"
 FRONTEND_ZIP = "frontend.zip"
 FRONTEND_ZIP = "frontend.zip"
 # The name of the backend zip during deployment.
 # The name of the backend zip during deployment.
 BACKEND_ZIP = "backend.zip"
 BACKEND_ZIP = "backend.zip"
-# The name of the sqlite database.
-DB_NAME = os.getenv("DB_NAME", "reflex.db")
-# The sqlite url.
-DB_URL = get_value("DB_URL", f"sqlite:///{DB_NAME}")
-# The redis url
-REDIS_URL = get_value("REDIS_URL")
 # The default title to show for Reflex apps.
 # The default title to show for Reflex apps.
 DEFAULT_TITLE = "Reflex App"
 DEFAULT_TITLE = "Reflex App"
 # The default description to show for Reflex apps.
 # The default description to show for Reflex apps.
@@ -252,8 +195,6 @@ OLD_CONFIG_FILE = f"pcconfig{PY_EXT}"
 PRODUCTION_BACKEND_URL = "https://{username}-{app_name}.api.pynecone.app"
 PRODUCTION_BACKEND_URL = "https://{username}-{app_name}.api.pynecone.app"
 # Token expiration time in seconds.
 # Token expiration time in seconds.
 TOKEN_EXPIRATION = 60 * 60
 TOKEN_EXPIRATION = 60 * 60
-# The event namespace for websocket
-EVENT_NAMESPACE = get_value("EVENT_NAMESPACE")
 
 
 # Testing variables.
 # Testing variables.
 # Testing os env set by pytest when running a test case.
 # Testing os env set by pytest when running a test case.
@@ -351,36 +292,6 @@ class SocketEvent(Enum):
         return str(self.value)
         return str(self.value)
 
 
 
 
-class Transports(Enum):
-    """Socket transports used by the reflex backend API."""
-
-    POLLING_WEBSOCKET = "['polling', 'websocket']"
-    WEBSOCKET_POLLING = "['websocket', 'polling']"
-    WEBSOCKET_ONLY = "['websocket']"
-    POLLING_ONLY = "['polling']"
-
-    def __str__(self) -> str:
-        """Get the string representation of the transports.
-
-        Returns:
-            The transports string.
-        """
-        return str(self.value)
-
-    def get_transports(self) -> str:
-        """Get the transports config for the backend.
-
-        Returns:
-            The transports config for the backend.
-        """
-        # Import here to avoid circular imports.
-        from reflex.config import get_config
-
-        # Get the API URL from the config.
-        config = get_config()
-        return str(config.backend_transports)
-
-
 class RouteArgType(SimpleNamespace):
 class RouteArgType(SimpleNamespace):
     """Type of dynamic route arg extracted from URI route."""
     """Type of dynamic route arg extracted from URI route."""
 
 
@@ -430,8 +341,9 @@ COLOR_MODE = "colorMode"
 TOGGLE_COLOR_MODE = "toggleColorMode"
 TOGGLE_COLOR_MODE = "toggleColorMode"
 
 
 # Server socket configuration variables
 # Server socket configuration variables
-CORS_ALLOWED_ORIGINS = get_value("CORS_ALLOWED_ORIGINS", ["*"], list)
 POLLING_MAX_HTTP_BUFFER_SIZE = 1000 * 1000
 POLLING_MAX_HTTP_BUFFER_SIZE = 1000 * 1000
+PING_INTERVAL = 25
+PING_TIMEOUT = 120
 
 
 # Alembic migrations
 # Alembic migrations
 ALEMBIC_CONFIG = os.environ.get("ALEMBIC_CONFIG", "alembic.ini")
 ALEMBIC_CONFIG = os.environ.get("ALEMBIC_CONFIG", "alembic.ini")

+ 5 - 4
reflex/model.py

@@ -1,5 +1,6 @@
 """Database built into Reflex."""
 """Database built into Reflex."""
 
 
+import os
 from collections import defaultdict
 from collections import defaultdict
 from pathlib import Path
 from pathlib import Path
 from typing import Any, Optional
 from typing import Any, Optional
@@ -40,13 +41,13 @@ def get_engine(url: Optional[str] = None):
         console.warn(
         console.warn(
             "Database is not initialized, run [bold]reflex db init[/bold] first."
             "Database is not initialized, run [bold]reflex db init[/bold] first."
         )
         )
-    echo_db_query = False
-    if conf.env == constants.Env.DEV and constants.SQLALCHEMY_ECHO:
-        echo_db_query = True
+    # Print the SQL queries if the log level is INFO or lower.
+    echo_db_query = os.environ.get("SQLALCHEMY_ECHO") == "True"
     return sqlmodel.create_engine(
     return sqlmodel.create_engine(
         url,
         url,
         echo=echo_db_query,
         echo=echo_db_query,
-        connect_args={"check_same_thread": False} if conf.admin_dash else {},
+        # Needed for the admin dash.
+        connect_args={"check_same_thread": False},
     )
     )
 
 
 
 

+ 17 - 30
reflex/reflex.py

@@ -15,6 +15,9 @@ from reflex.utils import build, console, exec, prerequisites, processes, telemet
 # Create the app.
 # Create the app.
 cli = typer.Typer(add_completion=False)
 cli = typer.Typer(add_completion=False)
 
 
+# Get the config.
+config = get_config()
+
 
 
 def version(value: bool):
 def version(value: bool):
     """Get the Reflex version.
     """Get the Reflex version.
@@ -48,13 +51,13 @@ def main(
 @cli.command()
 @cli.command()
 def init(
 def init(
     name: str = typer.Option(
     name: str = typer.Option(
-        None, metavar="APP_NAME", help="The name of the app to be initialized."
+        None, metavar="APP_NAME", help="The name of the app to initialize."
     ),
     ),
     template: constants.Template = typer.Option(
     template: constants.Template = typer.Option(
         constants.Template.DEFAULT, help="The template to initialize the app with."
         constants.Template.DEFAULT, help="The template to initialize the app with."
     ),
     ),
     loglevel: constants.LogLevel = typer.Option(
     loglevel: constants.LogLevel = typer.Option(
-        console.LOG_LEVEL, help="The log level to use."
+        config.loglevel, help="The log level to use."
     ),
     ),
 ):
 ):
     """Initialize a new Reflex app in the current directory."""
     """Initialize a new Reflex app in the current directory."""
@@ -75,7 +78,6 @@ def init(
     prerequisites.migrate_to_reflex()
     prerequisites.migrate_to_reflex()
 
 
     # Set up the app directory, only if the config doesn't exist.
     # Set up the app directory, only if the config doesn't exist.
-    config = get_config()
     if not os.path.exists(constants.CONFIG_FILE):
     if not os.path.exists(constants.CONFIG_FILE):
         prerequisites.create_config(app_name)
         prerequisites.create_config(app_name)
         prerequisites.initialize_app_directory(app_name, template)
         prerequisites.initialize_app_directory(app_name, template)
@@ -94,17 +96,23 @@ def init(
 @cli.command()
 @cli.command()
 def run(
 def run(
     env: constants.Env = typer.Option(
     env: constants.Env = typer.Option(
-        get_config().env, help="The environment to run the app in."
+        constants.Env.DEV, help="The environment to run the app in."
     ),
     ),
     frontend: bool = typer.Option(
     frontend: bool = typer.Option(
         False, "--frontend-only", help="Execute only frontend."
         False, "--frontend-only", help="Execute only frontend."
     ),
     ),
     backend: bool = typer.Option(False, "--backend-only", help="Execute only backend."),
     backend: bool = typer.Option(False, "--backend-only", help="Execute only backend."),
-    frontend_port: str = typer.Option(None, help="Specify a different frontend port."),
-    backend_port: str = typer.Option(None, help="Specify a different backend port."),
-    backend_host: str = typer.Option(None, help="Specify the backend host."),
+    frontend_port: str = typer.Option(
+        config.frontend_port, help="Specify a different frontend port."
+    ),
+    backend_port: str = typer.Option(
+        config.backend_port, help="Specify a different backend port."
+    ),
+    backend_host: str = typer.Option(
+        config.backend_host, help="Specify the backend host."
+    ),
     loglevel: constants.LogLevel = typer.Option(
     loglevel: constants.LogLevel = typer.Option(
-        console.LOG_LEVEL, help="The log level to use."
+        config.loglevel, help="The log level to use."
     ),
     ),
 ):
 ):
     """Run the app in the current directory."""
     """Run the app in the current directory."""
@@ -114,20 +122,6 @@ def run(
     # Show system info
     # Show system info
     exec.output_system_info()
     exec.output_system_info()
 
 
-    # Set ports as os env variables to take precedence over config and
-    # .env variables(if override_os_envs flag in config is set to False).
-    build.set_os_env(
-        frontend_port=frontend_port,
-        backend_port=backend_port,
-        backend_host=backend_host,
-    )
-
-    # Get the ports from the config.
-    config = get_config()
-    frontend_port = config.frontend_port if frontend_port is None else frontend_port
-    backend_port = config.backend_port if backend_port is None else backend_port
-    backend_host = config.backend_host if backend_host is None else backend_host
-
     # If no --frontend-only and no --backend-only, then turn on frontend and backend both
     # If no --frontend-only and no --backend-only, then turn on frontend and backend both
     if not frontend and not backend:
     if not frontend and not backend:
         frontend = True
         frontend = True
@@ -147,9 +141,6 @@ def run(
     console.rule("[bold]Starting Reflex App")
     console.rule("[bold]Starting Reflex App")
     app = prerequisites.get_app()
     app = prerequisites.get_app()
 
 
-    # Check the admin dashboard settings.
-    prerequisites.check_admin_settings()
-
     # Warn if schema is not up to date.
     # Warn if schema is not up to date.
     prerequisites.check_schema_up_to_date()
     prerequisites.check_schema_up_to_date()
 
 
@@ -199,9 +190,6 @@ def deploy(
     # Show system info
     # Show system info
     exec.output_system_info()
     exec.output_system_info()
 
 
-    # Get the app config.
-    config = get_config()
-
     # Check if the deploy url is set.
     # Check if the deploy url is set.
     if config.rxdeploy_url is None:
     if config.rxdeploy_url is None:
         console.info("This feature is coming soon!")
         console.info("This feature is coming soon!")
@@ -264,7 +252,6 @@ def export(
         build.setup_frontend(Path.cwd())
         build.setup_frontend(Path.cwd())
 
 
     # Export the app.
     # Export the app.
-    config = get_config()
     build.export(
     build.export(
         backend=backend,
         backend=backend,
         frontend=frontend,
         frontend=frontend,
@@ -294,7 +281,7 @@ db_cli = typer.Typer()
 def db_init():
 def db_init():
     """Create database schema and migration configuration."""
     """Create database schema and migration configuration."""
     # Check the database url.
     # Check the database url.
-    if get_config().db_url is None:
+    if config.db_url is None:
         console.error("db_url is not configured, cannot initialize.")
         console.error("db_url is not configured, cannot initialize.")
         return
         return
 
 

+ 8 - 6
reflex/utils/build.py

@@ -41,7 +41,7 @@ def set_reflex_project_hash():
     update_json_file(constants.REFLEX_JSON, {"project_hash": project_hash})
     update_json_file(constants.REFLEX_JSON, {"project_hash": project_hash})
 
 
 
 
-def set_environment_variables():
+def set_env_json():
     """Write the upload url to a REFLEX_JSON."""
     """Write the upload url to a REFLEX_JSON."""
     update_json_file(
     update_json_file(
         constants.ENV_JSON,
         constants.ENV_JSON,
@@ -102,13 +102,15 @@ def export(
     # Remove the static folder.
     # Remove the static folder.
     path_ops.rm(constants.WEB_STATIC_DIR)
     path_ops.rm(constants.WEB_STATIC_DIR)
 
 
-    # Generate the sitemap file.
+    # The export command to run.
     command = "export"
     command = "export"
-    if deploy_url is not None:
-        generate_sitemap_config(deploy_url)
-        command = "export-sitemap"
 
 
     if frontend:
     if frontend:
+        # Generate a sitemap if a deploy URL is provided.
+        if deploy_url is not None:
+            generate_sitemap_config(deploy_url)
+            command = "export-sitemap"
+
         checkpoints = [
         checkpoints = [
             "Linting and checking ",
             "Linting and checking ",
             "Compiled successfully",
             "Compiled successfully",
@@ -188,7 +190,7 @@ def setup_frontend(
     )
     )
 
 
     # Set the environment variables in client (env.json).
     # Set the environment variables in client (env.json).
-    set_environment_variables()
+    set_env_json()
 
 
     # Disable the Next telemetry.
     # Disable the Next telemetry.
     if disable_telemetry:
     if disable_telemetry:

+ 7 - 6
reflex/utils/exec.py

@@ -59,7 +59,7 @@ def run_frontend(
 
 
     # Run the frontend in development mode.
     # Run the frontend in development mode.
     console.rule("[bold green]App Running")
     console.rule("[bold green]App Running")
-    os.environ["PORT"] = get_config().frontend_port if port is None else port
+    os.environ["PORT"] = str(get_config().frontend_port if port is None else port)
     run_process_and_launch_url([prerequisites.get_package_manager(), "run", "dev"])
     run_process_and_launch_url([prerequisites.get_package_manager(), "run", "dev"])
 
 
 
 
@@ -74,7 +74,7 @@ def run_frontend_prod(
         port: The port to run the frontend on.
         port: The port to run the frontend on.
     """
     """
     # Set the port.
     # Set the port.
-    os.environ["PORT"] = get_config().frontend_port if port is None else port
+    os.environ["PORT"] = str(get_config().frontend_port if port is None else port)
 
 
     # Run the frontend in production mode.
     # Run the frontend in production mode.
     console.rule("[bold green]App Running")
     console.rule("[bold green]App Running")
@@ -129,9 +129,12 @@ def run_backend_prod(
         loglevel: The log level.
         loglevel: The log level.
     """
     """
     num_workers = processes.get_num_workers()
     num_workers = processes.get_num_workers()
+    config = get_config()
+    RUN_BACKEND_PROD = f"gunicorn --worker-class uvicorn.workers.UvicornH11Worker --preload --timeout {config.timeout} --log-level critical".split()
+    RUN_BACKEND_PROD_WINDOWS = f"uvicorn --timeout-keep-alive {config.timeout}".split()
     command = (
     command = (
         [
         [
-            *constants.RUN_BACKEND_PROD_WINDOWS,
+            *RUN_BACKEND_PROD_WINDOWS,
             "--host",
             "--host",
             host,
             host,
             "--port",
             "--port",
@@ -140,7 +143,7 @@ def run_backend_prod(
         ]
         ]
         if constants.IS_WINDOWS
         if constants.IS_WINDOWS
         else [
         else [
-            *constants.RUN_BACKEND_PROD,
+            *RUN_BACKEND_PROD,
             "--bind",
             "--bind",
             f"{host}:{port}",
             f"{host}:{port}",
             "--threads",
             "--threads",
@@ -176,7 +179,6 @@ def output_system_info():
         dependencies.extend(
         dependencies.extend(
             [
             [
                 f"[NVM {constants.NVM_VERSION} (Expected: {constants.NVM_VERSION}) (PATH: {constants.NVM_PATH})]",
                 f"[NVM {constants.NVM_VERSION} (Expected: {constants.NVM_VERSION}) (PATH: {constants.NVM_PATH})]",
-                f"[Bun {prerequisites.get_bun_version()} (Expected: {constants.BUN_VERSION}) (PATH: {constants.BUN_PATH})]",
             ],
             ],
         )
         )
     else:
     else:
@@ -202,4 +204,3 @@ def output_system_info():
     console.debug(f"Using package executer at: {prerequisites.get_package_manager()}")
     console.debug(f"Using package executer at: {prerequisites.get_package_manager()}")
     if system != "Windows":
     if system != "Windows":
         console.debug(f"Unzip path: {path_ops.which('unzip')}")
         console.debug(f"Unzip path: {path_ops.which('unzip')}")
-    # exit()

+ 0 - 17
reflex/utils/prerequisites.py

@@ -482,23 +482,6 @@ def initialize_frontend_dependencies():
     initialize_web_directory()
     initialize_web_directory()
 
 
 
 
-def check_admin_settings():
-    """Check if admin settings are set and valid for logging in cli app."""
-    admin_dash = get_config().admin_dash
-    if admin_dash:
-        if not admin_dash.models:
-            console.log(
-                f"[yellow][Admin Dashboard][/yellow] :megaphone: Admin dashboard enabled, but no models defined in [bold magenta]rxconfig.py[/bold magenta]."
-            )
-        else:
-            console.log(
-                f"[yellow][Admin Dashboard][/yellow] Admin enabled, building admin dashboard."
-            )
-            console.log(
-                "Admin dashboard running at: [bold green]http://localhost:8000/admin[/bold green]"
-            )
-
-
 def check_db_initialized() -> bool:
 def check_db_initialized() -> bool:
     """Check if the database migrations are initialized.
     """Check if the database migrations are initialized.
 
 

+ 0 - 15
reflex/utils/processes.py

@@ -9,13 +9,11 @@ import signal
 import subprocess
 import subprocess
 from concurrent import futures
 from concurrent import futures
 from typing import Callable, List, Optional, Tuple, Union
 from typing import Callable, List, Optional, Tuple, Union
-from urllib.parse import urlparse
 
 
 import psutil
 import psutil
 import typer
 import typer
 
 
 from reflex import constants
 from reflex import constants
-from reflex.config import get_config
 from reflex.utils import console, prerequisites
 from reflex.utils import console, prerequisites
 
 
 
 
@@ -37,19 +35,6 @@ def get_num_workers() -> int:
     return 1 if prerequisites.get_redis() is None else (os.cpu_count() or 1) * 2 + 1
     return 1 if prerequisites.get_redis() is None else (os.cpu_count() or 1) * 2 + 1
 
 
 
 
-def get_api_port() -> int:
-    """Get the API port.
-
-    Returns:
-        The API port.
-    """
-    port = urlparse(get_config().api_url).port
-    if port is None:
-        port = urlparse(constants.API_URL).port
-    assert port is not None
-    return port
-
-
 def get_process_on_port(port) -> Optional[psutil.Process]:
 def get_process_on_port(port) -> Optional[psutil.Process]:
     """Get the process on the given port.
     """Get the process on the given port.
 
 

+ 1 - 2
tests/conftest.py

@@ -8,7 +8,6 @@ from typing import Dict, Generator, List, Set, Union
 import pytest
 import pytest
 
 
 import reflex as rx
 import reflex as rx
-from reflex import constants
 from reflex.app import App
 from reflex.app import App
 from reflex.event import EventSpec
 from reflex.event import EventSpec
 
 
@@ -392,7 +391,7 @@ def base_config_values() -> Dict:
     Returns:
     Returns:
         Dictionary of base config values
         Dictionary of base config values
     """
     """
-    return {"app_name": "app", "db_url": constants.DB_URL, "env": rx.Env.DEV}
+    return {"app_name": "app"}
 
 
 
 
 @pytest.fixture
 @pytest.fixture

+ 4 - 4
tests/test_app.py

@@ -83,7 +83,7 @@ def redundant_test_state() -> Type[State]:
     return RedundantTestState
     return RedundantTestState
 
 
 
 
-@pytest.fixture()
+@pytest.fixture(scope="session")
 def test_model() -> Type[Model]:
 def test_model() -> Type[Model]:
     """A default model.
     """A default model.
 
 
@@ -91,13 +91,13 @@ def test_model() -> Type[Model]:
         A default model.
         A default model.
     """
     """
 
 
-    class TestModel(Model):
+    class TestModel(Model, table=True):  # type: ignore
         pass
         pass
 
 
     return TestModel
     return TestModel
 
 
 
 
-@pytest.fixture()
+@pytest.fixture(scope="session")
 def test_model_auth() -> Type[Model]:
 def test_model_auth() -> Type[Model]:
     """A default model.
     """A default model.
 
 
@@ -105,7 +105,7 @@ def test_model_auth() -> Type[Model]:
         A default model.
         A default model.
     """
     """
 
 
-    class TestModelAuth(Model):
+    class TestModelAuth(Model, table=True):  # type: ignore
         """A test model with auth."""
         """A test model with auth."""
 
 
         pass
         pass

+ 46 - 111
tests/test_config.py

@@ -1,138 +1,73 @@
 import os
 import os
-from typing import Dict
 
 
 import pytest
 import pytest
 
 
 import reflex as rx
 import reflex as rx
-from reflex import constants
-from reflex.config import DBConfig, get_config
-from reflex.constants import get_value
+from reflex.config import get_config
 
 
 
 
-@pytest.fixture
-def config_no_db_url_values(base_config_values) -> Dict:
-    """Create config values with no db_url.
+def test_requires_app_name():
+    """Test that a config requires an app_name."""
+    with pytest.raises(ValueError):
+        rx.Config()
 
 
-    Args:
-        base_config_values: Base config fixture.
-
-    Returns:
-        Config values.
-    """
-    base_config_values.pop("db_url")
-    return base_config_values
-
-
-@pytest.fixture(autouse=True)
-def config_empty_db_url_values(base_config_values):
-    """Create config values with empty db_url.
-
-    Args:
-        base_config_values: Base config values fixture.
-
-    Yields:
-        Config values
-    """
-    base_config_values["db_url"] = None
-    yield base_config_values
-    os.environ.pop("DB_URL", None)
-
-
-@pytest.fixture
-def config_none_db_url_values(base_config_values):
-    """Create config values with None (string) db_url.
 
 
-    Args:
-        base_config_values: Base config values fixture.
-
-    Yields:
-        Config values
-    """
-    base_config_values["db_url"] = "None"
-    yield base_config_values
-    os.environ.pop("DB_URL")
-
-
-def test_config_db_url(base_config_values):
-    """Test defined db_url is not changed.
+def test_set_app_name(base_config_values):
+    """Test that the app name is set to the value passed in.
 
 
     Args:
     Args:
-        base_config_values: base_config_values fixture.
+        base_config_values: Config values.
     """
     """
-    os.environ.pop("DB_URL")
     config = rx.Config(**base_config_values)
     config = rx.Config(**base_config_values)
-    assert config.db_url == base_config_values["db_url"]
-
-
-def test_default_db_url(config_no_db_url_values):
-    """Test that db_url is assigned the default value if not passed.
-
-    Args:
-        config_no_db_url_values: Config values with no db_url defined.
-    """
-    config = rx.Config(**config_no_db_url_values)
-    assert config.db_url == constants.DB_URL
-
+    assert config.app_name == base_config_values["app_name"]
 
 
-def test_empty_db_url(config_empty_db_url_values):
-    """Test that db_url is not automatically assigned if an empty value is defined.
 
 
-    Args:
-        config_empty_db_url_values: Config values with empty db_url.
-    """
-    config = rx.Config(**config_empty_db_url_values)
-    assert config.db_url is None
-
-
-def test_none_db_url(config_none_db_url_values):
-    """Test that db_url is set 'None' (string) assigned if an 'None' (string) value is defined.
+@pytest.mark.parametrize(
+    "param",
+    [
+        "db_config",
+        "admin_dash",
+        "env_path",
+    ],
+)
+def test_deprecated_params(base_config_values, param):
+    """Test that deprecated params are removed from the config.
 
 
     Args:
     Args:
-        config_none_db_url_values: Config values with None (string) db_url.
+        base_config_values: Config values.
+        param: The deprecated param.
     """
     """
-    config = rx.Config(**config_none_db_url_values)
-    assert config.db_url == "None"
+    with pytest.raises(ValueError):
+        rx.Config(**base_config_values, **{param: "test"})
 
 
 
 
-def test_db_url_precedence(base_config_values, sqlite_db_config_values):
-    """Test that db_url is not overwritten when db_url is defined.
+@pytest.mark.parametrize(
+    "env_var, value",
+    [
+        ("APP_NAME", "my_test_app"),
+        ("FRONTEND_PORT", 3001),
+        ("BACKEND_PORT", 8001),
+        ("API_URL", "https://mybackend.com:8000"),
+        ("DEPLOY_URL", "https://myfrontend.com"),
+        ("BACKEND_HOST", "127.0.0.1"),
+        ("DB_URL", "postgresql://user:pass@localhost:5432/db"),
+        ("REDIS_URL", "redis://localhost:6379"),
+        ("TIMEOUT", 600),
+    ],
+)
+def test_update_from_env(base_config_values, monkeypatch, env_var, value):
+    """Test that environment variables override config values.
 
 
     Args:
     Args:
-        base_config_values: config values that include db_ur.
-        sqlite_db_config_values: DB config values.
+        base_config_values: Config values.
+        monkeypatch: The pytest monkeypatch object.
+        env_var: The environment variable name.
+        value: The environment variable value.
     """
     """
-    db_config = DBConfig(**sqlite_db_config_values)
-    base_config_values["db_config"] = db_config
+    monkeypatch.setenv(env_var, value)
+    assert os.environ.get(env_var) == str(value)
     config = rx.Config(**base_config_values)
     config = rx.Config(**base_config_values)
-    assert config.db_url == base_config_values["db_url"]
-
-
-def test_db_url_from_db_config(config_no_db_url_values, sqlite_db_config_values):
-    """Test db_url generation from db_config.
-
-    Args:
-        config_no_db_url_values: Config values with no db_url.
-        sqlite_db_config_values: DB config values.
-    """
-    db_config = DBConfig(**sqlite_db_config_values)
-    config_no_db_url_values["db_config"] = db_config
-    config = rx.Config(**config_no_db_url_values)
-    assert config.db_url == db_config.get_url()
-
-
-@pytest.mark.parametrize(
-    "key, value, expected_value_type_in_config",
-    (
-        ("TIMEOUT", "1", int),
-        ("CORS_ALLOWED_ORIGINS", "[1, 2, 3]", list),
-        ("DB_NAME", "dbname", str),
-    ),
-)
-def test_get_value(monkeypatch, key, value, expected_value_type_in_config):
-    monkeypatch.setenv(key, value)
-    casted_value = get_value(key, type_=expected_value_type_in_config)
-
-    assert isinstance(casted_value, expected_value_type_in_config)
+    assert getattr(config, env_var.lower()) == value
 
 
 
 
 @pytest.mark.parametrize(
 @pytest.mark.parametrize(

+ 2 - 4
tests/test_utils.py

@@ -7,7 +7,7 @@ import pytest
 import typer
 import typer
 from packaging import version
 from packaging import version
 
 
-from reflex import Env, constants
+from reflex import constants
 from reflex.base import Base
 from reflex.base import Base
 from reflex.utils import (
 from reflex.utils import (
     build,
     build,
@@ -323,7 +323,7 @@ def test_setup_frontend(tmp_path, mocker):
     (assets / "favicon.ico").touch()
     (assets / "favicon.ico").touch()
 
 
     mocker.patch("reflex.utils.prerequisites.install_frontend_packages")
     mocker.patch("reflex.utils.prerequisites.install_frontend_packages")
-    mocker.patch("reflex.utils.build.set_environment_variables")
+    mocker.patch("reflex.utils.build.set_env_json")
 
 
     build.setup_frontend(tmp_path, disable_telemetry=False)
     build.setup_frontend(tmp_path, disable_telemetry=False)
     assert web_public_folder.exists()
     assert web_public_folder.exists()
@@ -421,8 +421,6 @@ def test_create_config_e2e(tmp_working_dir):
     exec((tmp_working_dir / constants.CONFIG_FILE).read_text(), eval_globals)
     exec((tmp_working_dir / constants.CONFIG_FILE).read_text(), eval_globals)
     config = eval_globals["config"]
     config = eval_globals["config"]
     assert config.app_name == app_name
     assert config.app_name == app_name
-    assert config.db_url == constants.DB_URL
-    assert config.env == Env.DEV
 
 
 
 
 @pytest.mark.parametrize(
 @pytest.mark.parametrize(