2026-04-07 00:42:23 +08:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
"""
|
|
|
|
|
|
CountBot 应用启动脚本
|
|
|
|
|
|
开发模式启动,支持热重载
|
|
|
|
|
|
支持本地网络 IP 监控,类似 Vue3 启动模式
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
import sys
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
from backend.utils.network import get_local_ips
|
|
|
|
|
|
from backend.utils.runtime_env import (
|
|
|
|
|
|
apply_bind_env,
|
|
|
|
|
|
is_public_bind_host,
|
|
|
|
|
|
resolve_bind_address,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# Windows 平台强制 UTF-8 编码,避免 GBK 编码错误
|
|
|
|
|
|
if sys.platform == "win32":
|
2026-02-23 14:27:47 +08:00
|
|
|
|
# Python 3.7+ 支持
|
|
|
|
|
|
os.environ["PYTHONIOENCODING"] = "utf-8"
|
|
|
|
|
|
# 设置控制台代码页为 UTF-8
|
|
|
|
|
|
try:
|
|
|
|
|
|
import ctypes
|
|
|
|
|
|
kernel32 = ctypes.windll.kernel32
|
|
|
|
|
|
kernel32.SetConsoleCP(65001)
|
|
|
|
|
|
kernel32.SetConsoleOutputCP(65001)
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
2026-02-21 09:48:01 +08:00
|
|
|
|
# 添加项目根目录到 Python 路径
|
|
|
|
|
|
project_root = Path(__file__).parent
|
|
|
|
|
|
sys.path.insert(0, str(project_root))
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-07 00:42:23 +08:00
|
|
|
|
def main():
|
2026-03-09 09:03:43 +08:00
|
|
|
|
"""
|
|
|
|
|
|
启动应用(开发模式)
|
|
|
|
|
|
|
2026-04-07 00:42:23 +08:00
|
|
|
|
功能:
|
|
|
|
|
|
- 启动 FastAPI 应用服务器(开发模式)
|
|
|
|
|
|
- 启用热重载,文件更改自动重启
|
|
|
|
|
|
- 显示本地和网络访问地址
|
|
|
|
|
|
- 监控 backend 目录的文件变化
|
|
|
|
|
|
"""
|
2026-03-23 23:01:59 +08:00
|
|
|
|
import uvicorn
|
|
|
|
|
|
from loguru import logger
|
|
|
|
|
|
|
|
|
|
|
|
from backend.utils.logger import build_uvicorn_log_config
|
2026-02-21 09:48:01 +08:00
|
|
|
|
|
2026-03-09 09:03:43 +08:00
|
|
|
|
# 读取配置
|
2026-04-07 00:42:23 +08:00
|
|
|
|
host, port = resolve_bind_address()
|
|
|
|
|
|
apply_bind_env(host, port)
|
2026-02-21 09:48:01 +08:00
|
|
|
|
|
2026-02-23 05:49:26 +00:00
|
|
|
|
# 获取本地 IP 地址
|
|
|
|
|
|
local_ips = get_local_ips()
|
|
|
|
|
|
|
|
|
|
|
|
# 打印启动信息
|
2026-02-21 09:48:01 +08:00
|
|
|
|
logger.info("=" * 60)
|
|
|
|
|
|
logger.info("CountBot 开发模式启动中...")
|
|
|
|
|
|
logger.info("=" * 60)
|
|
|
|
|
|
|
2026-02-23 05:49:26 +00:00
|
|
|
|
try:
|
|
|
|
|
|
# 启动服务器前显示"服务器启动完成"消息和访问地址
|
|
|
|
|
|
logger.info("服务器启动完成!")
|
|
|
|
|
|
logger.info("=" * 60)
|
|
|
|
|
|
|
|
|
|
|
|
# 显示本地访问地址
|
|
|
|
|
|
logger.info(f"Local: http://localhost:{port}")
|
|
|
|
|
|
|
|
|
|
|
|
# 显示网络访问地址(如果监听了所有接口或用户明确设置了 0.0.0.0)
|
2026-04-07 00:42:23 +08:00
|
|
|
|
if is_public_bind_host(host):
|
|
|
|
|
|
if local_ips:
|
|
|
|
|
|
for ip in local_ips:
|
|
|
|
|
|
logger.info(f"Network: http://{ip}:{port}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
logger.info("Network: (无法检测到本地 IP 地址)")
|
|
|
|
|
|
logger.info(f"提示: 请检查网络连接或手动访问 http://<your-ip>:{port}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
# 即使监听 127.0.0.1,也显示可用的网络 IP 供参考
|
|
|
|
|
|
if local_ips:
|
|
|
|
|
|
logger.info(f"Network: http://{host}:{port}")
|
|
|
|
|
|
logger.info("提示: 如需从其他设备访问,请设置 COUNTBOT_HOST=0.0.0.0")
|
|
|
|
|
|
logger.info(f"可用网络 IP: {', '.join(local_ips)}")
|
|
|
|
|
|
else:
|
|
|
|
|
|
logger.info(f"Network: http://{host}:{port}")
|
|
|
|
|
|
logger.info("提示: 如需从其他设备访问,请设置 COUNTBOT_HOST=0.0.0.0")
|
2026-02-23 05:49:26 +00:00
|
|
|
|
|
|
|
|
|
|
logger.info("-" * 60)
|
|
|
|
|
|
logger.info("热重载已启用 - 文件更改将自动重启")
|
|
|
|
|
|
logger.info("按下 Ctrl+C 停止服务器")
|
|
|
|
|
|
logger.info("=" * 60)
|
|
|
|
|
|
|
2026-04-07 00:42:23 +08:00
|
|
|
|
# 在 Windows 开发模式下,uvicorn 的 reload 会切到 SelectorEventLoop,
|
|
|
|
|
|
# 这会让 asyncio.create_subprocess_shell 无法使用。
|
|
|
|
|
|
run_kwargs = dict(
|
|
|
|
|
|
app="backend.app:app",
|
2026-03-23 23:01:59 +08:00
|
|
|
|
host=host,
|
|
|
|
|
|
port=port,
|
|
|
|
|
|
reload=True, # 开发模式启用热重载
|
|
|
|
|
|
reload_dirs=["backend"], # 监控 backend 目录
|
|
|
|
|
|
log_level="debug", # 开发模式使用 debug 日志级别
|
|
|
|
|
|
log_config=build_uvicorn_log_config(),
|
|
|
|
|
|
)
|
2026-04-07 00:42:23 +08:00
|
|
|
|
if sys.platform == "win32":
|
|
|
|
|
|
run_kwargs["loop"] = "none"
|
|
|
|
|
|
|
|
|
|
|
|
# 启动服务器(开发模式)
|
|
|
|
|
|
uvicorn.run(**run_kwargs)
|
2026-02-23 09:31:46 +00:00
|
|
|
|
except KeyboardInterrupt:
|
|
|
|
|
|
logger.info("Received keyboard interrupt")
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"Server error: {e}")
|
|
|
|
|
|
raise
|
|
|
|
|
|
finally:
|
|
|
|
|
|
logger.info("Application shutdown complete")
|
2026-02-21 09:48:01 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
main()
|