Files
dienakdz 87f2845483 Refactor code for improved readability and consistency
- Cleaned up whitespace and formatting in various files including http.py, language.py, logger.py, safe_exec.py, and SQL migration scripts.
- Consolidated import statements and removed unnecessary blank lines.
- Updated logging configuration for better clarity.
- Enhanced the safe execution code with improved error handling and logging.
- Removed commented-out code and unnecessary variables in backfill_zero_trades.py and other scripts.
- Added a pyproject.toml for Ruff and Vulture configuration.
- Introduced requirements-dev.txt for development dependencies.
- Removed commented-out stock entries in init.sql for cleaner migration scripts.
2026-04-09 14:30:51 +07:00

143 lines
4.9 KiB
Python

"""
QuantDinger Python API entrypoint.
"""
import os
import sys
from app import create_app
from app.config.settings import Config
# Ensure UTF-8 console output on Windows to avoid UnicodeEncodeError in logs.
# (PowerShell default encoding may be GBK/CP936.)
try:
if hasattr(sys.stdout, "reconfigure"):
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
if hasattr(sys.stderr, "reconfigure"):
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
except Exception:
pass
# Load local .env early so config classes can read from os.environ.
# This keeps local deployment simple: edit one file and run.
try:
from dotenv import load_dotenv
this_dir = os.path.dirname(os.path.abspath(__file__))
# Primary: backend_api_python/.env (same dir as run.py)
load_dotenv(os.path.join(this_dir, ".env"), override=False)
# Fallback: repo-root/.env (one level up) for users who place .env at workspace root.
parent_dir = os.path.dirname(this_dir)
load_dotenv(os.path.join(parent_dir, ".env"), override=False)
except Exception:
# python-dotenv is optional; environment variables can still be provided by the OS.
pass
# Optional: disable tqdm progress bars (some data providers like akshare may emit them),
# keeping console logs clean in local mode.
os.environ.setdefault("TQDM_DISABLE", "1")
# Optional: normalize outbound proxy settings for the whole process.
# This makes requests/yfinance/finnhub/tiingo/GoogleSearch etc work behind a local proxy.
def _apply_proxy_env():
def _set_if_blank(key: str, value: str) -> None:
"""
Set env var if it is missing OR present but empty.
(`os.environ.setdefault` does not override empty strings.)
"""
cur = os.getenv(key)
if cur is None or str(cur).strip() == "":
os.environ[key] = value
# If user provided explicit proxy URL, honor it.
proxy_url = (os.getenv("PROXY_URL") or "").strip()
if not proxy_url:
return
# Standard env vars used by requests and many libraries.
_set_if_blank("ALL_PROXY", proxy_url)
_set_if_blank("HTTP_PROXY", proxy_url)
_set_if_blank("HTTPS_PROXY", proxy_url)
_apply_proxy_env()
#################
# Optional: Disable SSL verification for development in corporate environments (SSL Inspection)
# This is NOT recommended for production use.
def _apply_ssl_verify():
verify_ssl = os.getenv("PYTHON_SSL_VERIFY", "true").lower() == "true"
if not verify_ssl:
import requests
import urllib3
from urllib3.exceptions import InsecureRequestWarning
# Suppress InsecureRequestWarning
urllib3.disable_warnings(InsecureRequestWarning)
# Monkey patch requests.Session.request to default verify=False
# This affects requests, yfinance, and many other libraries.
original_request = requests.Session.request
def patched_request(self, method, url, *args, **kwargs):
if "verify" not in kwargs:
kwargs["verify"] = False
return original_request(self, method, url, *args, **kwargs)
requests.Session.request = patched_request
# Also set common environment variables to skip verification
os.environ["CURL_CA_BUNDLE"] = ""
os.environ["REQUESTS_CA_BUNDLE"] = ""
os.environ["PYTHONHTTPSVERIFY"] = "0"
print("[DEV ONLY] SSL Verification disabled globally (Corporate SSL Inspection bypass)")
_apply_ssl_verify()
##################
# Add project root to Python path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# Create app instance (for gunicorn use)
# gunicorn -c gunicorn_config.py "run:app"
app = create_app()
def main():
"""Start application"""
# Keep startup messages ASCII-only and short.
print("QuantDinger Python API v2.2.2")
# ========== Critical Security Check for SECRET_KEY ==========
# In production (DEBUG=False), the SECRET_KEY MUST NOT use the default example value.
# This prevents attackers from forging JWT tokens with admin privileges.
default_secret = "quantdinger-secret-key-change-me"
current_secret = Config.SECRET_KEY
if not Config.DEBUG and current_secret == default_secret:
msg = (
"\n[SECURITY ERROR] SECRET_KEY is using the default example value.\n"
"You MUST change SECRET_KEY in backend_api_python/.env before running in production.\n"
"Example:\n"
" SECRET_KEY=$(python - << 'EOF'\n"
"import secrets; print(secrets.token_hex(32))\n"
"EOF\n"
)
# Print to both stdout and raise to stop the server
print(msg)
raise RuntimeError("Insecure SECRET_KEY configuration: using default example value in non-debug mode")
print(f"Service starting at: http://{Config.HOST}:{Config.PORT}")
# Flask dev server is for local development only.
app.run(host=Config.HOST, port=Config.PORT, debug=Config.DEBUG, threaded=True)
if __name__ == "__main__":
main()