增加了proxy配置

main
mula.liu 2025-12-03 13:40:44 +08:00
parent 72c9b3a243
commit 0139303b6a
6 changed files with 231 additions and 5 deletions

View File

@ -18,6 +18,9 @@ REDIS_MAX_CONNECTIONS=50
# ====================== # ======================
# Application Configuration # Application Configuration
# ====================== # ======================
# JWT Secret Key (IMPORTANT: Change this in production!)
JWT_SECRET_KEY=your-production-secret-key-change-this-to-random-string
# CORS - Support both internal IP access and external domain # CORS - Support both internal IP access and external domain
# Format: comma-separated list of origins # Format: comma-separated list of origins
# Examples: # Examples:
@ -45,6 +48,15 @@ CACHE_TTL_DAYS=3
# ====================== # ======================
MAX_UPLOAD_SIZE=10485760 MAX_UPLOAD_SIZE=10485760
# ======================
# Proxy Configuration
# ======================
# HTTP Proxy for accessing NASA JPL Horizons API (optional)
# Format: http://host:port or https://host:port
# Example: HTTP_PROXY=http://192.168.124.203:20171
HTTP_PROXY=http://192.168.124.203:20171
HTTPS_PROXY=http://192.168.124.203:20171
# ====================== # ======================
# Data Path Configuration # Data Path Configuration
# ====================== # ======================

View File

@ -133,6 +133,10 @@ cosmo/
# 修改数据库密码(必须) # 修改数据库密码(必须)
DATABASE_PASSWORD=your_secure_password_here DATABASE_PASSWORD=your_secure_password_here
# 修改 JWT Secret Key必须用于生成和验证 JWT token
# 使用随机字符串,至少 32 位
JWT_SECRET_KEY=your-random-secret-key-at-least-32-characters-long
# 修改 CORS 配置(支持内网 IP 和外网域名访问) # 修改 CORS 配置(支持内网 IP 和外网域名访问)
# 方式 1: 允许所有来源(开发/测试环境) # 方式 1: 允许所有来源(开发/测试环境)
CORS_ORIGINS=* CORS_ORIGINS=*
@ -152,6 +156,13 @@ CORS_ORIGINS=http://192.168.1.100,http://your-domain.com,https://your-domain.com
# 如果前后端分离部署在不同服务器,才需要设置完整地址: # 如果前后端分离部署在不同服务器,才需要设置完整地址:
# VITE_API_BASE_URL=http://your-domain.com/api # VITE_API_BASE_URL=http://your-domain.com/api
# HTTP 代理配置(用于访问 NASA JPL Horizons API
# 如果服务器在中国境内无法直接访问 NASA API需要配置 HTTP 代理
# 格式http://host:port 或 https://host:port
# 示例HTTP_PROXY=http://192.168.124.203:20171
HTTP_PROXY=
HTTPS_PROXY=
``` ```
**重要说明**: **重要说明**:

163
PROXY_SETUP.md 100644
View File

@ -0,0 +1,163 @@
# HTTP 代理配置指南
## 问题背景
在中国境内部署 Cosmo 时,后端服务需要访问 NASA JPL Horizons API 来获取天体数据,但由于网络限制,直接访问可能会超时或失败。
## 解决方案
配置 HTTP 代理来访问 NASA API。
## 配置步骤
### 1. 在 `.env.production` 中配置代理
编辑 `/Users/jiliu/WorkSpace/cosmo/.env.production` 文件,添加代理配置:
```bash
# HTTP 代理配置(用于访问 NASA JPL Horizons API
# 格式http://host:port
HTTP_PROXY=http://192.168.124.203:20171
HTTPS_PROXY=http://192.168.124.203:20171
```
**注意**:
- 代理地址格式必须包含协议前缀(`http://` 或 `https://`
- 如果您的代理服务器只监听 HTTP两个配置都使用 `http://`
- 确保代理服务器在部署的服务器上可以访问192.168.124.203:20171
### 2. 重启后端服务
配置修改后需要重启后端服务才能生效:
```bash
# 在部署服务器上执行
cd /path/to/cosmo
./deploy.sh --restart
```
或者使用 Docker Compose
```bash
docker-compose restart backend
```
## 工作原理
### 1. httpx 代理配置
后端使用 `httpx` 库直接访问 NASA API`get_object_data_raw` 方法)。代理通过 `httpx.AsyncClient``proxies` 参数传递:
```python
client_kwargs = {"timeout": 5.0}
if settings.proxy_dict:
client_kwargs["proxies"] = settings.proxy_dict
# proxies = {"http://": "http://192.168.124.203:20171",
# "https://": "http://192.168.124.203:20171"}
async with httpx.AsyncClient(**client_kwargs) as client:
response = await client.get(url, params=params)
```
### 2. astroquery 代理配置
`astroquery` 库(用于 `get_body_positions` 方法)通过标准环境变量使用代理:
```python
if settings.http_proxy:
os.environ['HTTP_PROXY'] = settings.http_proxy
if settings.https_proxy:
os.environ['HTTPS_PROXY'] = settings.https_proxy
```
## 验证配置
### 1. 检查日志
配置代理后,启动服务时会在日志中看到:
```
INFO - Set HTTP_PROXY for astroquery: http://192.168.124.203:20171
INFO - Set HTTPS_PROXY for astroquery: http://192.168.124.203:20171
```
访问 NASA API 时会看到:
```
INFO - Using proxy for NASA API: {'http://': 'http://192.168.124.203:20171', 'https://': 'http://192.168.124.203:20171'}
INFO - Fetching raw data for body 10
```
### 2. 测试 API 访问
在前端点击天体的 "JPL Horizons" 按钮,查看是否能成功获取数据。
或者直接访问后端 API
```bash
curl http://your-server-ip/api/nasa/object/10
```
## 常见问题
### 1. 代理地址格式错误
**错误**:
```bash
HTTP_PROXY=192.168.124.203:20171 # 缺少协议前缀
```
**正确**:
```bash
HTTP_PROXY=http://192.168.124.203:20171
```
### 2. 代理服务器无法访问
确保代理服务器在部署服务器上可以访问:
```bash
# 在部署服务器上测试
curl -x http://192.168.124.203:20171 https://ssd.jpl.nasa.gov/api/horizons.api
```
### 3. 仍然超时
如果配置代理后仍然超时,检查:
1. 代理服务器是否运行正常
2. 代理服务器是否允许访问 `ssd.jpl.nasa.gov`
3. 防火墙是否阻止了连接
### 4. 日志中没有代理信息
如果日志中没有 "Using proxy" 信息,检查:
1. `.env.production` 文件是否正确配置
2. 是否重启了服务
3. Docker 容器是否正确加载了环境变量:
```bash
docker-compose exec backend env | grep PROXY
```
## 不需要代理的情况
如果您的服务器可以直接访问 NASA API例如海外服务器只需将代理配置留空即可
```bash
HTTP_PROXY=
HTTPS_PROXY=
```
## 相关文件
- **配置**: `backend/app/config.py` - Settings 类中的 `http_proxy`, `https_proxy`, `proxy_dict`
- **使用**: `backend/app/services/horizons.py` - HorizonsService 类
- **环境变量**: `.env.production` - HTTP_PROXY, HTTPS_PROXY
## 参考链接
- [httpx Proxies Documentation](https://www.python-httpx.org/advanced/#http-proxying)
- [NASA JPL Horizons API](https://ssd-api.jpl.nasa.gov/doc/horizons.html)
- [astroquery Documentation](https://astroquery.readthedocs.io/)

View File

@ -64,6 +64,22 @@ class Settings(BaseSettings):
upload_dir: str = "upload" upload_dir: str = "upload"
max_upload_size: int = 10485760 # 10MB max_upload_size: int = 10485760 # 10MB
# Proxy settings (for accessing NASA JPL Horizons API in China)
http_proxy: str = ""
https_proxy: str = ""
@property
def proxy_dict(self) -> dict[str, str] | None:
"""Get proxy configuration as a dictionary for httpx"""
if self.http_proxy or self.https_proxy:
proxies = {}
if self.http_proxy:
proxies["http://"] = self.http_proxy
if self.https_proxy:
proxies["https://"] = self.https_proxy
return proxies
return None
@property @property
def database_url(self) -> str: def database_url(self) -> str:
"""Construct database URL for SQLAlchemy""" """Construct database URL for SQLAlchemy"""

View File

@ -34,11 +34,19 @@ from app.services.cache_preheat import preheat_all_caches
from app.database import close_db from app.database import close_db
# Configure logging # Configure logging
# Set root logger to WARNING in production, INFO in development
log_level = logging.INFO if settings.jwt_secret_key == "your-secret-key-change-this-in-production" else logging.WARNING
logging.basicConfig( logging.basicConfig(
level=logging.INFO, level=log_level,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
) )
# Reduce noise from specific loggers in production
if log_level == logging.WARNING:
logging.getLogger("app.services.cache").setLevel(logging.ERROR)
logging.getLogger("app.services.redis_cache").setLevel(logging.ERROR)
logging.getLogger("app.api.celestial_position").setLevel(logging.WARNING)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -7,8 +7,10 @@ from astropy.time import Time
import logging import logging
import re import re
import httpx import httpx
import os
from app.models.celestial import Position, CelestialBody from app.models.celestial import Position, CelestialBody
from app.config import settings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -20,6 +22,15 @@ class HorizonsService:
"""Initialize the service""" """Initialize the service"""
self.location = "@sun" # Heliocentric coordinates self.location = "@sun" # Heliocentric coordinates
# Set proxy for astroquery if configured
# astroquery uses standard HTTP_PROXY and HTTPS_PROXY environment variables
if settings.http_proxy:
os.environ['HTTP_PROXY'] = settings.http_proxy
logger.info(f"Set HTTP_PROXY for astroquery: {settings.http_proxy}")
if settings.https_proxy:
os.environ['HTTPS_PROXY'] = settings.https_proxy
logger.info(f"Set HTTPS_PROXY for astroquery: {settings.https_proxy}")
async def get_object_data_raw(self, body_id: str) -> str: async def get_object_data_raw(self, body_id: str) -> str:
""" """
Get raw object data (terminal style text) from Horizons Get raw object data (terminal style text) from Horizons
@ -33,7 +44,7 @@ class HorizonsService:
url = "https://ssd.jpl.nasa.gov/api/horizons.api" url = "https://ssd.jpl.nasa.gov/api/horizons.api"
# Ensure ID is quoted for COMMAND # Ensure ID is quoted for COMMAND
cmd_val = f"'{body_id}'" if not body_id.startswith("'") else body_id cmd_val = f"'{body_id}'" if not body_id.startswith("'") else body_id
params = { params = {
"format": "text", "format": "text",
"COMMAND": cmd_val, "COMMAND": cmd_val,
@ -44,10 +55,15 @@ class HorizonsService:
} }
try: try:
async with httpx.AsyncClient() as client: # Configure proxy if available
client_kwargs = {"timeout": 5.0}
if settings.proxy_dict:
client_kwargs["proxies"] = settings.proxy_dict
logger.info(f"Using proxy for NASA API: {settings.proxy_dict}")
async with httpx.AsyncClient(**client_kwargs) as client:
logger.info(f"Fetching raw data for body {body_id}") logger.info(f"Fetching raw data for body {body_id}")
# Reduced timeout for China network (NASA JPL may be blocked) response = await client.get(url, params=params)
response = await client.get(url, params=params, timeout=5.0)
if response.status_code != 200: if response.status_code != 200:
raise Exception(f"NASA API returned status {response.status_code}") raise Exception(f"NASA API returned status {response.status_code}")