forked from dou-jiang/codex-console
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathwebui.py
More file actions
180 lines (142 loc) · 5.85 KB
/
webui.py
File metadata and controls
180 lines (142 loc) · 5.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
"""
Web UI 启动入口
"""
import uvicorn
import logging
import socket
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.core.timezone_utils import apply_process_timezone
from src.core.db_logs import install_database_log_handler
from src.database.init_db import initialize_database
from src.config.settings import get_settings
from src.config.project_notice import build_terminal_notice_lines
def _print_project_notice():
"""Print the project notice to the terminal on startup."""
for line in build_terminal_notice_lines():
print(line)
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():
"""设置应用程序"""
# 统一进程时区为北京时间,避免容器默认 UTC 导致时间错位
apply_process_timezone()
# 加载 .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
)
install_database_log_handler()
logger = logging.getLogger(__name__)
logger.info("数据库初始化完成,地基已经打好")
logger.info(f"数据目录已安顿好: {data_dir}")
logger.info(f"日志目录也已就位: {logs_dir}")
logger.info("应用程序设置完成,齿轮已经咔哒一声卡上了")
return settings
def start_webui():
_print_project_notice()
"""启动 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
start_webui()
if __name__ == "__main__":
# PyInstaller 打包后 Windows 上 uvicorn 可能拉起多进程,
# 这里先做 freeze_support,避免 multiprocessing-fork 参数报错。
import multiprocessing
multiprocessing.freeze_support()
main()