From 376a7de23e260b1999dc3a4f97f17397c9d7b917 Mon Sep 17 00:00:00 2001 From: Muhamad Aji Wibisono Date: Tue, 10 Jun 2025 21:27:28 +0700 Subject: [PATCH 1/9] feat: added proxy as an argument --- surfsense_backend/main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/surfsense_backend/main.py b/surfsense_backend/main.py index 81ef520..8d3f356 100644 --- a/surfsense_backend/main.py +++ b/surfsense_backend/main.py @@ -11,6 +11,7 @@ logging.basicConfig( if __name__ == "__main__": parser = argparse.ArgumentParser(description='Run the SurfSense application') parser.add_argument('--reload', action='store_true', help='Enable hot reloading') + parser.add_argument('--proxy', nargs='?', const='0.0.0.0/0', default=None, help='Enable proxy headers with allowed IPs (e.g., --proxy 0.0.0.0/0)') args = parser.parse_args() uvicorn.run( @@ -18,5 +19,7 @@ if __name__ == "__main__": host="0.0.0.0", log_level="info", reload=args.reload, - reload_dirs=["app"] + reload_dirs=["app"], + proxy_headers=bool(args.proxy), + forwarded_allow_ips=args.proxy if args.proxy else "", ) From 587f9f4c09ae051d1051c32dde21538527d84e36 Mon Sep 17 00:00:00 2001 From: Muhamad Aji Wibisono Date: Tue, 10 Jun 2025 22:06:45 +0700 Subject: [PATCH 2/9] feat: added richer uvicorn configuration in .env --- surfsense_backend/.env.example | 41 +++++++++++++++ surfsense_backend/main.py | 95 +++++++++++++++++++++++++++++++--- 2 files changed, 128 insertions(+), 8 deletions(-) diff --git a/surfsense_backend/.env.example b/surfsense_backend/.env.example index 9a66a43..b094dae 100644 --- a/surfsense_backend/.env.example +++ b/surfsense_backend/.env.example @@ -45,3 +45,44 @@ LANGSMITH_API_KEY=lsv2_pt_..... LANGSMITH_PROJECT=surfsense +# OPTIONAL: LiteLLM API Base +FAST_LLM_API_BASE="" +STRATEGIC_LLM_API_BASE="" +LONG_CONTEXT_LLM_API_BASE="" +TTS_SERVICE_API_BASE="" +STT_SERVICE_API_BASE="" + +# --- Uvicorn Server Configuration --- +UVICORN_HOST="0.0.0.0" # Host to bind (default: 0.0.0.0) +UVICORN_PORT=8000 # Port to bind (default: 8000) +UVICORN_LOG_LEVEL="info" # Log level (critical, error, warning, info, debug, trace) + +# --- Advanced Uvicorn Options (uncomment to use) --- +# UVICORN_PROXY_HEADERS=false # Enable proxy headers (true/false) +# UVICORN_FORWARDED_ALLOW_IPS="127.0.0.1" # Comma-separated list of IPs to trust with proxy headers +# UVICORN_WORKERS=1 # Number of worker processes +# UVICORN_ACCESS_LOG=true # Access log (true/false) +# UVICORN_LOOP="auto" # Event loop implementation (auto, asyncio, uvloop) +# UVICORN_HTTP="auto" # HTTP protocol implementation (auto, h11, httptools) +# UVICORN_WS="auto" # WebSocket protocol implementation (auto, none, websockets, wsproto) +# UVICORN_LIFESPAN="auto" # Lifespan implementation (auto, on, off) +# UVICORN_ENV_FILE=".env" # Environment configuration file +# UVICORN_LOG_CONFIG="" # Logging configuration file (YAML/JSON/dict) +# UVICORN_SERVER_HEADER=true # Enable server header (true/false) +# UVICORN_DATE_HEADER=true # Enable date header (true/false) +# UVICORN_LIMIT_CONCURRENCY= # Maximum number of concurrent connections or tasks +# UVICORN_LIMIT_MAX_REQUESTS= # Maximum number of requests a worker will process before restarting +# UVICORN_TIMEOUT_KEEP_ALIVE=5 # Timeout for keep-alive connections (seconds) +# UVICORN_TIMEOUT_NOTIFY=30 # Timeout for graceful shutdown (seconds) +# UVICORN_SSL_KEYFILE="" # SSL key file (path) +# UVICORN_SSL_CERTFILE="" # SSL certificate file (path) +# UVICORN_SSL_KEYFILE_PASSWORD="" # SSL key file password +# UVICORN_SSL_VERSION="" # SSL version to use +# UVICORN_SSL_CERT_REQS="" # Whether client certificate is required (none, optional, required) +# UVICORN_SSL_CA_CERTS="" # CA certificates file +# UVICORN_SSL_CIPHERS="" # Ciphers to use +# UVICORN_HEADERS="" # List of custom default HTTP response headers (comma-separated, e.g. X-Frame-Options:DENY,X-Content-Type-Options:nosniff) +# UVICORN_USE_COLORS=true # Enable colorized logging output (true/false) +# UVICORN_UDS="" # Bind to a UNIX domain socket (overrides host/port) +# UVICORN_FD="" # Bind to socket from this file descriptor +# UVICORN_ROOT_PATH="" # Root path for ASGI applications submounted below a given URL path diff --git a/surfsense_backend/main.py b/surfsense_backend/main.py index 8d3f356..7c27a8b 100644 --- a/surfsense_backend/main.py +++ b/surfsense_backend/main.py @@ -1,6 +1,8 @@ import uvicorn import argparse import logging +import os +from dotenv import load_dotenv logging.basicConfig( level=logging.INFO, @@ -8,18 +10,95 @@ logging.basicConfig( datefmt='%Y-%m-%d %H:%M:%S' ) +load_dotenv() + if __name__ == "__main__": parser = argparse.ArgumentParser(description='Run the SurfSense application') parser.add_argument('--reload', action='store_true', help='Enable hot reloading') - parser.add_argument('--proxy', nargs='?', const='0.0.0.0/0', default=None, help='Enable proxy headers with allowed IPs (e.g., --proxy 0.0.0.0/0)') args = parser.parse_args() - uvicorn.run( - "app.app:app", - host="0.0.0.0", - log_level="info", + config_kwargs = dict( + app="app.app:app", + host=os.getenv("UVICORN_HOST", "0.0.0.0"), + port=int(os.getenv("UVICORN_PORT", 8000)), + log_level=os.getenv("UVICORN_LOG_LEVEL", "info"), reload=args.reload, - reload_dirs=["app"], - proxy_headers=bool(args.proxy), - forwarded_allow_ips=args.proxy if args.proxy else "", + reload_dirs=["app"] if args.reload else None, ) + + # Only add advanced args if set in env + if os.getenv("UVICORN_PROXY_HEADERS"): + config_kwargs["proxy_headers"] = ( + os.getenv("UVICORN_PROXY_HEADERS").lower() == "true" + ) + if os.getenv("UVICORN_FORWARDED_ALLOW_IPS"): + config_kwargs["forwarded_allow_ips"] = os.getenv("UVICORN_FORWARDED_ALLOW_IPS") + if os.getenv("UVICORN_WORKERS"): + config_kwargs["workers"] = int(os.getenv("UVICORN_WORKERS")) + if os.getenv("UVICORN_ACCESS_LOG"): + config_kwargs["access_log"] = os.getenv("UVICORN_ACCESS_LOG").lower() == "true" + if os.getenv("UVICORN_LOOP"): + config_kwargs["loop"] = os.getenv("UVICORN_LOOP") + if os.getenv("UVICORN_HTTP"): + config_kwargs["http"] = os.getenv("UVICORN_HTTP") + if os.getenv("UVICORN_WS"): + config_kwargs["ws"] = os.getenv("UVICORN_WS") + if os.getenv("UVICORN_LIFESPAN"): + config_kwargs["lifespan"] = os.getenv("UVICORN_LIFESPAN") + if os.getenv("UVICORN_ENV_FILE"): + config_kwargs["env_file"] = os.getenv("UVICORN_ENV_FILE") + if os.getenv("UVICORN_LOG_CONFIG"): + config_kwargs["log_config"] = os.getenv("UVICORN_LOG_CONFIG") + if os.getenv("UVICORN_SERVER_HEADER"): + config_kwargs["server_header"] = ( + os.getenv("UVICORN_SERVER_HEADER").lower() == "true" + ) + if os.getenv("UVICORN_DATE_HEADER"): + config_kwargs["date_header"] = ( + os.getenv("UVICORN_DATE_HEADER").lower() == "true" + ) + if os.getenv("UVICORN_LIMIT_CONCURRENCY"): + config_kwargs["limit_concurrency"] = int(os.getenv("UVICORN_LIMIT_CONCURRENCY")) + if os.getenv("UVICORN_LIMIT_MAX_REQUESTS"): + config_kwargs["limit_max_requests"] = int( + os.getenv("UVICORN_LIMIT_MAX_REQUESTS") + ) + if os.getenv("UVICORN_TIMEOUT_KEEP_ALIVE"): + config_kwargs["timeout_keep_alive"] = int( + os.getenv("UVICORN_TIMEOUT_KEEP_ALIVE") + ) + if os.getenv("UVICORN_TIMEOUT_NOTIFY"): + config_kwargs["timeout_notify"] = int(os.getenv("UVICORN_TIMEOUT_NOTIFY")) + if os.getenv("UVICORN_SSL_KEYFILE"): + config_kwargs["ssl_keyfile"] = os.getenv("UVICORN_SSL_KEYFILE") + if os.getenv("UVICORN_SSL_CERTFILE"): + config_kwargs["ssl_certfile"] = os.getenv("UVICORN_SSL_CERTFILE") + if os.getenv("UVICORN_SSL_KEYFILE_PASSWORD"): + config_kwargs["ssl_keyfile_password"] = os.getenv( + "UVICORN_SSL_KEYFILE_PASSWORD" + ) + if os.getenv("UVICORN_SSL_VERSION"): + config_kwargs["ssl_version"] = int(os.getenv("UVICORN_SSL_VERSION")) + if os.getenv("UVICORN_SSL_CERT_REQS"): + config_kwargs["ssl_cert_reqs"] = int(os.getenv("UVICORN_SSL_CERT_REQS")) + if os.getenv("UVICORN_SSL_CA_CERTS"): + config_kwargs["ssl_ca_certs"] = os.getenv("UVICORN_SSL_CA_CERTS") + if os.getenv("UVICORN_SSL_CIPHERS"): + config_kwargs["ssl_ciphers"] = os.getenv("UVICORN_SSL_CIPHERS") + if os.getenv("UVICORN_HEADERS"): + config_kwargs["headers"] = [ + tuple(h.split(":", 1)) + for h in os.getenv("UVICORN_HEADERS").split(",") + if ":" in h + ] + if os.getenv("UVICORN_USE_COLORS"): + config_kwargs["use_colors"] = os.getenv("UVICORN_USE_COLORS").lower() == "true" + if os.getenv("UVICORN_UDS"): + config_kwargs["uds"] = os.getenv("UVICORN_UDS") + if os.getenv("UVICORN_FD"): + config_kwargs["fd"] = int(os.getenv("UVICORN_FD")) + if os.getenv("UVICORN_ROOT_PATH"): + config_kwargs["root_path"] = os.getenv("UVICORN_ROOT_PATH") + + config = uvicorn.Config(**config_kwargs) + uvicorn.run(config) From 505d539fdfbb07ec53c5a28d314ea61d5e35c5b8 Mon Sep 17 00:00:00 2001 From: Muhamad Aji Wibisono Date: Tue, 10 Jun 2025 22:12:45 +0700 Subject: [PATCH 3/9] refactor: moved uvicorn env loading to ./app/config/uvicorn.py --- surfsense_backend/.env.example | 1 + surfsense_backend/app/config/uvicorn.py | 92 ++++++++++++++++++++++++ surfsense_backend/main.py | 94 ++----------------------- 3 files changed, 99 insertions(+), 88 deletions(-) create mode 100644 surfsense_backend/app/config/uvicorn.py diff --git a/surfsense_backend/.env.example b/surfsense_backend/.env.example index b094dae..336bf28 100644 --- a/surfsense_backend/.env.example +++ b/surfsense_backend/.env.example @@ -53,6 +53,7 @@ TTS_SERVICE_API_BASE="" STT_SERVICE_API_BASE="" # --- Uvicorn Server Configuration --- +# Full documentation for Uvicorn options can be found at: https://www.uvicorn.org/#command-line-options UVICORN_HOST="0.0.0.0" # Host to bind (default: 0.0.0.0) UVICORN_PORT=8000 # Port to bind (default: 8000) UVICORN_LOG_LEVEL="info" # Log level (critical, error, warning, info, debug, trace) diff --git a/surfsense_backend/app/config/uvicorn.py b/surfsense_backend/app/config/uvicorn.py new file mode 100644 index 0000000..deea190 --- /dev/null +++ b/surfsense_backend/app/config/uvicorn.py @@ -0,0 +1,92 @@ +import os + + +def load_uvicorn_config(args=None): + """ + Load Uvicorn configuration from environment variables and CLI args. + Returns a dict suitable for passing to uvicorn.Config. + """ + config_kwargs = dict( + app="app.app:app", + host=os.getenv("UVICORN_HOST", "0.0.0.0"), + port=int(os.getenv("UVICORN_PORT", 8000)), + log_level=os.getenv("UVICORN_LOG_LEVEL", "info"), + reload=args.reload if args else False, + reload_dirs=["app"] if (args and args.reload) else None, + ) + + # Only add advanced args if set in env + if os.getenv("UVICORN_PROXY_HEADERS"): + config_kwargs["proxy_headers"] = ( + os.getenv("UVICORN_PROXY_HEADERS").lower() == "true" + ) + if os.getenv("UVICORN_FORWARDED_ALLOW_IPS"): + config_kwargs["forwarded_allow_ips"] = os.getenv("UVICORN_FORWARDED_ALLOW_IPS") + if os.getenv("UVICORN_WORKERS"): + config_kwargs["workers"] = int(os.getenv("UVICORN_WORKERS")) + if os.getenv("UVICORN_ACCESS_LOG"): + config_kwargs["access_log"] = os.getenv("UVICORN_ACCESS_LOG").lower() == "true" + if os.getenv("UVICORN_LOOP"): + config_kwargs["loop"] = os.getenv("UVICORN_LOOP") + if os.getenv("UVICORN_HTTP"): + config_kwargs["http"] = os.getenv("UVICORN_HTTP") + if os.getenv("UVICORN_WS"): + config_kwargs["ws"] = os.getenv("UVICORN_WS") + if os.getenv("UVICORN_LIFESPAN"): + config_kwargs["lifespan"] = os.getenv("UVICORN_LIFESPAN") + if os.getenv("UVICORN_ENV_FILE"): + config_kwargs["env_file"] = os.getenv("UVICORN_ENV_FILE") + if os.getenv("UVICORN_LOG_CONFIG"): + config_kwargs["log_config"] = os.getenv("UVICORN_LOG_CONFIG") + if os.getenv("UVICORN_SERVER_HEADER"): + config_kwargs["server_header"] = ( + os.getenv("UVICORN_SERVER_HEADER").lower() == "true" + ) + if os.getenv("UVICORN_DATE_HEADER"): + config_kwargs["date_header"] = ( + os.getenv("UVICORN_DATE_HEADER").lower() == "true" + ) + if os.getenv("UVICORN_LIMIT_CONCURRENCY"): + config_kwargs["limit_concurrency"] = int(os.getenv("UVICORN_LIMIT_CONCURRENCY")) + if os.getenv("UVICORN_LIMIT_MAX_REQUESTS"): + config_kwargs["limit_max_requests"] = int( + os.getenv("UVICORN_LIMIT_MAX_REQUESTS") + ) + if os.getenv("UVICORN_TIMEOUT_KEEP_ALIVE"): + config_kwargs["timeout_keep_alive"] = int( + os.getenv("UVICORN_TIMEOUT_KEEP_ALIVE") + ) + if os.getenv("UVICORN_TIMEOUT_NOTIFY"): + config_kwargs["timeout_notify"] = int(os.getenv("UVICORN_TIMEOUT_NOTIFY")) + if os.getenv("UVICORN_SSL_KEYFILE"): + config_kwargs["ssl_keyfile"] = os.getenv("UVICORN_SSL_KEYFILE") + if os.getenv("UVICORN_SSL_CERTFILE"): + config_kwargs["ssl_certfile"] = os.getenv("UVICORN_SSL_CERTFILE") + if os.getenv("UVICORN_SSL_KEYFILE_PASSWORD"): + config_kwargs["ssl_keyfile_password"] = os.getenv( + "UVICORN_SSL_KEYFILE_PASSWORD" + ) + if os.getenv("UVICORN_SSL_VERSION"): + config_kwargs["ssl_version"] = int(os.getenv("UVICORN_SSL_VERSION")) + if os.getenv("UVICORN_SSL_CERT_REQS"): + config_kwargs["ssl_cert_reqs"] = int(os.getenv("UVICORN_SSL_CERT_REQS")) + if os.getenv("UVICORN_SSL_CA_CERTS"): + config_kwargs["ssl_ca_certs"] = os.getenv("UVICORN_SSL_CA_CERTS") + if os.getenv("UVICORN_SSL_CIPHERS"): + config_kwargs["ssl_ciphers"] = os.getenv("UVICORN_SSL_CIPHERS") + if os.getenv("UVICORN_HEADERS"): + config_kwargs["headers"] = [ + tuple(h.split(":", 1)) + for h in os.getenv("UVICORN_HEADERS").split(",") + if ":" in h + ] + if os.getenv("UVICORN_USE_COLORS"): + config_kwargs["use_colors"] = os.getenv("UVICORN_USE_COLORS").lower() == "true" + if os.getenv("UVICORN_UDS"): + config_kwargs["uds"] = os.getenv("UVICORN_UDS") + if os.getenv("UVICORN_FD"): + config_kwargs["fd"] = int(os.getenv("UVICORN_FD")) + if os.getenv("UVICORN_ROOT_PATH"): + config_kwargs["root_path"] = os.getenv("UVICORN_ROOT_PATH") + + return config_kwargs diff --git a/surfsense_backend/main.py b/surfsense_backend/main.py index 7c27a8b..79cc1bc 100644 --- a/surfsense_backend/main.py +++ b/surfsense_backend/main.py @@ -1,104 +1,22 @@ import uvicorn import argparse import logging -import os from dotenv import load_dotenv +from app.config.uvicorn import load_uvicorn_config logging.basicConfig( level=logging.INFO, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', - datefmt='%Y-%m-%d %H:%M:%S' + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", ) load_dotenv() if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Run the SurfSense application') - parser.add_argument('--reload', action='store_true', help='Enable hot reloading') + parser = argparse.ArgumentParser(description="Run the SurfSense application") + parser.add_argument("--reload", action="store_true", help="Enable hot reloading") args = parser.parse_args() - config_kwargs = dict( - app="app.app:app", - host=os.getenv("UVICORN_HOST", "0.0.0.0"), - port=int(os.getenv("UVICORN_PORT", 8000)), - log_level=os.getenv("UVICORN_LOG_LEVEL", "info"), - reload=args.reload, - reload_dirs=["app"] if args.reload else None, - ) + config = uvicorn.Config(**load_uvicorn_config(args)) - # Only add advanced args if set in env - if os.getenv("UVICORN_PROXY_HEADERS"): - config_kwargs["proxy_headers"] = ( - os.getenv("UVICORN_PROXY_HEADERS").lower() == "true" - ) - if os.getenv("UVICORN_FORWARDED_ALLOW_IPS"): - config_kwargs["forwarded_allow_ips"] = os.getenv("UVICORN_FORWARDED_ALLOW_IPS") - if os.getenv("UVICORN_WORKERS"): - config_kwargs["workers"] = int(os.getenv("UVICORN_WORKERS")) - if os.getenv("UVICORN_ACCESS_LOG"): - config_kwargs["access_log"] = os.getenv("UVICORN_ACCESS_LOG").lower() == "true" - if os.getenv("UVICORN_LOOP"): - config_kwargs["loop"] = os.getenv("UVICORN_LOOP") - if os.getenv("UVICORN_HTTP"): - config_kwargs["http"] = os.getenv("UVICORN_HTTP") - if os.getenv("UVICORN_WS"): - config_kwargs["ws"] = os.getenv("UVICORN_WS") - if os.getenv("UVICORN_LIFESPAN"): - config_kwargs["lifespan"] = os.getenv("UVICORN_LIFESPAN") - if os.getenv("UVICORN_ENV_FILE"): - config_kwargs["env_file"] = os.getenv("UVICORN_ENV_FILE") - if os.getenv("UVICORN_LOG_CONFIG"): - config_kwargs["log_config"] = os.getenv("UVICORN_LOG_CONFIG") - if os.getenv("UVICORN_SERVER_HEADER"): - config_kwargs["server_header"] = ( - os.getenv("UVICORN_SERVER_HEADER").lower() == "true" - ) - if os.getenv("UVICORN_DATE_HEADER"): - config_kwargs["date_header"] = ( - os.getenv("UVICORN_DATE_HEADER").lower() == "true" - ) - if os.getenv("UVICORN_LIMIT_CONCURRENCY"): - config_kwargs["limit_concurrency"] = int(os.getenv("UVICORN_LIMIT_CONCURRENCY")) - if os.getenv("UVICORN_LIMIT_MAX_REQUESTS"): - config_kwargs["limit_max_requests"] = int( - os.getenv("UVICORN_LIMIT_MAX_REQUESTS") - ) - if os.getenv("UVICORN_TIMEOUT_KEEP_ALIVE"): - config_kwargs["timeout_keep_alive"] = int( - os.getenv("UVICORN_TIMEOUT_KEEP_ALIVE") - ) - if os.getenv("UVICORN_TIMEOUT_NOTIFY"): - config_kwargs["timeout_notify"] = int(os.getenv("UVICORN_TIMEOUT_NOTIFY")) - if os.getenv("UVICORN_SSL_KEYFILE"): - config_kwargs["ssl_keyfile"] = os.getenv("UVICORN_SSL_KEYFILE") - if os.getenv("UVICORN_SSL_CERTFILE"): - config_kwargs["ssl_certfile"] = os.getenv("UVICORN_SSL_CERTFILE") - if os.getenv("UVICORN_SSL_KEYFILE_PASSWORD"): - config_kwargs["ssl_keyfile_password"] = os.getenv( - "UVICORN_SSL_KEYFILE_PASSWORD" - ) - if os.getenv("UVICORN_SSL_VERSION"): - config_kwargs["ssl_version"] = int(os.getenv("UVICORN_SSL_VERSION")) - if os.getenv("UVICORN_SSL_CERT_REQS"): - config_kwargs["ssl_cert_reqs"] = int(os.getenv("UVICORN_SSL_CERT_REQS")) - if os.getenv("UVICORN_SSL_CA_CERTS"): - config_kwargs["ssl_ca_certs"] = os.getenv("UVICORN_SSL_CA_CERTS") - if os.getenv("UVICORN_SSL_CIPHERS"): - config_kwargs["ssl_ciphers"] = os.getenv("UVICORN_SSL_CIPHERS") - if os.getenv("UVICORN_HEADERS"): - config_kwargs["headers"] = [ - tuple(h.split(":", 1)) - for h in os.getenv("UVICORN_HEADERS").split(",") - if ":" in h - ] - if os.getenv("UVICORN_USE_COLORS"): - config_kwargs["use_colors"] = os.getenv("UVICORN_USE_COLORS").lower() == "true" - if os.getenv("UVICORN_UDS"): - config_kwargs["uds"] = os.getenv("UVICORN_UDS") - if os.getenv("UVICORN_FD"): - config_kwargs["fd"] = int(os.getenv("UVICORN_FD")) - if os.getenv("UVICORN_ROOT_PATH"): - config_kwargs["root_path"] = os.getenv("UVICORN_ROOT_PATH") - - config = uvicorn.Config(**config_kwargs) uvicorn.run(config) From 03919c88c6c69c91cce3639917c4f91ffba19ddd Mon Sep 17 00:00:00 2001 From: Muhamad Aji Wibisono Date: Tue, 10 Jun 2025 22:52:19 +0700 Subject: [PATCH 4/9] fix: config loading into run --- surfsense_backend/.env.example | 63 +++++++++++++++++----------------- surfsense_backend/main.py | 5 ++- 2 files changed, 33 insertions(+), 35 deletions(-) diff --git a/surfsense_backend/.env.example b/surfsense_backend/.env.example index 336bf28..e0b53ad 100644 --- a/surfsense_backend/.env.example +++ b/surfsense_backend/.env.example @@ -54,36 +54,35 @@ STT_SERVICE_API_BASE="" # --- Uvicorn Server Configuration --- # Full documentation for Uvicorn options can be found at: https://www.uvicorn.org/#command-line-options -UVICORN_HOST="0.0.0.0" # Host to bind (default: 0.0.0.0) -UVICORN_PORT=8000 # Port to bind (default: 8000) -UVICORN_LOG_LEVEL="info" # Log level (critical, error, warning, info, debug, trace) +UVICORN_HOST="0.0.0.0" +UVICORN_PORT=8000 +UVICORN_LOG_LEVEL=info -# --- Advanced Uvicorn Options (uncomment to use) --- -# UVICORN_PROXY_HEADERS=false # Enable proxy headers (true/false) -# UVICORN_FORWARDED_ALLOW_IPS="127.0.0.1" # Comma-separated list of IPs to trust with proxy headers -# UVICORN_WORKERS=1 # Number of worker processes -# UVICORN_ACCESS_LOG=true # Access log (true/false) -# UVICORN_LOOP="auto" # Event loop implementation (auto, asyncio, uvloop) -# UVICORN_HTTP="auto" # HTTP protocol implementation (auto, h11, httptools) -# UVICORN_WS="auto" # WebSocket protocol implementation (auto, none, websockets, wsproto) -# UVICORN_LIFESPAN="auto" # Lifespan implementation (auto, on, off) -# UVICORN_ENV_FILE=".env" # Environment configuration file -# UVICORN_LOG_CONFIG="" # Logging configuration file (YAML/JSON/dict) -# UVICORN_SERVER_HEADER=true # Enable server header (true/false) -# UVICORN_DATE_HEADER=true # Enable date header (true/false) -# UVICORN_LIMIT_CONCURRENCY= # Maximum number of concurrent connections or tasks -# UVICORN_LIMIT_MAX_REQUESTS= # Maximum number of requests a worker will process before restarting -# UVICORN_TIMEOUT_KEEP_ALIVE=5 # Timeout for keep-alive connections (seconds) -# UVICORN_TIMEOUT_NOTIFY=30 # Timeout for graceful shutdown (seconds) -# UVICORN_SSL_KEYFILE="" # SSL key file (path) -# UVICORN_SSL_CERTFILE="" # SSL certificate file (path) -# UVICORN_SSL_KEYFILE_PASSWORD="" # SSL key file password -# UVICORN_SSL_VERSION="" # SSL version to use -# UVICORN_SSL_CERT_REQS="" # Whether client certificate is required (none, optional, required) -# UVICORN_SSL_CA_CERTS="" # CA certificates file -# UVICORN_SSL_CIPHERS="" # Ciphers to use -# UVICORN_HEADERS="" # List of custom default HTTP response headers (comma-separated, e.g. X-Frame-Options:DENY,X-Content-Type-Options:nosniff) -# UVICORN_USE_COLORS=true # Enable colorized logging output (true/false) -# UVICORN_UDS="" # Bind to a UNIX domain socket (overrides host/port) -# UVICORN_FD="" # Bind to socket from this file descriptor -# UVICORN_ROOT_PATH="" # Root path for ASGI applications submounted below a given URL path +# --- Advanced Uvicorn Options --- +# UVICORN_PROXY_HEADERS=false +# UVICORN_FORWARDED_ALLOW_IPS="127.0.0.1" +# UVICORN_WORKERS=1 +# UVICORN_ACCESS_LOG=true +# UVICORN_LOOP="auto" +# UVICORN_HTTP="auto" +# UVICORN_WS="auto" +# UVICORN_LIFESPAN="auto" +# UVICORN_LOG_CONFIG="" +# UVICORN_SERVER_HEADER=true +# UVICORN_DATE_HEADER=true +# UVICORN_LIMIT_CONCURRENCY= +# UVICORN_LIMIT_MAX_REQUESTS= +# UVICORN_TIMEOUT_KEEP_ALIVE=5 +# UVICORN_TIMEOUT_NOTIFY=30 +# UVICORN_SSL_KEYFILE="" +# UVICORN_SSL_CERTFILE="" +# UVICORN_SSL_KEYFILE_PASSWORD="" +# UVICORN_SSL_VERSION="" +# UVICORN_SSL_CERT_REQS="" +# UVICORN_SSL_CA_CERTS="" +# UVICORN_SSL_CIPHERS="" +# UVICORN_HEADERS="" +# UVICORN_USE_COLORS=true +# UVICORN_UDS="" +# UVICORN_FD="" +# UVICORN_ROOT_PATH="" diff --git a/surfsense_backend/main.py b/surfsense_backend/main.py index 79cc1bc..2884cf3 100644 --- a/surfsense_backend/main.py +++ b/surfsense_backend/main.py @@ -17,6 +17,5 @@ if __name__ == "__main__": parser.add_argument("--reload", action="store_true", help="Enable hot reloading") args = parser.parse_args() - config = uvicorn.Config(**load_uvicorn_config(args)) - - uvicorn.run(config) + config_kwargs = load_uvicorn_config(args) + uvicorn.run(**config_kwargs) From 131397de4ea3c867a78c891ac8dbcaafed6ac0fe Mon Sep 17 00:00:00 2001 From: Muhamad Aji Wibisono Date: Tue, 10 Jun 2025 23:12:59 +0700 Subject: [PATCH 5/9] refactor: changed uvicorn server initialization --- surfsense_backend/main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/surfsense_backend/main.py b/surfsense_backend/main.py index 2884cf3..f44b7d6 100644 --- a/surfsense_backend/main.py +++ b/surfsense_backend/main.py @@ -18,4 +18,7 @@ if __name__ == "__main__": args = parser.parse_args() config_kwargs = load_uvicorn_config(args) - uvicorn.run(**config_kwargs) + config = uvicorn.Config(**config_kwargs) + server = uvicorn.Server(config) + + server.run() From d31b9589fba9990b07603f592a37044df342960a Mon Sep 17 00:00:00 2001 From: Muhamad Aji Wibisono Date: Tue, 10 Jun 2025 23:26:28 +0700 Subject: [PATCH 6/9] chore: organized env example style --- surfsense_backend/.env.example | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/surfsense_backend/.env.example b/surfsense_backend/.env.example index e0b53ad..6b364e0 100644 --- a/surfsense_backend/.env.example +++ b/surfsense_backend/.env.example @@ -3,42 +3,42 @@ DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/surfsense SECRET_KEY=SECRET NEXT_FRONTEND_URL=http://localhost:3000 -#Auth +# Auth AUTH_TYPE=GOOGLE or LOCAL # For Google Auth Only GOOGLE_OAUTH_CLIENT_ID=924507538m GOOGLE_OAUTH_CLIENT_SECRET=GOCSV -#Embedding Model +# Embedding Model EMBEDDING_MODEL=mixedbread-ai/mxbai-embed-large-v1 RERANKERS_MODEL_NAME=ms-marco-MiniLM-L-12-v2 RERANKERS_MODEL_TYPE=flashrank -#LiteLLM TTS Provider: https://docs.litellm.ai/docs/text_to_speech#supported-providers +# LiteLLM TTS Provider: https://docs.litellm.ai/docs/text_to_speech#supported-providers TTS_SERVICE=openai/tts-1 -#Respective TTS Service API +# Respective TTS Service API TTS_SERVICE_API_KEY= -#OPTIONAL: TTS Provider API Base +# OPTIONAL: TTS Provider API Base TTS_SERVICE_API_BASE= -#LiteLLM STT Provider: https://docs.litellm.ai/docs/audio_transcription#supported-providers +# LiteLLM STT Provider: https://docs.litellm.ai/docs/audio_transcription#supported-providers STT_SERVICE=openai/whisper-1 -#Respective STT Service API +# Respective STT Service API STT_SERVICE_API_KEY="" -#OPTIONAL: STT Provider API Base +# OPTIONAL: STT Provider API Base STT_SERVICE_API_BASE= FIRECRAWL_API_KEY=fcr-01J0000000000000000000000 -#File Parser Service +# File Parser Service ETL_SERVICE=UNSTRUCTURED or LLAMACLOUD UNSTRUCTURED_API_KEY=Tpu3P0U8iy LLAMA_CLOUD_API_KEY=llx-nnn -#OPTIONAL: Add these for LangSmith Observability +# OPTIONAL: Add these for LangSmith Observability LANGSMITH_TRACING=true LANGSMITH_ENDPOINT=https://api.smith.langchain.com LANGSMITH_API_KEY=lsv2_pt_..... @@ -52,13 +52,13 @@ LONG_CONTEXT_LLM_API_BASE="" TTS_SERVICE_API_BASE="" STT_SERVICE_API_BASE="" -# --- Uvicorn Server Configuration --- +# Uvicorn Server Configuration # Full documentation for Uvicorn options can be found at: https://www.uvicorn.org/#command-line-options UVICORN_HOST="0.0.0.0" UVICORN_PORT=8000 UVICORN_LOG_LEVEL=info -# --- Advanced Uvicorn Options --- +# OPTIONAL: Advanced Uvicorn Options (uncomment to use) # UVICORN_PROXY_HEADERS=false # UVICORN_FORWARDED_ALLOW_IPS="127.0.0.1" # UVICORN_WORKERS=1 From 5a39920e4039e446294e179fa075ebf178bc3303 Mon Sep 17 00:00:00 2001 From: Muhamad Aji Wibisono Date: Tue, 10 Jun 2025 23:39:13 +0700 Subject: [PATCH 7/9] chore: removed duplicate .env var --- surfsense_backend/.env.example | 8 -------- 1 file changed, 8 deletions(-) diff --git a/surfsense_backend/.env.example b/surfsense_backend/.env.example index 6b364e0..2fd950e 100644 --- a/surfsense_backend/.env.example +++ b/surfsense_backend/.env.example @@ -44,14 +44,6 @@ LANGSMITH_ENDPOINT=https://api.smith.langchain.com LANGSMITH_API_KEY=lsv2_pt_..... LANGSMITH_PROJECT=surfsense - -# OPTIONAL: LiteLLM API Base -FAST_LLM_API_BASE="" -STRATEGIC_LLM_API_BASE="" -LONG_CONTEXT_LLM_API_BASE="" -TTS_SERVICE_API_BASE="" -STT_SERVICE_API_BASE="" - # Uvicorn Server Configuration # Full documentation for Uvicorn options can be found at: https://www.uvicorn.org/#command-line-options UVICORN_HOST="0.0.0.0" From 3022e448030e824d391c2028810cc4f3d68ea050 Mon Sep 17 00:00:00 2001 From: Muhamad Aji Wibisono Date: Wed, 11 Jun 2025 00:02:54 +0700 Subject: [PATCH 8/9] refactor: applied coderabbit refactor suggestion --- surfsense_backend/app/config/uvicorn.py | 136 +++++++++++------------- 1 file changed, 63 insertions(+), 73 deletions(-) diff --git a/surfsense_backend/app/config/uvicorn.py b/surfsense_backend/app/config/uvicorn.py index deea190..f7086e1 100644 --- a/surfsense_backend/app/config/uvicorn.py +++ b/surfsense_backend/app/config/uvicorn.py @@ -1,5 +1,27 @@ import os +def _parse_bool(value): + """Parse boolean value from string.""" + return value.lower() == "true" if value else False + +def _parse_int(value, var_name): + """Parse integer value with error handling.""" + try: + return int(value) + except ValueError: + raise ValueError(f"Invalid integer value for {var_name}: {value}") + +def _parse_headers(value): + """Parse headers from comma-separated string.""" + try: + return [ + tuple(h.split(":", 1)) + for h in value.split(",") + if ":" in h + ] + except Exception: + raise ValueError(f"Invalid headers format: {value}") + def load_uvicorn_config(args=None): """ @@ -14,79 +36,47 @@ def load_uvicorn_config(args=None): reload=args.reload if args else False, reload_dirs=["app"] if (args and args.reload) else None, ) + + # Configuration mapping for advanced options + config_mapping = { + "UVICORN_PROXY_HEADERS": ("proxy_headers", _parse_bool), + "UVICORN_FORWARDED_ALLOW_IPS": ("forwarded_allow_ips", str), + "UVICORN_WORKERS": ("workers", lambda x: _parse_int(x, "UVICORN_WORKERS")), + "UVICORN_ACCESS_LOG": ("access_log", _parse_bool), + "UVICORN_LOOP": ("loop", str), + "UVICORN_HTTP": ("http", str), + "UVICORN_WS": ("ws", str), + "UVICORN_LIFESPAN": ("lifespan", str), + "UVICORN_ENV_FILE": ("env_file", str), + "UVICORN_LOG_CONFIG": ("log_config", str), + "UVICORN_SERVER_HEADER": ("server_header", _parse_bool), + "UVICORN_DATE_HEADER": ("date_header", _parse_bool), + "UVICORN_LIMIT_CONCURRENCY": ("limit_concurrency", lambda x: _parse_int(x, "UVICORN_LIMIT_CONCURRENCY")), + "UVICORN_LIMIT_MAX_REQUESTS": ("limit_max_requests", lambda x: _parse_int(x, "UVICORN_LIMIT_MAX_REQUESTS")), + "UVICORN_TIMEOUT_KEEP_ALIVE": ("timeout_keep_alive", lambda x: _parse_int(x, "UVICORN_TIMEOUT_KEEP_ALIVE")), + "UVICORN_TIMEOUT_NOTIFY": ("timeout_notify", lambda x: _parse_int(x, "UVICORN_TIMEOUT_NOTIFY")), + "UVICORN_SSL_KEYFILE": ("ssl_keyfile", str), + "UVICORN_SSL_CERTFILE": ("ssl_certfile", str), + "UVICORN_SSL_KEYFILE_PASSWORD": ("ssl_keyfile_password", str), + "UVICORN_SSL_VERSION": ("ssl_version", lambda x: _parse_int(x, "UVICORN_SSL_VERSION")), + "UVICORN_SSL_CERT_REQS": ("ssl_cert_reqs", lambda x: _parse_int(x, "UVICORN_SSL_CERT_REQS")), + "UVICORN_SSL_CA_CERTS": ("ssl_ca_certs", str), + "UVICORN_SSL_CIPHERS": ("ssl_ciphers", str), + "UVICORN_HEADERS": ("headers", _parse_headers), + "UVICORN_USE_COLORS": ("use_colors", _parse_bool), + "UVICORN_UDS": ("uds", str), + "UVICORN_FD": ("fd", lambda x: _parse_int(x, "UVICORN_FD")), + "UVICORN_ROOT_PATH": ("root_path", str), + } + + # Process advanced configuration options + for env_var, (config_key, parser) in config_mapping.items(): + value = os.getenv(env_var) + if value: + try: + config_kwargs[config_key] = parser(value) + except ValueError as e: + raise ValueError(f"Configuration error for {env_var}: {e}") - # Only add advanced args if set in env - if os.getenv("UVICORN_PROXY_HEADERS"): - config_kwargs["proxy_headers"] = ( - os.getenv("UVICORN_PROXY_HEADERS").lower() == "true" - ) - if os.getenv("UVICORN_FORWARDED_ALLOW_IPS"): - config_kwargs["forwarded_allow_ips"] = os.getenv("UVICORN_FORWARDED_ALLOW_IPS") - if os.getenv("UVICORN_WORKERS"): - config_kwargs["workers"] = int(os.getenv("UVICORN_WORKERS")) - if os.getenv("UVICORN_ACCESS_LOG"): - config_kwargs["access_log"] = os.getenv("UVICORN_ACCESS_LOG").lower() == "true" - if os.getenv("UVICORN_LOOP"): - config_kwargs["loop"] = os.getenv("UVICORN_LOOP") - if os.getenv("UVICORN_HTTP"): - config_kwargs["http"] = os.getenv("UVICORN_HTTP") - if os.getenv("UVICORN_WS"): - config_kwargs["ws"] = os.getenv("UVICORN_WS") - if os.getenv("UVICORN_LIFESPAN"): - config_kwargs["lifespan"] = os.getenv("UVICORN_LIFESPAN") - if os.getenv("UVICORN_ENV_FILE"): - config_kwargs["env_file"] = os.getenv("UVICORN_ENV_FILE") - if os.getenv("UVICORN_LOG_CONFIG"): - config_kwargs["log_config"] = os.getenv("UVICORN_LOG_CONFIG") - if os.getenv("UVICORN_SERVER_HEADER"): - config_kwargs["server_header"] = ( - os.getenv("UVICORN_SERVER_HEADER").lower() == "true" - ) - if os.getenv("UVICORN_DATE_HEADER"): - config_kwargs["date_header"] = ( - os.getenv("UVICORN_DATE_HEADER").lower() == "true" - ) - if os.getenv("UVICORN_LIMIT_CONCURRENCY"): - config_kwargs["limit_concurrency"] = int(os.getenv("UVICORN_LIMIT_CONCURRENCY")) - if os.getenv("UVICORN_LIMIT_MAX_REQUESTS"): - config_kwargs["limit_max_requests"] = int( - os.getenv("UVICORN_LIMIT_MAX_REQUESTS") - ) - if os.getenv("UVICORN_TIMEOUT_KEEP_ALIVE"): - config_kwargs["timeout_keep_alive"] = int( - os.getenv("UVICORN_TIMEOUT_KEEP_ALIVE") - ) - if os.getenv("UVICORN_TIMEOUT_NOTIFY"): - config_kwargs["timeout_notify"] = int(os.getenv("UVICORN_TIMEOUT_NOTIFY")) - if os.getenv("UVICORN_SSL_KEYFILE"): - config_kwargs["ssl_keyfile"] = os.getenv("UVICORN_SSL_KEYFILE") - if os.getenv("UVICORN_SSL_CERTFILE"): - config_kwargs["ssl_certfile"] = os.getenv("UVICORN_SSL_CERTFILE") - if os.getenv("UVICORN_SSL_KEYFILE_PASSWORD"): - config_kwargs["ssl_keyfile_password"] = os.getenv( - "UVICORN_SSL_KEYFILE_PASSWORD" - ) - if os.getenv("UVICORN_SSL_VERSION"): - config_kwargs["ssl_version"] = int(os.getenv("UVICORN_SSL_VERSION")) - if os.getenv("UVICORN_SSL_CERT_REQS"): - config_kwargs["ssl_cert_reqs"] = int(os.getenv("UVICORN_SSL_CERT_REQS")) - if os.getenv("UVICORN_SSL_CA_CERTS"): - config_kwargs["ssl_ca_certs"] = os.getenv("UVICORN_SSL_CA_CERTS") - if os.getenv("UVICORN_SSL_CIPHERS"): - config_kwargs["ssl_ciphers"] = os.getenv("UVICORN_SSL_CIPHERS") - if os.getenv("UVICORN_HEADERS"): - config_kwargs["headers"] = [ - tuple(h.split(":", 1)) - for h in os.getenv("UVICORN_HEADERS").split(",") - if ":" in h - ] - if os.getenv("UVICORN_USE_COLORS"): - config_kwargs["use_colors"] = os.getenv("UVICORN_USE_COLORS").lower() == "true" - if os.getenv("UVICORN_UDS"): - config_kwargs["uds"] = os.getenv("UVICORN_UDS") - if os.getenv("UVICORN_FD"): - config_kwargs["fd"] = int(os.getenv("UVICORN_FD")) - if os.getenv("UVICORN_ROOT_PATH"): - config_kwargs["root_path"] = os.getenv("UVICORN_ROOT_PATH") return config_kwargs From d23541ee4412db1e6504fc8ea205528a898cff0a Mon Sep 17 00:00:00 2001 From: Muhamad Aji Wibisono Date: Wed, 11 Jun 2025 10:09:43 +0700 Subject: [PATCH 9/9] docs: added documentation about uvicorn environment variables --- .../content/docs/docker-installation.mdx | 36 ++++++++++++++++++ .../content/docs/manual-installation.mdx | 38 +++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/surfsense_web/content/docs/docker-installation.mdx b/surfsense_web/content/docs/docker-installation.mdx index ca91d21..0a5ba8a 100644 --- a/surfsense_web/content/docs/docker-installation.mdx +++ b/surfsense_web/content/docs/docker-installation.mdx @@ -110,6 +110,42 @@ Before you begin, ensure you have: | LANGSMITH_API_KEY | Your LangSmith API key | | LANGSMITH_PROJECT | LangSmith project name (e.g., `surfsense`) | +**Backend Uvicorn Server Configuration:** +| ENV VARIABLE | DESCRIPTION | DEFAULT VALUE | +|------------------------------|---------------------------------------------|---------------| +| UVICORN_HOST | Host address to bind the server | 0.0.0.0 | +| UVICORN_PORT | Port to run the backend API | 8000 | +| UVICORN_LOG_LEVEL | Logging level (e.g., info, debug, warning) | info | +| UVICORN_PROXY_HEADERS | Enable/disable proxy headers | false | +| UVICORN_FORWARDED_ALLOW_IPS | Comma-separated list of allowed IPs | 127.0.0.1 | +| UVICORN_WORKERS | Number of worker processes | 1 | +| UVICORN_ACCESS_LOG | Enable/disable access log (true/false) | true | +| UVICORN_LOOP | Event loop implementation | auto | +| UVICORN_HTTP | HTTP protocol implementation | auto | +| UVICORN_WS | WebSocket protocol implementation | auto | +| UVICORN_LIFESPAN | Lifespan implementation | auto | +| UVICORN_LOG_CONFIG | Path to logging config file or empty string | | +| UVICORN_SERVER_HEADER | Enable/disable Server header | true | +| UVICORN_DATE_HEADER | Enable/disable Date header | true | +| UVICORN_LIMIT_CONCURRENCY | Max concurrent connections | | +| UVICORN_LIMIT_MAX_REQUESTS | Max requests before worker restart | | +| UVICORN_TIMEOUT_KEEP_ALIVE | Keep-alive timeout (seconds) | 5 | +| UVICORN_TIMEOUT_NOTIFY | Worker shutdown notification timeout (sec) | 30 | +| UVICORN_SSL_KEYFILE | Path to SSL key file | | +| UVICORN_SSL_CERTFILE | Path to SSL certificate file | | +| UVICORN_SSL_KEYFILE_PASSWORD | Password for SSL key file | | +| UVICORN_SSL_VERSION | SSL version | | +| UVICORN_SSL_CERT_REQS | SSL certificate requirements | | +| UVICORN_SSL_CA_CERTS | Path to CA certificates file | | +| UVICORN_SSL_CIPHERS | SSL ciphers | | +| UVICORN_HEADERS | Comma-separated list of headers | | +| UVICORN_USE_COLORS | Enable/disable colored logs | true | +| UVICORN_UDS | Unix domain socket path | | +| UVICORN_FD | File descriptor to bind to | | +| UVICORN_ROOT_PATH | Root path for the application | | + +For more details, see the [Uvicorn documentation](https://www.uvicorn.org/#command-line-options). + ### Frontend Environment Variables | ENV VARIABLE | DESCRIPTION | diff --git a/surfsense_web/content/docs/manual-installation.mdx b/surfsense_web/content/docs/manual-installation.mdx index a6a104a..1f58783 100644 --- a/surfsense_web/content/docs/manual-installation.mdx +++ b/surfsense_web/content/docs/manual-installation.mdx @@ -80,6 +80,44 @@ Edit the `.env` file and set the following variables: | LANGSMITH_API_KEY | Your LangSmith API key | | LANGSMITH_PROJECT | LangSmith project name (e.g., `surfsense`) | +**Uvicorn Server Configuration** +| ENV VARIABLE | DESCRIPTION | DEFAULT VALUE | +|------------------------------|---------------------------------------------|---------------| +| UVICORN_HOST | Host address to bind the server | 0.0.0.0 | +| UVICORN_PORT | Port to run the backend API | 8000 | +| UVICORN_LOG_LEVEL | Logging level (e.g., info, debug, warning) | info | +| UVICORN_PROXY_HEADERS | Enable/disable proxy headers | false | +| UVICORN_FORWARDED_ALLOW_IPS | Comma-separated list of allowed IPs | 127.0.0.1 | +| UVICORN_WORKERS | Number of worker processes | 1 | +| UVICORN_ACCESS_LOG | Enable/disable access log (true/false) | true | +| UVICORN_LOOP | Event loop implementation | auto | +| UVICORN_HTTP | HTTP protocol implementation | auto | +| UVICORN_WS | WebSocket protocol implementation | auto | +| UVICORN_LIFESPAN | Lifespan implementation | auto | +| UVICORN_LOG_CONFIG | Path to logging config file or empty string | | +| UVICORN_SERVER_HEADER | Enable/disable Server header | true | +| UVICORN_DATE_HEADER | Enable/disable Date header | true | +| UVICORN_LIMIT_CONCURRENCY | Max concurrent connections | | +| UVICORN_LIMIT_MAX_REQUESTS | Max requests before worker restart | | +| UVICORN_TIMEOUT_KEEP_ALIVE | Keep-alive timeout (seconds) | 5 | +| UVICORN_TIMEOUT_NOTIFY | Worker shutdown notification timeout (sec) | 30 | +| UVICORN_SSL_KEYFILE | Path to SSL key file | | +| UVICORN_SSL_CERTFILE | Path to SSL certificate file | | +| UVICORN_SSL_KEYFILE_PASSWORD | Password for SSL key file | | +| UVICORN_SSL_VERSION | SSL version | | +| UVICORN_SSL_CERT_REQS | SSL certificate requirements | | +| UVICORN_SSL_CA_CERTS | Path to CA certificates file | | +| UVICORN_SSL_CIPHERS | SSL ciphers | | +| UVICORN_HEADERS | Comma-separated list of headers | | +| UVICORN_USE_COLORS | Enable/disable colored logs | true | +| UVICORN_UDS | Unix domain socket path | | +| UVICORN_FD | File descriptor to bind to | | +| UVICORN_ROOT_PATH | Root path for the application | | + +Refer to the `.env.example` file for all available Uvicorn options and their usage. Uncomment and set in your `.env` file as needed. + +For more details, see the [Uvicorn documentation](https://www.uvicorn.org/#command-line-options). + ### 2. Install Dependencies Install the backend dependencies using `uv`: