|
@@ -8,11 +8,9 @@ import sys
|
|
|
import urllib.parse
|
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
|
|
-from dotenv import load_dotenv
|
|
|
-
|
|
|
from reflex import constants
|
|
|
-from reflex.admin import AdminDash
|
|
|
from reflex.base import Base
|
|
|
+from reflex.utils import console
|
|
|
|
|
|
|
|
|
class DBConfig(Base):
|
|
@@ -126,130 +124,135 @@ class DBConfig(Base):
|
|
|
class Config(Base):
|
|
|
"""A Reflex config."""
|
|
|
|
|
|
+ class Config:
|
|
|
+ """Pydantic config for the config."""
|
|
|
+
|
|
|
+ validate_assignment = True
|
|
|
+
|
|
|
# The name of the app.
|
|
|
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.
|
|
|
- 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.
|
|
|
- redis_url: Optional[str] = constants.REDIS_URL
|
|
|
+ redis_url: Optional[str] = None
|
|
|
|
|
|
# Telemetry opt-in.
|
|
|
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
|
|
|
- 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.
|
|
|
- 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: 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.
|
|
|
next_compression: bool = True
|
|
|
|
|
|
# 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):
|
|
|
"""Initialize the config values.
|
|
|
|
|
|
- If db_url is not provided gets it from db_config.
|
|
|
-
|
|
|
Args:
|
|
|
*args: The args 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)
|
|
|
|
|
|
- # 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]:
|
|
|
"""Get the websocket event namespace.
|