Some checks are pending
Docker Image CI / build-and-push-image (push) Waiting to run
174 lines
5.7 KiB
Python
174 lines
5.7 KiB
Python
"""
|
||
Web UI 启动入口
|
||
"""
|
||
|
||
import uvicorn
|
||
import logging
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
# 添加项目根目录到 Python 路径
|
||
# PyInstaller 打包后 __file__ 在临时解压目录,需要用 sys.executable 所在目录作为数据目录
|
||
import os
|
||
if getattr(sys, 'frozen', False):
|
||
# 打包后:使用可执行文件所在目录
|
||
project_root = Path(sys.executable).parent
|
||
_src_root = Path(sys._MEIPASS)
|
||
else:
|
||
project_root = Path(__file__).parent
|
||
_src_root = project_root
|
||
sys.path.insert(0, str(_src_root))
|
||
|
||
from src.core.utils import setup_logging
|
||
from src.database.init_db import initialize_database
|
||
from src.config.settings import get_settings
|
||
|
||
|
||
def _load_dotenv():
|
||
"""加载 .env 文件(可执行文件同目录或项目根目录)"""
|
||
env_path = project_root / ".env"
|
||
if not env_path.exists():
|
||
return
|
||
with open(env_path, encoding="utf-8") as f:
|
||
for line in f:
|
||
line = line.strip()
|
||
if not line or line.startswith("#") or "=" not in line:
|
||
continue
|
||
key, _, value = line.partition("=")
|
||
key = key.strip()
|
||
value = value.strip().strip('"').strip("'")
|
||
if key and key not in os.environ:
|
||
os.environ[key] = value
|
||
|
||
|
||
def setup_application():
|
||
"""设置应用程序"""
|
||
# 加载 .env 文件(优先级低于已有环境变量)
|
||
_load_dotenv()
|
||
|
||
# 确保数据目录和日志目录在可执行文件所在目录(打包后也适用)
|
||
data_dir = project_root / "data"
|
||
logs_dir = project_root / "logs"
|
||
data_dir.mkdir(exist_ok=True)
|
||
logs_dir.mkdir(exist_ok=True)
|
||
|
||
# 将数据目录路径注入环境变量,供数据库配置使用
|
||
os.environ.setdefault("APP_DATA_DIR", str(data_dir))
|
||
os.environ.setdefault("APP_LOGS_DIR", str(logs_dir))
|
||
|
||
# 初始化数据库(必须先于获取设置)
|
||
try:
|
||
initialize_database()
|
||
except Exception as e:
|
||
print(f"数据库初始化失败: {e}")
|
||
raise
|
||
|
||
# 获取配置(需要数据库已初始化)
|
||
settings = get_settings()
|
||
|
||
# 配置日志(日志文件写到实际 logs 目录)
|
||
log_file = str(logs_dir / Path(settings.log_file).name)
|
||
setup_logging(
|
||
log_level=settings.log_level,
|
||
log_file=log_file
|
||
)
|
||
|
||
logger = logging.getLogger(__name__)
|
||
logger.info("数据库初始化完成")
|
||
logger.info(f"数据目录: {data_dir}")
|
||
logger.info(f"日志目录: {logs_dir}")
|
||
|
||
logger.info("应用程序设置完成")
|
||
return settings
|
||
|
||
|
||
def start_webui():
|
||
"""启动 Web UI"""
|
||
# 设置应用程序
|
||
settings = setup_application()
|
||
|
||
# 导入 FastAPI 应用(延迟导入以避免循环依赖)
|
||
from src.web.app import app
|
||
|
||
# 配置 uvicorn
|
||
uvicorn_config = {
|
||
"app": "src.web.app:app",
|
||
"host": settings.webui_host,
|
||
"port": settings.webui_port,
|
||
"reload": settings.debug,
|
||
"log_level": "info" if settings.debug else "warning",
|
||
"access_log": settings.debug,
|
||
"ws": "websockets",
|
||
}
|
||
|
||
logger = logging.getLogger(__name__)
|
||
logger.info(f"启动 Web UI 在 http://{settings.webui_host}:{settings.webui_port}")
|
||
logger.info(f"调试模式: {settings.debug}")
|
||
|
||
# 启动服务器
|
||
uvicorn.run(**uvicorn_config)
|
||
|
||
|
||
def main():
|
||
"""主函数"""
|
||
import argparse
|
||
import os
|
||
|
||
parser = argparse.ArgumentParser(description="OpenAI/Codex CLI 自动注册系统 Web UI")
|
||
parser.add_argument("--host", help="监听主机 (也可通过 WEBUI_HOST 环境变量设置)")
|
||
parser.add_argument("--port", type=int, help="监听端口 (也可通过 WEBUI_PORT 环境变量设置)")
|
||
parser.add_argument("--debug", action="store_true", help="启用调试模式 (也可通过 DEBUG=1 环境变量设置)")
|
||
parser.add_argument("--reload", action="store_true", help="启用热重载")
|
||
parser.add_argument("--log-level", help="日志级别 (也可通过 LOG_LEVEL 环境变量设置)")
|
||
parser.add_argument("--access-password", help="Web UI 访问密钥 (也可通过 WEBUI_ACCESS_PASSWORD 环境变量设置)")
|
||
args = parser.parse_args()
|
||
|
||
# 更新配置
|
||
from src.config.settings import update_settings
|
||
|
||
updates = {}
|
||
|
||
# 优先使用命令行参数,如果没有则尝试从环境变量获取
|
||
host = args.host or os.environ.get("WEBUI_HOST")
|
||
if host:
|
||
updates["webui_host"] = host
|
||
|
||
port = args.port or os.environ.get("WEBUI_PORT")
|
||
if port:
|
||
updates["webui_port"] = int(port)
|
||
|
||
debug = args.debug or os.environ.get("DEBUG", "").lower() in ("1", "true", "yes")
|
||
if debug:
|
||
updates["debug"] = debug
|
||
|
||
log_level = args.log_level or os.environ.get("LOG_LEVEL")
|
||
if log_level:
|
||
updates["log_level"] = log_level
|
||
|
||
access_password = args.access_password or os.environ.get("WEBUI_ACCESS_PASSWORD")
|
||
if access_password:
|
||
updates["webui_access_password"] = access_password
|
||
|
||
if updates:
|
||
update_settings(**updates)
|
||
|
||
# 启动 Web UI
|
||
# 初始化 Playwright 浏览器池(如果引擎设置为 playwright)
|
||
try:
|
||
from src.config.settings import get_settings as _gs
|
||
_cfg = _gs()
|
||
if getattr(_cfg, 'registration_engine', 'http') == 'playwright':
|
||
from src.core.playwright_pool import playwright_pool
|
||
_pool_size = getattr(_cfg, 'playwright_pool_size', 5)
|
||
if playwright_pool.initialize(pool_size=_pool_size):
|
||
print(f"[Playwright] 浏览器池已启动,池大小: {_pool_size}")
|
||
else:
|
||
print("[Playwright] 浏览器池启动失败,将在首次使用时尝试")
|
||
except Exception as _pw_err:
|
||
print(f"[Playwright] 初始化跳过: {_pw_err}")
|
||
|
||
start_webui()
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main() |