From 41e97bbc46c8c9288013ec74cbf286a202fbab41 Mon Sep 17 00:00:00 2001 From: Nev Delap Date: Sat, 2 Sep 2023 23:38:22 +1000 Subject: [PATCH] Issues 1633 Add frontend_path to config to support running multiple reflex apps off the same domain, and 1583 Show the correct info on where the site is being served. (#1724) * Support setting Next.js basePath in Reflex config. (#1633) - Tests. - And sorted config in next.config.js template. * Display the correct running at url with basePath if it is set. (#1583) * Formatting, fixed by black. * Fix indenting in test data. * Fixed that conflict resolution shouldnt have included console.debug line. * Rmove use of :=. Add http:// to url. Use urljoin to build url. --- reflex/.templates/web/next.config.js | 3 +- reflex/config.py | 3 + reflex/utils/exec.py | 12 ++-- reflex/utils/prerequisites.py | 36 +++++++--- tests/test_config.py | 1 + tests/test_prerequites.py | 103 +++++++++++++++++++++++++++ 6 files changed, 144 insertions(+), 14 deletions(-) create mode 100644 tests/test_prerequites.py diff --git a/reflex/.templates/web/next.config.js b/reflex/.templates/web/next.config.js index dd7a7908a..65c4f4fd5 100644 --- a/reflex/.templates/web/next.config.js +++ b/reflex/.templates/web/next.config.js @@ -1,5 +1,6 @@ module.exports = { - reactStrictMode: true, + basePath: "", compress: true, + reactStrictMode: true, trailingSlash: true, }; diff --git a/reflex/config.py b/reflex/config.py index 41d804bf4..517557313 100644 --- a/reflex/config.py +++ b/reflex/config.py @@ -138,6 +138,9 @@ class Config(Base): # The port to run the frontend on. frontend_port: int = 3000 + # The path to run the frontend on. + frontend_path: str = "" + # The port to run the backend on. backend_port: int = 8000 diff --git a/reflex/utils/exec.py b/reflex/utils/exec.py index f5f0f362a..6a1c248e1 100644 --- a/reflex/utils/exec.py +++ b/reflex/utils/exec.py @@ -6,8 +6,10 @@ import hashlib import json import os import platform +import re import sys from pathlib import Path +from urllib.parse import urljoin import psutil import uvicorn @@ -83,13 +85,15 @@ def run_process_and_launch_url(run_command: list[str]): ) if process.stdout: for line in processes.stream_logs("Starting frontend", process): - if "ready started server on" in line: + match = re.search("ready started server on ([0-9.:]+)", line) + if match: if first_run: - url = line.split("url: ")[-1].strip() + url = f"http://{match.group(1)}" + if get_config().frontend_path != "": + url = urljoin(url, get_config().frontend_path) console.print(f"App running at: [bold green]{url}") - first_run = False else: - console.print(f"New packages detected updating app...") + console.print("New packages detected updating app...") else: console.debug(line) new_hash = detect_package_change(json_file_path) diff --git a/reflex/utils/prerequisites.py b/reflex/utils/prerequisites.py index 09f7365c9..bb3e978d5 100644 --- a/reflex/utils/prerequisites.py +++ b/reflex/utils/prerequisites.py @@ -23,7 +23,7 @@ from packaging import version from redis import Redis from reflex import constants, model -from reflex.config import get_config +from reflex.config import Config, get_config from reflex.utils import console, path_ops, processes @@ -216,16 +216,11 @@ def initialize_web_directory(): next_config_file = os.path.join(constants.WEB_DIR, constants.NEXT_CONFIG_FILE) with open(next_config_file, "r") as file: - lines = file.readlines() - for i, line in enumerate(lines): - if "compress:" in line: - new_line = line.replace( - "true", "true" if get_config().next_compression else "false" - ) - lines[i] = new_line + next_config = file.read() + next_config = update_next_config(next_config, get_config()) with open(next_config_file, "w") as file: - file.writelines(lines) + file.write(next_config) # Initialize the reflex json file. init_reflex_json() @@ -245,6 +240,29 @@ def init_reflex_json(): path_ops.update_json_file(constants.REFLEX_JSON, reflex_json) +def update_next_config(next_config: str, config: Config) -> str: + """Update Next.js config from Reflex config. Is its own function for testing. + + Args: + next_config: Content of next.config.js. + config: A reflex Config object. + + Returns: + The next_config updated from config. + """ + next_config = re.sub( + "compress: (true|false)", + f'compress: {"true" if config.next_compression else "false"}', + next_config, + ) + next_config = re.sub( + 'basePath: ".*?"', + f'basePath: "{config.frontend_path or ""}"', + next_config, + ) + return next_config + + def remove_existing_bun_installation(): """Remove existing bun installation.""" console.debug("Removing existing bun installation.") diff --git a/tests/test_config.py b/tests/test_config.py index 57e04aaa5..2bc92e8be 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -47,6 +47,7 @@ def test_deprecated_params(base_config_values, param): [ ("APP_NAME", "my_test_app"), ("FRONTEND_PORT", 3001), + ("FRONTEND_PATH", "/test"), ("BACKEND_PORT", 8001), ("API_URL", "https://mybackend.com:8000"), ("DEPLOY_URL", "https://myfrontend.com"), diff --git a/tests/test_prerequites.py b/tests/test_prerequites.py new file mode 100644 index 000000000..f4d7e43d5 --- /dev/null +++ b/tests/test_prerequites.py @@ -0,0 +1,103 @@ +import pytest + +from reflex.config import Config +from reflex.utils.prerequisites import update_next_config + + +@pytest.mark.parametrize( + "template_next_config, reflex_config, expected_next_config", + [ + ( + """ + module.exports = { + basePath: "", + compress: true, + reactStrictMode: true, + trailingSlash: true, + }; + """, + Config( + app_name="test", + ), + """ + module.exports = { + basePath: "", + compress: true, + reactStrictMode: true, + trailingSlash: true, + }; + """, + ), + ( + """ + module.exports = { + basePath: "", + compress: true, + reactStrictMode: true, + trailingSlash: true, + }; + """, + Config( + app_name="test", + next_compression=False, + ), + """ + module.exports = { + basePath: "", + compress: false, + reactStrictMode: true, + trailingSlash: true, + }; + """, + ), + ( + """ + module.exports = { + basePath: "", + compress: true, + reactStrictMode: true, + trailingSlash: true, + }; + """, + Config( + app_name="test", + frontend_path="/test", + ), + """ + module.exports = { + basePath: "/test", + compress: true, + reactStrictMode: true, + trailingSlash: true, + }; + """, + ), + ( + """ + module.exports = { + basePath: "", + compress: true, + reactStrictMode: true, + trailingSlash: true, + }; + """, + Config( + app_name="test", + frontend_path="/test", + next_compression=False, + ), + """ + module.exports = { + basePath: "/test", + compress: false, + reactStrictMode: true, + trailingSlash: true, + }; + """, + ), + ], +) +def test_update_next_config(template_next_config, reflex_config, expected_next_config): + assert ( + update_next_config(template_next_config, reflex_config) == expected_next_config + )