Spaces:
Paused
Paused
Upload 18 files
Browse files- app/main.py +46 -0
- app/sub_manager.py +103 -73
app/main.py
CHANGED
|
@@ -244,6 +244,23 @@ def debug_show_config():
|
|
| 244 |
logger.error(f"读取配置文件时出错: {str(e)}")
|
| 245 |
return jsonify({"success": False, "error": str(e)}), 500
|
| 246 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
# --- Yacd UI & Clash API 反向代理路由 ---
|
| 248 |
|
| 249 |
@app.route('/ui/')
|
|
@@ -422,6 +439,34 @@ def index():
|
|
| 422 |
configDiv.innerHTML = '<p style="color:red">请求失败: ' + error + '</p>';
|
| 423 |
}});
|
| 424 |
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 425 |
</script>
|
| 426 |
</head>
|
| 427 |
<body>
|
|
@@ -439,6 +484,7 @@ def index():
|
|
| 439 |
<div class="debug-section">
|
| 440 |
<h3>调试选项</h3>
|
| 441 |
<button class="button" onclick="viewConfig()">查看当前配置文件</button>
|
|
|
|
| 442 |
<button class="button danger" onclick="if(confirm('确定要清理配置并重启服务吗?此操作不可逆!')) requestWithApiKey('/debug/clean')">清理配置并重启</button>
|
| 443 |
<div id="config-content" style="margin-top: 15px;"></div>
|
| 444 |
</div>
|
|
|
|
| 244 |
logger.error(f"读取配置文件时出错: {str(e)}")
|
| 245 |
return jsonify({"success": False, "error": str(e)}), 500
|
| 246 |
|
| 247 |
+
@app.route("/debug/raw-config", methods=["GET"])
|
| 248 |
+
@authenticate
|
| 249 |
+
def debug_show_raw_config():
|
| 250 |
+
"""获取并显示原始下载的订阅内容"""
|
| 251 |
+
config_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data", "config.yaml.raw")
|
| 252 |
+
|
| 253 |
+
if not os.path.exists(config_path):
|
| 254 |
+
return jsonify({"success": False, "error": "原始配置文件不存在"}), 404
|
| 255 |
+
|
| 256 |
+
try:
|
| 257 |
+
with open(config_path, "r", encoding="utf-8") as f:
|
| 258 |
+
content = f.read()
|
| 259 |
+
return jsonify({"success": True, "content": content})
|
| 260 |
+
except Exception as e:
|
| 261 |
+
logger.error(f"读取原始配置文件时出错: {str(e)}")
|
| 262 |
+
return jsonify({"success": False, "error": str(e)}), 500
|
| 263 |
+
|
| 264 |
# --- Yacd UI & Clash API 反向代理路由 ---
|
| 265 |
|
| 266 |
@app.route('/ui/')
|
|
|
|
| 439 |
configDiv.innerHTML = '<p style="color:red">请求失败: ' + error + '</p>';
|
| 440 |
}});
|
| 441 |
}}
|
| 442 |
+
|
| 443 |
+
function viewRawConfig() {{
|
| 444 |
+
const apiKey = prompt('请输入API密钥 (默认为 changeme)', 'changeme');
|
| 445 |
+
if (apiKey === null) return;
|
| 446 |
+
|
| 447 |
+
fetch('/debug/raw-config', {{
|
| 448 |
+
headers: {{ 'X-API-Key': apiKey }}
|
| 449 |
+
}})
|
| 450 |
+
.then(response => response.json())
|
| 451 |
+
.then(data => {{
|
| 452 |
+
const configDiv = document.getElementById('config-content');
|
| 453 |
+
configDiv.innerHTML = ''; // 清空之前的内容
|
| 454 |
+
if (data.success) {{
|
| 455 |
+
const pre = document.createElement('pre');
|
| 456 |
+
pre.textContent = data.content;
|
| 457 |
+
configDiv.appendChild(pre);
|
| 458 |
+
}} else {{
|
| 459 |
+
const errorP = document.createElement('p');
|
| 460 |
+
errorP.style.color = 'red';
|
| 461 |
+
errorP.textContent = '获取原始配置失败: ' + data.error;
|
| 462 |
+
configDiv.appendChild(errorP);
|
| 463 |
+
}}
|
| 464 |
+
}})
|
| 465 |
+
.catch(error => {{
|
| 466 |
+
const configDiv = document.getElementById('config-content');
|
| 467 |
+
configDiv.innerHTML = '<p style="color:red">请求失败: ' + error + '</p>';
|
| 468 |
+
}});
|
| 469 |
+
}}
|
| 470 |
</script>
|
| 471 |
</head>
|
| 472 |
<body>
|
|
|
|
| 484 |
<div class="debug-section">
|
| 485 |
<h3>调试选项</h3>
|
| 486 |
<button class="button" onclick="viewConfig()">查看当前配置文件</button>
|
| 487 |
+
<button class="button" onclick="viewRawConfig()">查看原始订阅内容</button>
|
| 488 |
<button class="button danger" onclick="if(confirm('确定要清理配置并重启服务吗?此操作不可逆!')) requestWithApiKey('/debug/clean')">清理配置并重启</button>
|
| 489 |
<div id="config-content" style="margin-top: 15px;"></div>
|
| 490 |
</div>
|
app/sub_manager.py
CHANGED
|
@@ -303,7 +303,10 @@ secret: ""
|
|
| 303 |
config_content = "secret: ''\n" + config_content
|
| 304 |
has_patch = True
|
| 305 |
|
| 306 |
-
#
|
|
|
|
|
|
|
|
|
|
| 307 |
try:
|
| 308 |
import yaml
|
| 309 |
logger.info("正在使用PyYAML解析和修复配置")
|
|
@@ -313,95 +316,128 @@ secret: ""
|
|
| 313 |
|
| 314 |
# 检查是否有代理和代理组
|
| 315 |
if config_yaml and isinstance(config_yaml, dict):
|
| 316 |
-
|
| 317 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
proxy_names = set()
|
| 319 |
-
for proxy in
|
| 320 |
if isinstance(proxy, dict) and "name" in proxy:
|
| 321 |
proxy_names.add(proxy["name"])
|
|
|
|
| 322 |
|
| 323 |
# 添加内置代理
|
| 324 |
proxy_names.add("DIRECT")
|
| 325 |
proxy_names.add("REJECT")
|
| 326 |
|
| 327 |
-
#
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 338 |
groups_fixed = True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 339 |
|
| 340 |
-
#
|
| 341 |
-
if
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
|
| 346 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 347 |
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
has_global = True
|
| 353 |
-
break
|
| 354 |
|
| 355 |
-
|
|
|
|
| 356 |
# 添加GLOBAL策略组
|
| 357 |
logger.info("添加GLOBAL策略组")
|
| 358 |
-
if "proxy-groups" not in config_yaml:
|
| 359 |
-
config_yaml["proxy-groups"] = []
|
| 360 |
|
| 361 |
# 确定要放入GLOBAL组的代理
|
| 362 |
global_proxies = ["DIRECT"]
|
| 363 |
-
#
|
| 364 |
-
for
|
| 365 |
-
if
|
| 366 |
-
proxy_name
|
| 367 |
-
if proxy_name != "DIRECT" and proxy_name != "REJECT":
|
| 368 |
-
global_proxies.append(proxy_name)
|
| 369 |
|
| 370 |
-
|
| 371 |
-
"name": "GLOBAL",
|
| 372 |
-
"type": "select",
|
| 373 |
-
"proxies": global_proxies
|
| 374 |
-
})
|
| 375 |
-
groups_fixed = True
|
| 376 |
-
has_patch = True
|
| 377 |
-
|
| 378 |
-
# 如果修复了代理组,重新生成配置
|
| 379 |
-
if groups_fixed:
|
| 380 |
-
config_content = yaml.dump(config_yaml, sort_keys=False, allow_unicode=True)
|
| 381 |
-
has_patch = True
|
| 382 |
-
else:
|
| 383 |
-
# 配置缺少代理或代理组,添加基本结构
|
| 384 |
-
if "proxies" not in config_yaml:
|
| 385 |
-
config_yaml["proxies"] = [{"name": "DIRECT", "type": "direct"}]
|
| 386 |
-
has_patch = True
|
| 387 |
-
|
| 388 |
-
if "proxy-groups" not in config_yaml:
|
| 389 |
config_yaml["proxy-groups"] = [{
|
| 390 |
"name": "GLOBAL",
|
| 391 |
"type": "select",
|
| 392 |
-
"proxies":
|
| 393 |
}]
|
|
|
|
| 394 |
has_patch = True
|
| 395 |
-
|
| 396 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 397 |
config_content = yaml.dump(config_yaml, sort_keys=False, allow_unicode=True)
|
|
|
|
| 398 |
else:
|
| 399 |
-
#
|
| 400 |
-
logger.warning("
|
| 401 |
-
# 使用简单的文本分析,避免添加重复的配置
|
| 402 |
-
has_proxy_groups = "proxy-groups:" in config_content
|
| 403 |
-
has_global_group = "GLOBAL" in config_content or "- name: GLOBAL" in config_content
|
| 404 |
-
|
| 405 |
if not has_proxy_groups and not has_global_group:
|
| 406 |
logger.info("未检测到proxy-groups或GLOBAL策略组,添加基本的GLOBAL策略组")
|
| 407 |
config_content += """
|
|
@@ -414,13 +450,10 @@ proxy-groups:
|
|
| 414 |
has_patch = True
|
| 415 |
else:
|
| 416 |
logger.info("检测到已有proxy-groups或GLOBAL配置,跳过添加")
|
| 417 |
-
|
| 418 |
except ImportError as e:
|
| 419 |
logger.error(f"导入PyYAML模块失败: {str(e)}")
|
| 420 |
-
#
|
| 421 |
-
has_proxy_groups = "proxy-groups:" in config_content
|
| 422 |
-
has_global_group = "GLOBAL" in config_content or "- name: GLOBAL" in config_content
|
| 423 |
-
|
| 424 |
if not has_proxy_groups and not has_global_group:
|
| 425 |
logger.info("未检测到proxy-groups或GLOBAL策略组,添加基本的GLOBAL策略组")
|
| 426 |
config_content += """
|
|
@@ -435,10 +468,7 @@ proxy-groups:
|
|
| 435 |
logger.info("检测到已有proxy-groups或GLOBAL配置,跳过添加")
|
| 436 |
except Exception as yaml_err:
|
| 437 |
logger.error(f"处理YAML配置时出错: {str(yaml_err)}")
|
| 438 |
-
#
|
| 439 |
-
has_proxy_groups = "proxy-groups:" in config_content
|
| 440 |
-
has_global_group = "GLOBAL" in config_content or "- name: GLOBAL" in config_content
|
| 441 |
-
|
| 442 |
if not has_proxy_groups and not has_global_group:
|
| 443 |
logger.info("未检测到proxy-groups或GLOBAL策略组,添加基本的GLOBAL策略组")
|
| 444 |
config_content += """
|
|
|
|
| 303 |
config_content = "secret: ''\n" + config_content
|
| 304 |
has_patch = True
|
| 305 |
|
| 306 |
+
# 确保配置了全局策略组 - 使用简单的文本检查
|
| 307 |
+
has_proxy_groups = "proxy-groups:" in config_content
|
| 308 |
+
has_global_group = "GLOBAL" in config_content or "- name: GLOBAL" in config_content
|
| 309 |
+
|
| 310 |
try:
|
| 311 |
import yaml
|
| 312 |
logger.info("正在使用PyYAML解析和修复配置")
|
|
|
|
| 316 |
|
| 317 |
# 检查是否有代理和代理组
|
| 318 |
if config_yaml and isinstance(config_yaml, dict):
|
| 319 |
+
# 确保proxies存在并且是一个有效的列表
|
| 320 |
+
if "proxies" not in config_yaml or not isinstance(config_yaml["proxies"], list):
|
| 321 |
+
logger.warning("配置中未找到有效的proxies列表,尝试保留原始内容")
|
| 322 |
+
|
| 323 |
+
# 即使没有proxy-groups,也先保留所有已有的proxies
|
| 324 |
+
if "proxies" in config_yaml and isinstance(config_yaml["proxies"], list):
|
| 325 |
+
proxies_list = config_yaml["proxies"]
|
| 326 |
+
|
| 327 |
+
# 收集所有代理节点名称
|
| 328 |
proxy_names = set()
|
| 329 |
+
for proxy in proxies_list:
|
| 330 |
if isinstance(proxy, dict) and "name" in proxy:
|
| 331 |
proxy_names.add(proxy["name"])
|
| 332 |
+
logger.info(f"找到 {len(proxy_names)} 个代理节点")
|
| 333 |
|
| 334 |
# 添加内置代理
|
| 335 |
proxy_names.add("DIRECT")
|
| 336 |
proxy_names.add("REJECT")
|
| 337 |
|
| 338 |
+
# 检查并固定proxy-groups
|
| 339 |
+
if "proxy-groups" in config_yaml and isinstance(config_yaml["proxy-groups"], list):
|
| 340 |
+
groups_fixed = False
|
| 341 |
+
for group in config_yaml["proxy-groups"]:
|
| 342 |
+
if isinstance(group, dict) and "proxies" in group and "name" in group:
|
| 343 |
+
group_name = group["name"]
|
| 344 |
+
# 检查group["proxies"]中哪些是无效引用
|
| 345 |
+
invalid_proxies = []
|
| 346 |
+
valid_proxies = []
|
| 347 |
+
|
| 348 |
+
for proxy in group["proxies"]:
|
| 349 |
+
if proxy in proxy_names or proxy == group_name:
|
| 350 |
+
valid_proxies.append(proxy)
|
| 351 |
+
else:
|
| 352 |
+
logger.warning(f"删除代理组 {group_name} 中的无效引用: {proxy}")
|
| 353 |
+
invalid_proxies.append(proxy)
|
| 354 |
+
groups_fixed = True
|
| 355 |
+
|
| 356 |
+
# 确保代理组至少有一个有效代理
|
| 357 |
+
if not valid_proxies:
|
| 358 |
+
valid_proxies.append("DIRECT")
|
| 359 |
+
logger.warning(f"为空代理组 {group_name} 添加默认代理: DIRECT")
|
| 360 |
groups_fixed = True
|
| 361 |
+
|
| 362 |
+
group["proxies"] = valid_proxies
|
| 363 |
+
|
| 364 |
+
# 检查是否有GLOBAL组,如果没有,添加一个
|
| 365 |
+
has_global = False
|
| 366 |
+
for group in config_yaml["proxy-groups"]:
|
| 367 |
+
if isinstance(group, dict) and group.get("name") == "GLOBAL":
|
| 368 |
+
has_global = True
|
| 369 |
+
break
|
| 370 |
+
|
| 371 |
+
if not has_global:
|
| 372 |
+
# 添加GLOBAL策略组
|
| 373 |
+
logger.info("添加GLOBAL策略组")
|
| 374 |
+
|
| 375 |
+
# 确定要放入GLOBAL组的代理
|
| 376 |
+
global_proxies = ["DIRECT"]
|
| 377 |
+
# 从代理节点和其他策略组中选择要添加到GLOBAL的项
|
| 378 |
+
# 优先添加策略组
|
| 379 |
+
for group in config_yaml["proxy-groups"]:
|
| 380 |
+
if isinstance(group, dict) and "name" in group:
|
| 381 |
+
group_name = group["name"]
|
| 382 |
+
if group_name != "GLOBAL":
|
| 383 |
+
global_proxies.append(group_name)
|
| 384 |
|
| 385 |
+
# 如果没有其他策略组,添加所有代理节点
|
| 386 |
+
if len(global_proxies) <= 1: # 只有DIRECT
|
| 387 |
+
for proxy_name in proxy_names:
|
| 388 |
+
if proxy_name != "DIRECT" and proxy_name != "REJECT":
|
| 389 |
+
global_proxies.append(proxy_name)
|
| 390 |
|
| 391 |
+
# 添加GLOBAL组到配置
|
| 392 |
+
config_yaml["proxy-groups"].insert(0, {
|
| 393 |
+
"name": "GLOBAL",
|
| 394 |
+
"type": "select",
|
| 395 |
+
"proxies": global_proxies
|
| 396 |
+
})
|
| 397 |
+
groups_fixed = True
|
| 398 |
+
has_patch = True
|
| 399 |
|
| 400 |
+
# 如果修复了代理组,重新生成配置
|
| 401 |
+
if groups_fixed:
|
| 402 |
+
config_content = yaml.dump(config_yaml, sort_keys=False, allow_unicode=True)
|
| 403 |
+
has_patch = True
|
|
|
|
|
|
|
| 404 |
|
| 405 |
+
# 如果没有proxy-groups,添加基本的proxy-groups
|
| 406 |
+
elif not "proxy-groups" in config_yaml:
|
| 407 |
# 添加GLOBAL策略组
|
| 408 |
logger.info("添加GLOBAL策略组")
|
|
|
|
|
|
|
| 409 |
|
| 410 |
# 确定要放入GLOBAL组的代理
|
| 411 |
global_proxies = ["DIRECT"]
|
| 412 |
+
# 如果有代理节点,添加所有代理节点
|
| 413 |
+
for proxy_name in proxy_names:
|
| 414 |
+
if proxy_name != "DIRECT" and proxy_name != "REJECT":
|
| 415 |
+
global_proxies.append(proxy_name)
|
|
|
|
|
|
|
| 416 |
|
| 417 |
+
# 添加proxy-groups到配置
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 418 |
config_yaml["proxy-groups"] = [{
|
| 419 |
"name": "GLOBAL",
|
| 420 |
"type": "select",
|
| 421 |
+
"proxies": global_proxies
|
| 422 |
}]
|
| 423 |
+
config_content = yaml.dump(config_yaml, sort_keys=False, allow_unicode=True)
|
| 424 |
has_patch = True
|
| 425 |
+
|
| 426 |
+
# 如果既没有proxies也没有proxy-groups,添加基本配置
|
| 427 |
+
elif "proxies" not in config_yaml and "proxy-groups" not in config_yaml:
|
| 428 |
+
# 可能不是Clash配置或非常基础的配置
|
| 429 |
+
logger.warning("配置中未找到proxies和proxy-groups,添加基本配置")
|
| 430 |
+
config_yaml["proxies"] = [{"name": "DIRECT", "type": "direct"}]
|
| 431 |
+
config_yaml["proxy-groups"] = [{
|
| 432 |
+
"name": "GLOBAL",
|
| 433 |
+
"type": "select",
|
| 434 |
+
"proxies": ["DIRECT"]
|
| 435 |
+
}]
|
| 436 |
config_content = yaml.dump(config_yaml, sort_keys=False, allow_unicode=True)
|
| 437 |
+
has_patch = True
|
| 438 |
else:
|
| 439 |
+
# 无法解析为有效的YAML,尝试简单文本修复
|
| 440 |
+
logger.warning("配置无法解析为有效的YAML,使用简单文本修复")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 441 |
if not has_proxy_groups and not has_global_group:
|
| 442 |
logger.info("未检测到proxy-groups或GLOBAL策略组,添加基本的GLOBAL策略组")
|
| 443 |
config_content += """
|
|
|
|
| 450 |
has_patch = True
|
| 451 |
else:
|
| 452 |
logger.info("检测到已有proxy-groups或GLOBAL配置,跳过添加")
|
| 453 |
+
|
| 454 |
except ImportError as e:
|
| 455 |
logger.error(f"导入PyYAML模块失败: {str(e)}")
|
| 456 |
+
# 回退到简单的文本检查
|
|
|
|
|
|
|
|
|
|
| 457 |
if not has_proxy_groups and not has_global_group:
|
| 458 |
logger.info("未检测到proxy-groups或GLOBAL策略组,添加基本的GLOBAL策略组")
|
| 459 |
config_content += """
|
|
|
|
| 468 |
logger.info("检测到已有proxy-groups或GLOBAL配置,跳过添加")
|
| 469 |
except Exception as yaml_err:
|
| 470 |
logger.error(f"处理YAML配置时出错: {str(yaml_err)}")
|
| 471 |
+
# 回退到简单的文本检查
|
|
|
|
|
|
|
|
|
|
| 472 |
if not has_proxy_groups and not has_global_group:
|
| 473 |
logger.info("未检测到proxy-groups或GLOBAL策略组,添加基本的GLOBAL策略组")
|
| 474 |
config_content += """
|