feat: Enable SSL flags for local development (#7060)
* ssl-support: Enable SSL for local development to simulate a production-like environment. This would allow testing features requiring SSL during development, third party integrations, and allowing easier SSL integration without WAFs and reverse proxies * rebase * rebase fixes --------- Co-authored-by: lpetkov <lpetkov@pros.com> Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org> Co-authored-by: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com> Co-authored-by: Jordan Frazier <jordan.frazier@datastax.com>
This commit is contained in:
parent
3fccfd75ed
commit
58b9551758
4 changed files with 33 additions and 12 deletions
|
|
@ -122,6 +122,8 @@ python -m langflow run [OPTIONS]
|
||||||
| <Link id="run-auto-saving-interval"/>`--auto-saving-interval` | `1000` | Integer | Set the interval for flow auto-saving in milliseconds.<br/>See [`LANGFLOW_AUTO_SAVING_INTERVAL` variable](./environment-variables.md#LANGFLOW_AUTO_SAVING_INTERVAL). |
|
| <Link id="run-auto-saving-interval"/>`--auto-saving-interval` | `1000` | Integer | Set the interval for flow auto-saving in milliseconds.<br/>See [`LANGFLOW_AUTO_SAVING_INTERVAL` variable](./environment-variables.md#LANGFLOW_AUTO_SAVING_INTERVAL). |
|
||||||
| <Link id="run-health-check-max-retries"/>`--health-check-max-retries` | `5` | Integer | Set the maximum number of retries for the health check. Use `--no-health-check-max-retries` to disable the maximum number of retries for the health check.<br/>See [`LANGFLOW_HEALTH_CHECK_MAX_RETRIES` variable](./environment-variables.md#LANGFLOW_HEALTH_CHECK_MAX_RETRIES). |
|
| <Link id="run-health-check-max-retries"/>`--health-check-max-retries` | `5` | Integer | Set the maximum number of retries for the health check. Use `--no-health-check-max-retries` to disable the maximum number of retries for the health check.<br/>See [`LANGFLOW_HEALTH_CHECK_MAX_RETRIES` variable](./environment-variables.md#LANGFLOW_HEALTH_CHECK_MAX_RETRIES). |
|
||||||
| <Link id="run-max-file-size-upload"/>`--max-file-size-upload` | `100` | Integer | Set the maximum file size for the upload in megabytes.<br/>See [`LANGFLOW_MAX_FILE_SIZE_UPLOAD` variable](./environment-variables.md#LANGFLOW_MAX_FILE_SIZE_UPLOAD). |
|
| <Link id="run-max-file-size-upload"/>`--max-file-size-upload` | `100` | Integer | Set the maximum file size for the upload in megabytes.<br/>See [`LANGFLOW_MAX_FILE_SIZE_UPLOAD` variable](./environment-variables.md#LANGFLOW_MAX_FILE_SIZE_UPLOAD). |
|
||||||
|
| <Link id="run-ssl-cert-file-path"/>`--ssl-cert-file-path` | Not set | String | Path to the SSL certificate file on the local system. |
|
||||||
|
| <Link id="run-ssl-key-file-path"/>`--ssl-key-file-path` | Not set | String | Path to the SSL key file on the local system. |
|
||||||
| <Link id="run-help"/>`--help` | *Not applicable* | *Not applicable* | Display information about the command usage and its options and arguments. |
|
| <Link id="run-help"/>`--help` | *Not applicable* | *Not applicable* | Display information about the command usage and its options and arguments. |
|
||||||
|
|
||||||
### langflow superuser
|
### langflow superuser
|
||||||
|
|
|
||||||
|
|
@ -226,9 +226,11 @@ The following table lists the environment variables supported by Langflow.
|
||||||
| <Link id="LANGFLOW_LOAD_FLOWS_PATH"/><span class="env-prefix">LANGFLOW_</span>LOAD_FLOWS_PATH | String | Not set | Path to a directory containing flow JSON files to be loaded on startup. Note that this feature only works if <span class="env-prefix">LANGFLOW_</span>AUTO_LOGIN is enabled. |
|
| <Link id="LANGFLOW_LOAD_FLOWS_PATH"/><span class="env-prefix">LANGFLOW_</span>LOAD_FLOWS_PATH | String | Not set | Path to a directory containing flow JSON files to be loaded on startup. Note that this feature only works if <span class="env-prefix">LANGFLOW_</span>AUTO_LOGIN is enabled. |
|
||||||
| <Link id="LANGFLOW_WORKER_TIMEOUT"/><span class="env-prefix">LANGFLOW_</span>WORKER_TIMEOUT | Integer | `300` | Worker timeout in seconds.<br/>See [`--worker-timeout` option](./configuration-cli.md#run-worker-timeout). |
|
| <Link id="LANGFLOW_WORKER_TIMEOUT"/><span class="env-prefix">LANGFLOW_</span>WORKER_TIMEOUT | Integer | `300` | Worker timeout in seconds.<br/>See [`--worker-timeout` option](./configuration-cli.md#run-worker-timeout). |
|
||||||
| <Link id="LANGFLOW_WORKERS"/><span class="env-prefix">LANGFLOW_</span>WORKERS | Integer | `1` | Number of worker processes.<br/>See [`--workers` option](./configuration-cli.md#run-workers). |
|
| <Link id="LANGFLOW_WORKERS"/><span class="env-prefix">LANGFLOW_</span>WORKERS | Integer | `1` | Number of worker processes.<br/>See [`--workers` option](./configuration-cli.md#run-workers). |
|
||||||
|
| <Link id="LANGFLOW_SSL_CERT_FILE"/><span class="env-prefix">LANGFLOW_</span>SSL_CERT_FILE | String | Not set | Path to the SSL certificate file on the local system. |
|
||||||
|
| <Link id="LANGFLOW_SSL_KEY_FILE"/><span class="env-prefix">LANGFLOW_</span>SSL_KEY_FILE | String | Not set | Path to the SSL key file on the local system. |
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
## Configure .env, override.conf, and tasks.json files
|
## Configure .env, override.conf, and tasks.json files
|
||||||
|
|
||||||
The following examples show how to configure Langflow using environment variables in different scenarios.
|
The following examples show how to configure Langflow using environment variables in different scenarios.
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,10 @@ def run(
|
||||||
help="Defines the polling interval for the webhook.",
|
help="Defines the polling interval for the webhook.",
|
||||||
show_default=False,
|
show_default=False,
|
||||||
),
|
),
|
||||||
|
ssl_cert_file_path: str | None = typer.Option(
|
||||||
|
None, help="Defines the SSL certificate file path.", show_default=False
|
||||||
|
),
|
||||||
|
ssl_key_file_path: str | None = typer.Option(None, help="Defines the SSL key file path.", show_default=False),
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Run Langflow."""
|
"""Run Langflow."""
|
||||||
# Register SIGTERM handler
|
# Register SIGTERM handler
|
||||||
|
|
@ -202,6 +206,8 @@ def run(
|
||||||
log_level = settings_service.settings.log_level
|
log_level = settings_service.settings.log_level
|
||||||
frontend_path = settings_service.settings.frontend_path
|
frontend_path = settings_service.settings.frontend_path
|
||||||
backend_only = settings_service.settings.backend_only
|
backend_only = settings_service.settings.backend_only
|
||||||
|
ssl_cert_file_path = settings_service.settings.ssl_cert_file if ssl_cert_file_path is None else ssl_cert_file_path
|
||||||
|
ssl_key_file_path = settings_service.settings.ssl_key_file if ssl_key_file_path is None else ssl_key_file_path
|
||||||
|
|
||||||
# create path object if frontend_path is provided
|
# create path object if frontend_path is provided
|
||||||
static_files_dir: Path | None = Path(frontend_path) if frontend_path else None
|
static_files_dir: Path | None = Path(frontend_path) if frontend_path else None
|
||||||
|
|
@ -215,7 +221,10 @@ def run(
|
||||||
"bind": f"{host}:{port}",
|
"bind": f"{host}:{port}",
|
||||||
"workers": get_number_of_workers(workers),
|
"workers": get_number_of_workers(workers),
|
||||||
"timeout": worker_timeout,
|
"timeout": worker_timeout,
|
||||||
|
"certfile": ssl_cert_file_path,
|
||||||
|
"keyfile": ssl_key_file_path,
|
||||||
}
|
}
|
||||||
|
protocol = "https" if options["keyfile"] and options["certfile"] else "http"
|
||||||
|
|
||||||
# Define an env variable to know if we are just testing the server
|
# Define an env variable to know if we are just testing the server
|
||||||
if "pytest" in sys.modules:
|
if "pytest" in sys.modules:
|
||||||
|
|
@ -226,10 +235,10 @@ def run(
|
||||||
# Run using uvicorn on MacOS and Windows
|
# Run using uvicorn on MacOS and Windows
|
||||||
# Windows doesn't support gunicorn
|
# Windows doesn't support gunicorn
|
||||||
# MacOS requires an env variable to be set to use gunicorn
|
# MacOS requires an env variable to be set to use gunicorn
|
||||||
run_on_windows(host, port, log_level, options, app)
|
run_on_windows(host, port, log_level, options, app, protocol)
|
||||||
else:
|
else:
|
||||||
# Run using gunicorn on Linux
|
# Run using gunicorn on Linux
|
||||||
process = run_on_mac_or_linux(host, port, log_level, options, app)
|
process = run_on_mac_or_linux(host, port, log_level, options, app, protocol)
|
||||||
if open_browser and not backend_only:
|
if open_browser and not backend_only:
|
||||||
click.launch(f"http://{host}:{port}")
|
click.launch(f"http://{host}:{port}")
|
||||||
if process:
|
if process:
|
||||||
|
|
@ -250,12 +259,14 @@ def run(
|
||||||
raise typer.Exit(1) from e
|
raise typer.Exit(1) from e
|
||||||
|
|
||||||
|
|
||||||
def wait_for_server_ready(host, port) -> None:
|
def wait_for_server_ready(host, port, protocol) -> None:
|
||||||
"""Wait for the server to become ready by polling the health endpoint."""
|
"""Wait for the server to become ready by polling the health endpoint."""
|
||||||
status_code = 0
|
status_code = 0
|
||||||
while status_code != httpx.codes.OK:
|
while status_code != httpx.codes.OK:
|
||||||
try:
|
try:
|
||||||
status_code = httpx.get(f"http://{host}:{port}/health").status_code
|
status_code = httpx.get(
|
||||||
|
f"{protocol}://{host}:{port}/health", verify=host not in ("127.0.0.1", "localhost")
|
||||||
|
).status_code
|
||||||
except HTTPError:
|
except HTTPError:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
except Exception: # noqa: BLE001
|
except Exception: # noqa: BLE001
|
||||||
|
|
@ -263,18 +274,18 @@ def wait_for_server_ready(host, port) -> None:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
def run_on_mac_or_linux(host, port, log_level, options, app):
|
def run_on_mac_or_linux(host, port, log_level, options, app, protocol):
|
||||||
webapp_process = Process(target=run_langflow, args=(host, port, log_level, options, app))
|
webapp_process = Process(target=run_langflow, args=(host, port, log_level, options, app))
|
||||||
webapp_process.start()
|
webapp_process.start()
|
||||||
wait_for_server_ready(host, port)
|
wait_for_server_ready(host, port, protocol)
|
||||||
|
|
||||||
print_banner(host, port)
|
print_banner(host, port, protocol)
|
||||||
return webapp_process
|
return webapp_process
|
||||||
|
|
||||||
|
|
||||||
def run_on_windows(host, port, log_level, options, app) -> None:
|
def run_on_windows(host, port, log_level, options, app, protocol) -> None:
|
||||||
"""Run the Langflow server on Windows."""
|
"""Run the Langflow server on Windows."""
|
||||||
print_banner(host, port)
|
print_banner(host, port, protocol)
|
||||||
run_langflow(host, port, log_level, options, app)
|
run_langflow(host, port, log_level, options, app)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -358,7 +369,7 @@ def stylize_text(text: str, to_style: str, *, is_prerelease: bool) -> str:
|
||||||
return text.replace(to_style, styled_text)
|
return text.replace(to_style, styled_text)
|
||||||
|
|
||||||
|
|
||||||
def print_banner(host: str, port: int) -> None:
|
def print_banner(host: str, port: int, protocol: str) -> None:
|
||||||
notices = []
|
notices = []
|
||||||
package_names = [] # Track package names for pip install instructions
|
package_names = [] # Track package names for pip install instructions
|
||||||
is_pre_release = False # Track if any package is a pre-release
|
is_pre_release = False # Track if any package is a pre-release
|
||||||
|
|
@ -398,7 +409,7 @@ def print_banner(host: str, port: int) -> None:
|
||||||
"We collect anonymous usage data to improve Langflow.\n"
|
"We collect anonymous usage data to improve Langflow.\n"
|
||||||
"You can opt-out by setting [bold]DO_NOT_TRACK=true[/bold] in your environment."
|
"You can opt-out by setting [bold]DO_NOT_TRACK=true[/bold] in your environment."
|
||||||
)
|
)
|
||||||
access_link = f"Access [link=http://{host}:{port}]http://{host}:{port}[/link]"
|
access_link = f"Access [link={protocol}://{host}:{port}]{protocol}://{host}:{port}[/link]"
|
||||||
|
|
||||||
panel_content = "\n\n".join([title, *styled_notices, info_text, telemetry_text, access_link])
|
panel_content = "\n\n".join([title, *styled_notices, info_text, telemetry_text, access_link])
|
||||||
panel = Panel(panel_content, box=box.ROUNDED, border_style="blue", expand=False)
|
panel = Panel(panel_content, box=box.ROUNDED, border_style="blue", expand=False)
|
||||||
|
|
@ -416,6 +427,8 @@ def run_langflow(host, port, log_level, options, app) -> None:
|
||||||
port=port,
|
port=port,
|
||||||
log_level=log_level.lower(),
|
log_level=log_level.lower(),
|
||||||
loop="asyncio",
|
loop="asyncio",
|
||||||
|
ssl_keyfile=options["keyfile"],
|
||||||
|
ssl_certfile=options["certfile"],
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
from langflow.server import LangflowApplication
|
from langflow.server import LangflowApplication
|
||||||
|
|
|
||||||
|
|
@ -224,6 +224,10 @@ class Settings(BaseSettings):
|
||||||
"""The polling interval for the webhook in ms."""
|
"""The polling interval for the webhook in ms."""
|
||||||
fs_flows_polling_interval: int = 10000
|
fs_flows_polling_interval: int = 10000
|
||||||
"""The polling interval in milliseconds for synchronizing flows from the file system."""
|
"""The polling interval in milliseconds for synchronizing flows from the file system."""
|
||||||
|
ssl_cert_file: str | None = None
|
||||||
|
"""Path to the SSL certificate file on the local system."""
|
||||||
|
ssl_key_file: str | None = None
|
||||||
|
"""Path to the SSL key file on the local system."""
|
||||||
|
|
||||||
# MCP Server
|
# MCP Server
|
||||||
mcp_server_enabled: bool = True
|
mcp_server_enabled: bool = True
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue