diff --git a/.env.template b/.env.template index 4a28fe3a..9ef756b4 100644 --- a/.env.template +++ b/.env.template @@ -1,2 +1,6 @@ SWAPPS_TRACE_ELOG_API_URL=https://accel-webapp-dev.slac.stanford.edu/api/elog-apptoken SWAPPS_TRACE_ELOG_API_KEY= + +# Optional: Proxy URL for environments that require proxy access to ELOG API +# Leave empty or unset if no proxy is needed +SWAPPS_TRACE_ELOG_PROXY_URL= diff --git a/docs/installation.md b/docs/installation.md index 5b3a1510..d84cb7f7 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -52,6 +52,7 @@ Below is a table describing the environment variables that need to be set. | PYDM_ARCHIVER_URL | The base URL for the Archiver Appliance Data Plugin | | SWAPPS_TRACE_ELOG_API_URL | The base URL for the E-Log API | | SWAPPS_TRACE_ELOG_API_KEY | Your API key for authenticating with the E-Log system | +| SWAPPS_TRACE_ELOG_PROXY_URL | Proxy URL for environments that require proxy access to ELOG API | diff --git a/trace/main.py b/trace/main.py index f5a414b0..de002511 100644 --- a/trace/main.py +++ b/trace/main.py @@ -35,7 +35,14 @@ from config import logger, datetime_pv from file_io import PathAction, TraceFileHandler from widgets import ControlPanel, ElogPostModal, DataInsightTool, PlotSettingsModal -from services import Theme, IconColors, ThemeManager, get_user, post_entry +from services import ( + Theme, + IconColors, + ThemeManager, + get_user, + post_entry, + test_proxy_connection, +) DISABLE_AUTO_SCROLL = -2 # Using -2 as invalid since QButtonGroups use -1 as invalid @@ -552,6 +559,17 @@ def elog_button_clicked(self) -> bool: bool True if the post was successful, False otherwise. """ + # Test proxy connection first if proxy is configured + proxy_success, proxy_error = test_proxy_connection() + if not proxy_success: + error_dialog = QMessageBox() + error_dialog.setIcon(QMessageBox.Warning) + error_dialog.setWindowTitle("Proxy Connection Failed") + error_dialog.setText(proxy_error) + error_dialog.setStandardButtons(QMessageBox.Ok) + error_dialog.exec_() + return False + # Test if API is reachable status_code, _ = get_user() if status_code != 200: diff --git a/trace/services/__init__.py b/trace/services/__init__.py index 3f985237..bc057203 100644 --- a/trace/services/__init__.py +++ b/trace/services/__init__.py @@ -1,2 +1,2 @@ -from .elog_client import get_user, post_entry, get_logbooks +from .elog_client import get_user, post_entry, get_logbooks, test_proxy_connection from .theme_manager import ThemeManager, Theme, IconColors diff --git a/trace/services/elog_client.py b/trace/services/elog_client.py index 689740fa..97693aa5 100644 --- a/trace/services/elog_client.py +++ b/trace/services/elog_client.py @@ -6,6 +6,7 @@ import os import json +from typing import Optional from pathlib import Path import requests @@ -17,6 +18,53 @@ ELOG_API_URL = os.getenv("SWAPPS_TRACE_ELOG_API_URL") ELOG_API_KEY = os.getenv("SWAPPS_TRACE_ELOG_API_KEY") +# Configure proxy if specified in environment +ELOG_PROXY_URL = os.getenv("SWAPPS_TRACE_ELOG_PROXY_URL") +if ELOG_PROXY_URL: + os.environ["HTTP_PROXY"] = ELOG_PROXY_URL + os.environ["HTTPS_PROXY"] = ELOG_PROXY_URL + logger.info(f"ELOG client configured to use proxy: {ELOG_PROXY_URL}") + + +def test_proxy_connection() -> tuple[bool, Optional[str]]: + """ + Tests proxy connectivity if a proxy is configured. + + :return: A tuple of (success, error_message). If successful, error_message is None. + If failed, error_message contains a detailed description of the failure. + """ + if not ELOG_PROXY_URL: + # No proxy configured, consider this a success + return True, None + + if not ELOG_API_URL: + return False, "ELOG API URL is not configured. Cannot test proxy connection." + + error_msg = ( + f"Failed to connect through proxy {ELOG_PROXY_URL}. " + f"Please check your network connection and proxy configuration." + ) + + try: + requests.head(ELOG_API_URL, timeout=2) + logger.info(f"Proxy connection test successful: {ELOG_PROXY_URL}") + return True, None + except requests.exceptions.ProxyError as e: + logger.error(f"Proxy connection test failed: {e}") + return False, error_msg + except requests.exceptions.ConnectionError as e: + logger.error(f"Proxy connection test failed: {e}") + return False, error_msg + except requests.exceptions.Timeout as e: + error_msg = ( + f"Connection timeout through proxy {ELOG_PROXY_URL}. " f"The proxy or server may be slow or unresponsive." + ) + logger.error(f"Proxy connection test failed: {e}") + return False, error_msg + except requests.exceptions.RequestException as e: + logger.error(f"Proxy connection test failed: {e}") + return False, error_msg + def get_user() -> tuple[int, dict | Exception]: """