Commit bb6420d3b834971dd2ee63bddab769933014ddd4

Authored by tangwang
1 parent 7fbca0d7

前端同源代理后端,避免写死6002和外部认证冲突

- 前端 JS 不再写死后端地址:默认 API_BASE_URL 为空串,所有搜索与 suggest 请求改为同源路径 (/search/*),仅在显式注入 window.API_BASE_URL 时才覆盖,避免 .env 中旧的 http://43.166.252.75:6002 等配置污染浏览器请求。
- 在 scripts/frontend_server.py 上实现轻量级反向代理:拦截 /search/、/admin/、/indexer/ 的 GET/POST/OPTIONS 请求,服务端将请求转发到本机 6002 (BACKEND_PROXY_URL,默认 http://127.0.0.1:6002),并把响应原样返回前端。
- 通过“浏览器 → web服务器:6003(认证) → GPU:6003(本项目前端) → GPU 本机:6002(后端)”这条链路,彻底绕开 web 服务器 6002 上单独的 Basic Auth,解决了外网访问时前端能打开但搜索请求被 web:6002 拦截的问题。
- frontend_server 默认不再注入 window.API_BASE_URL,只有在设置 FRONTEND_INJECT_API_BASE_URL=1 且 API_BASE_URL 有值时才向 HTML 注入脚本,确保默认行为始终是同源调用,由 6003 统一代理后端。
- 更新 frontend/index.html 中的静态 JS 版本号(tenant_facets_config.js 和 app.js),强制浏览器拉取最新脚本,避免旧版前端继续使用硬编码的后端地址。

Made-with: Cursor
Showing 2 changed files with 40 additions and 5 deletions   Show diff stats
frontend/index.html
... ... @@ -197,8 +197,8 @@
197 197 <p>saas-search © 2025 | API: <span id="apiUrl">Loading...</span></p>
198 198 </footer>
199 199  
200   - <script src="/static/js/tenant_facets_config.js?v=1.3"></script>
201   - <script src="/static/js/app.js?v=3.6"></script>
  200 + <script src="/static/js/tenant_facets_config.js?v=1.4"></script>
  201 + <script src="/static/js/app.js?v=1.0"></script>
202 202 <script>
203 203 // 自动补全功能(使用后端 /search/suggestions 接口)
204 204 const SUGGEST_API = API_BASE_URL + '/search/suggestions';
... ...
scripts/frontend_server.py
... ... @@ -19,6 +19,10 @@ from dotenv import load_dotenv
19 19 project_root = Path(__file__).parent.parent
20 20 load_dotenv(project_root / '.env')
21 21  
  22 +# Get API_BASE_URL from environment(默认不注入,避免被旧 .env 覆盖同源策略)
  23 +# 仅当显式设置 FRONTEND_INJECT_API_BASE_URL=1 时才注入 window.API_BASE_URL。
  24 +API_BASE_URL = os.getenv('API_BASE_URL') or None
  25 +INJECT_API_BASE_URL = os.getenv('FRONTEND_INJECT_API_BASE_URL', '0') == '1'
22 26 # Backend proxy target for same-origin API forwarding
23 27 BACKEND_PROXY_URL = os.getenv('BACKEND_PROXY_URL', 'http://127.0.0.1:6002').rstrip('/')
24 28  
... ... @@ -27,7 +31,7 @@ frontend_dir = os.path.join(os.path.dirname(__file__), &#39;../frontend&#39;)
27 31 os.chdir(frontend_dir)
28 32  
29 33 # Get port from environment variable or default
30   -PORT = int(os.getenv('FRONTEND_PORT', 6003))
  34 +PORT = int(os.getenv('PORT', 6003))
31 35  
32 36 # Configure logging to suppress scanner noise
33 37 logging.basicConfig(level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s')
... ... @@ -116,17 +120,48 @@ class MyHTTPRequestHandler(http.server.SimpleHTTPRequestHandler, RateLimitingMix
116 120 self.wfile.write(b'{"error":"Bad Gateway: backend proxy failed"}')
117 121  
118 122 def do_GET(self):
119   - """Handle GET requests with lightweight API proxy."""
  123 + """Handle GET requests with API config injection."""
120 124 path = self.path.split('?')[0]
121 125  
122 126 # Proxy API paths to backend first
123 127 if self._is_proxy_path(path):
124 128 self._proxy_to_backend()
125 129 return
  130 +
126 131 # Route / to index.html
127 132 if path == '/' or path == '':
128 133 self.path = '/index.html' + (self.path.split('?', 1)[1] if '?' in self.path else '')
129   - super().do_GET()
  134 +
  135 + # Inject API config for HTML files
  136 + if self.path.endswith('.html'):
  137 + self._serve_html_with_config()
  138 + else:
  139 + super().do_GET()
  140 +
  141 + def _serve_html_with_config(self):
  142 + """Serve HTML with optional API_BASE_URL injected."""
  143 + try:
  144 + file_path = self.path.lstrip('/')
  145 + if not os.path.exists(file_path):
  146 + self.send_error(404)
  147 + return
  148 +
  149 + with open(file_path, 'r', encoding='utf-8') as f:
  150 + html = f.read()
  151 +
  152 + # 默认不注入 API_BASE_URL,避免历史 .env(如 http://xx:6002)覆盖同源调用。
  153 + # 仅当 FRONTEND_INJECT_API_BASE_URL=1 且 API_BASE_URL 有值时才注入。
  154 + if INJECT_API_BASE_URL and API_BASE_URL:
  155 + config_script = f'<script>window.API_BASE_URL="{API_BASE_URL}";</script>\n '
  156 + html = html.replace('<script src="/static/js/app.js', config_script + '<script src="/static/js/app.js', 1)
  157 +
  158 + self.send_response(200)
  159 + self.send_header('Content-Type', 'text/html; charset=utf-8')
  160 + self.end_headers()
  161 + self.wfile.write(html.encode('utf-8'))
  162 + except Exception as e:
  163 + logging.error(f"Error serving HTML: {e}")
  164 + self.send_error(500)
130 165  
131 166 def do_POST(self):
132 167 """Handle POST requests. Proxy API requests to backend."""
... ...