clash / app.py
clash-linux's picture
Upload 17 files
03d4200 verified
"""
Hugging Face Spaces 启动入口
"""
import os
import sys
import subprocess
import time
import signal
import atexit
def setup_environment():
"""设置必要的环境变量和目录"""
# 确保数据目录存在
try:
os.makedirs("data", exist_ok=True)
print("✅ 数据目录已创建")
except Exception as e:
print(f"⚠️ 创建数据目录失败: {str(e)}")
# 打印环境诊断信息
print("🔍 系统信息:")
print(f" - Python: {sys.version}")
print(f" - 工作目录: {os.getcwd()}")
print(f" - 用户: {os.getuid() if hasattr(os, 'getuid') else 'N/A'}")
# 检查二进制文件
for binary in ["subconverter/subconverter", "clash_core/clash.meta-linux-amd64"]:
if os.path.exists(binary):
try:
# 文件信息
print(f"✅ 发现二进制文件: {binary}")
print(f" - 大小: {os.path.getsize(binary)} 字节")
print(f" - 权限: {oct(os.stat(binary).st_mode)[-3:]}")
print(f" - 可执行: {os.access(binary, os.X_OK)}")
# 尝试获取文件类型
try:
file_type = subprocess.check_output(["file", binary], universal_newlines=True)
print(f" - 类型: {file_type.strip()}")
except Exception as e:
print(f" - 获取文件类型失败: {str(e)}")
# 设置执行权限
os.chmod(binary, 0o755)
print(f"✅ {binary}权限已设置")
except Exception as e:
print(f"⚠️ 检查{binary}时出错: {str(e)}")
else:
print(f"❌ 找不到二进制文件: {binary}")
# 尝试执行 Clash 来获取版本信息
clash_path = "clash_core/clash.meta-linux-amd64"
if os.path.exists(clash_path):
try:
version_output = subprocess.check_output([clash_path, "-v"], stderr=subprocess.STDOUT, universal_newlines=True, timeout=2)
print(f"✅ Clash版本信息: {version_output.strip()}")
except Exception as e:
print(f"⚠️ 获取Clash版本信息失败: {str(e)}")
# 设置环境变量
os.environ["FLASK_PORT"] = "7860" # HF Spaces 要求的端口
# 如果没有设置 SUB_URL 环境变量,尝试从 .env 加载
if not os.environ.get("SUB_URL") and os.path.exists(".env"):
try:
with open(".env", "r") as f:
for line in f:
line = line.strip()
if line and not line.startswith("#"):
key, value = line.split("=", 1)
os.environ[key] = value
print("✅ 从.env加载了环境变量")
except Exception as e:
print(f"⚠️ 加载.env失败: {str(e)}")
def start_app():
"""启动应用程序"""
print("🚀 正在启动 Simple Clash Relay...")
# 使用 gunicorn 启动 Flask 应用
app_process = subprocess.Popen(
["gunicorn", "--bind", "0.0.0.0:7860", "--workers", "2", "--threads", "4", "--timeout", "120", "app.main:app"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True
)
# 注册应用程序退出时的清理函数
def cleanup():
print("🛑 正在停止应用...")
app_process.terminate()
try:
app_process.wait(timeout=5)
except subprocess.TimeoutExpired:
app_process.kill()
atexit.register(cleanup)
# 信号处理
def signal_handler(sig, frame):
print(f"📢 接收到信号 {sig},正在退出...")
cleanup()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
# 输出应用程序日志
while True:
output = app_process.stdout.readline()
if output:
print(output, end="")
# 如果进程已经退出,退出循环
if app_process.poll() is not None:
break
# 获取剩余输出
remaining_output, _ = app_process.communicate()
if remaining_output:
print(remaining_output, end="")
return app_process.returncode
if __name__ == "__main__":
setup_environment()
exit_code = start_app()
sys.exit(exit_code)