Spaces:
Running
Running
Sync from GitHub (tests passed)
Browse files- app/inference.py +2 -1
- app/main.py +8 -4
- app/settings.py +29 -0
app/inference.py
CHANGED
|
@@ -69,7 +69,8 @@ def get_current_price(session: Session, symbol: str) -> Optional[float]:
|
|
| 69 |
logger.info(f"Using Twelve Data price for copper: ${float(price):.4f}")
|
| 70 |
return float(price)
|
| 71 |
except Exception as e:
|
| 72 |
-
|
|
|
|
| 73 |
|
| 74 |
# Try yfinance as fallback
|
| 75 |
try:
|
|
|
|
| 69 |
logger.info(f"Using Twelve Data price for copper: ${float(price):.4f}")
|
| 70 |
return float(price)
|
| 71 |
except Exception as e:
|
| 72 |
+
from app.settings import mask_api_key
|
| 73 |
+
logger.debug(f"Twelve Data price fetch failed: {mask_api_key(str(e))}")
|
| 74 |
|
| 75 |
# Try yfinance as fallback
|
| 76 |
try:
|
app/main.py
CHANGED
|
@@ -513,8 +513,9 @@ async def get_live_price():
|
|
| 513 |
return {"price": None, "error": f"API error: {response.status_code}"}
|
| 514 |
|
| 515 |
except Exception as e:
|
| 516 |
-
|
| 517 |
-
|
|
|
|
| 518 |
|
| 519 |
|
| 520 |
# =============================================================================
|
|
@@ -586,9 +587,12 @@ async def websocket_live_price(websocket: WebSocket):
|
|
| 586 |
heartbeat_task.cancel()
|
| 587 |
|
| 588 |
except Exception as e:
|
| 589 |
-
|
|
|
|
|
|
|
|
|
|
| 590 |
try:
|
| 591 |
-
await websocket.send_json({"error":
|
| 592 |
except Exception:
|
| 593 |
pass
|
| 594 |
|
|
|
|
| 513 |
return {"price": None, "error": f"API error: {response.status_code}"}
|
| 514 |
|
| 515 |
except Exception as e:
|
| 516 |
+
from app.settings import mask_api_key
|
| 517 |
+
logger.error(f"Twelve Data API error: {mask_api_key(str(e))}")
|
| 518 |
+
return {"price": None, "error": "API error"}
|
| 519 |
|
| 520 |
|
| 521 |
# =============================================================================
|
|
|
|
| 587 |
heartbeat_task.cancel()
|
| 588 |
|
| 589 |
except Exception as e:
|
| 590 |
+
# Mask potential API keys in error messages
|
| 591 |
+
from app.settings import mask_api_key
|
| 592 |
+
safe_error = mask_api_key(str(e))
|
| 593 |
+
logger.error(f"WebSocket error: {safe_error}")
|
| 594 |
try:
|
| 595 |
+
await websocket.send_json({"error": "Connection error"}) # Don't expose details
|
| 596 |
except Exception:
|
| 597 |
pass
|
| 598 |
|
app/settings.py
CHANGED
|
@@ -155,3 +155,32 @@ def get_settings() -> Settings:
|
|
| 155 |
return Settings()
|
| 156 |
|
| 157 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
return Settings()
|
| 156 |
|
| 157 |
|
| 158 |
+
def mask_api_key(text: str, settings: Settings = None) -> str:
|
| 159 |
+
"""
|
| 160 |
+
Mask API keys in text to prevent leaking in logs.
|
| 161 |
+
Replaces known API key patterns with masked versions.
|
| 162 |
+
"""
|
| 163 |
+
import re
|
| 164 |
+
|
| 165 |
+
if settings is None:
|
| 166 |
+
settings = get_settings()
|
| 167 |
+
|
| 168 |
+
result = text
|
| 169 |
+
|
| 170 |
+
# Mask known API keys
|
| 171 |
+
keys_to_mask = [
|
| 172 |
+
settings.twelvedata_api_key,
|
| 173 |
+
settings.openrouter_api_key,
|
| 174 |
+
settings.newsapi_key,
|
| 175 |
+
settings.pipeline_trigger_secret,
|
| 176 |
+
]
|
| 177 |
+
|
| 178 |
+
for key in keys_to_mask:
|
| 179 |
+
if key and len(key) > 8:
|
| 180 |
+
masked = f"{key[:4]}...{key[-4:]}"
|
| 181 |
+
result = result.replace(key, masked)
|
| 182 |
+
|
| 183 |
+
# Also mask any apikey= query params
|
| 184 |
+
result = re.sub(r'apikey=[a-zA-Z0-9_-]+', 'apikey=***MASKED***', result)
|
| 185 |
+
|
| 186 |
+
return result
|