clash-linux commited on
Commit
cd9d8e5
·
verified ·
1 Parent(s): a0e1ff5

Upload 20 files

Browse files
Files changed (2) hide show
  1. app/main.py +53 -11
  2. entrypoint.sh +19 -10
app/main.py CHANGED
@@ -278,13 +278,21 @@ def clash_api_proxy(subpath):
278
  return jsonify({"message": "Authentication required"}), 401
279
 
280
  # 2. 检查Clash是否运行
281
- if clash_manager is None or clash_manager.clash_process is None or clash_manager.clash_process.poll() is not None:
282
  logger.error(f"Clash API不可用,无法代理请求: {subpath}")
283
  return jsonify({
284
  "success": False,
285
  "error": f"Clash API未运行: {initialization_error or '内部错误'}"
286
  }), 503
287
-
 
 
 
 
 
 
 
 
288
  # 3. 构建目标URL
289
  target_url = f"http://127.0.0.1:{CLASH_API_PORT}/{subpath}"
290
  if request.query_string:
@@ -294,25 +302,33 @@ def clash_api_proxy(subpath):
294
 
295
  # 4. 转发请求
296
  try:
297
- # 准备请求头,移除Host,保留Yacd可能发送的认证头
298
- req_headers = {key: value for key, value in request.headers if key.lower() != 'host'}
 
 
 
299
 
300
- # 对于WebSocket连接 (Clash API 日志/流量)
301
- if request.headers.get('Upgrade', '').lower() == 'websocket':
302
- # 这里需要一个更复杂的WebSocket代理实现,暂时返回错误
303
- logger.warning("不支持的WebSocket代理请求")
304
- return jsonify({"error": "WebSocket proxy not supported"}), 501
 
 
 
 
 
305
 
306
  # 普通HTTP请求
307
  resp = requests.request(
308
  method=request.method,
309
  url=target_url,
310
  headers=req_headers,
311
- data=request.get_data(),
312
  cookies=request.cookies,
313
  allow_redirects=False,
314
  stream=True, # 对于大响应或流式响应
315
- timeout=30 # 添加超时
316
  )
317
 
318
  # 5. 构建并返回响应
@@ -376,6 +392,14 @@ def index():
376
  border: 1px dashed #ccc;
377
  background-color: #f9f9f9;
378
  }}
 
 
 
 
 
 
 
 
379
  pre {{ max-height: 300px; overflow: auto; background-color: #eee; padding: 10px; border-radius: 4px; }}
380
  </style>
381
  <script>
@@ -433,6 +457,24 @@ def index():
433
  <h2>控制面板</h2>
434
  {yacd_link}
435
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
436
  <h2>基本操作</h2>
437
  <button class="button warning" onclick="requestWithApiKey('/api/refresh')">刷新订阅并重启Clash</button>
438
 
 
278
  return jsonify({"message": "Authentication required"}), 401
279
 
280
  # 2. 检查Clash是否运行
281
+ if clash_manager is None:
282
  logger.error(f"Clash API不可用,无法代理请求: {subpath}")
283
  return jsonify({
284
  "success": False,
285
  "error": f"Clash API未运行: {initialization_error or '内部错误'}"
286
  }), 503
287
+
288
+ # 特殊处理 /logs 路径 (这是WebSocket接口,我们不支持)
289
+ if subpath == 'logs':
290
+ logger.info("请求了日志WebSocket接口,但当前版本不支持WebSocket代理")
291
+ return jsonify({
292
+ "success": False,
293
+ "error": "不支持WebSocket日志接口。请使用标准HTTP API。"
294
+ }), 400
295
+
296
  # 3. 构建目标URL
297
  target_url = f"http://127.0.0.1:{CLASH_API_PORT}/{subpath}"
298
  if request.query_string:
 
302
 
303
  # 4. 转发请求
304
  try:
305
+ # 准备请求头,移除Host
306
+ req_headers = {
307
+ k: v for k, v in request.headers.items()
308
+ if k.lower() not in ['host', 'content-length']
309
+ }
310
 
311
+ # 确保Content-Type正确传递,尤其对PUT请求
312
+ if request.method in ['POST', 'PUT', 'PATCH'] and 'Content-Type' not in req_headers:
313
+ req_headers['Content-Type'] = 'application/json'
314
+
315
+ # 对于PUT请求,确保正确获取请求数据
316
+ if request.method == 'PUT':
317
+ logger.debug(f"处理PUT请求到 {target_url}, 数据: {request.data}")
318
+ req_data = request.get_data()
319
+ else:
320
+ req_data = request.get_data()
321
 
322
  # 普通HTTP请求
323
  resp = requests.request(
324
  method=request.method,
325
  url=target_url,
326
  headers=req_headers,
327
+ data=req_data,
328
  cookies=request.cookies,
329
  allow_redirects=False,
330
  stream=True, # 对于大响应或流式响应
331
+ timeout=10 # 缩短超时,防止工作进程卡住
332
  )
333
 
334
  # 5. 构建并返回响应
 
392
  border: 1px dashed #ccc;
393
  background-color: #f9f9f9;
394
  }}
395
+ .note {{
396
+ background-color: #fcf8e3;
397
+ border-left: 4px solid #f0ad4e;
398
+ padding: 10px 15px;
399
+ margin: 10px 0;
400
+ color: #8a6d3b;
401
+ font-size: 14px;
402
+ }}
403
  pre {{ max-height: 300px; overflow: auto; background-color: #eee; padding: 10px; border-radius: 4px; }}
404
  </style>
405
  <script>
 
457
  <h2>控制面板</h2>
458
  {yacd_link}
459
 
460
+ <div class="note">
461
+ <strong>⚠️ 重要提示:</strong>
462
+ <ul>
463
+ <li>首次使用高级控制面板时,您需要在设置中填入:
464
+ <ul>
465
+ <li>外部控制器地址:<code>/clashapi</code></li>
466
+ <li>密钥:<code>changeme</code> (除非您自定义了API_KEY)</li>
467
+ </ul>
468
+ </li>
469
+ <li>当前版本对控制面板有以下限制:
470
+ <ul>
471
+ <li>不支持WebSocket接口,因此日志查看功能不可用</li>
472
+ <li>某些PUT操作可能不稳定,如果遇到问题请刷新页面</li>
473
+ </ul>
474
+ </li>
475
+ </ul>
476
+ </div>
477
+
478
  <h2>基本操作</h2>
479
  <button class="button warning" onclick="requestWithApiKey('/api/refresh')">刷新订阅并重启Clash</button>
480
 
entrypoint.sh CHANGED
@@ -84,18 +84,27 @@ if [ -z "$API_KEY" ]; then
84
  fi
85
 
86
  # 启动Flask应用
87
- echo "${GREEN}Starting Flask application...${NC}"
88
 
89
- # 使用gunicorn启动Flask应用(生产环境推荐)
90
- # 如果WORKER_COUNT未设置,使用CPU核心数+1的worker数量
91
- WORKER_COUNT=${WORKER_COUNT:-$(( $(nproc) + 1 ))}
92
- echo "Using $WORKER_COUNT workers"
 
 
 
 
 
 
93
 
94
- # 确保所有参数在一行或使用清晰的续行符
95
- exec gunicorn \
96
- --workers $WORKER_COUNT \
97
- --bind 0.0.0.0:${FLASK_PORT:-7860} \
98
- --log-level info \
 
99
  --access-logfile - \
100
  --error-logfile - \
 
 
101
  app.main:app
 
84
  fi
85
 
86
  # 启动Flask应用
87
+ echo -e "${GREEN}Starting Flask application...${NC}"
88
 
89
+ if [ -n "$WORKERS" ]; then
90
+ echo "Using $WORKERS workers"
91
+ DEFAULT_WORKERS="$WORKERS"
92
+ else
93
+ # 计算合适的worker数量 (默认根据CPU数量,但最少2个,最多17个)
94
+ DEFAULT_WORKERS=$(($(nproc) * 2 + 1))
95
+ [ $DEFAULT_WORKERS -lt 2 ] && DEFAULT_WORKERS=2
96
+ [ $DEFAULT_WORKERS -gt 17 ] && DEFAULT_WORKERS=17
97
+ echo "Using $DEFAULT_WORKERS workers"
98
+ fi
99
 
100
+ # 增加超时时间,确保长连接能正常工作
101
+ # 增加记录器配置使错误更容易诊断
102
+ cd /app && gunicorn \
103
+ --bind 0.0.0.0:${FLASK_PORT} \
104
+ --workers ${DEFAULT_WORKERS} \
105
+ --timeout 300 \
106
  --access-logfile - \
107
  --error-logfile - \
108
+ --capture-output \
109
+ --log-level info \
110
  app.main:app