feat(web): main.py web 支持 --ssl-certfile/--ssl-keyfile(uvicorn 原生 TLS,免 nginx)+ bump 0.26.8

两者同时给即在本端口跑 HTTPS,只给其一报错;都不给=明文(向后兼容)。
适配「只有 8765 对外」场景:zcbot 直接在 8765 上 HTTPS,不用 nginx/不挪端口。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-06-25 10:43:18 +08:00
parent 0cf6e3e61e
commit 23c5ab20e0
2 changed files with 19 additions and 5 deletions

View File

@ -1,3 +1,3 @@
# zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。
# 改版本只动这一行。
__version__ = "0.26.7"
__version__ = "0.26.8"

22
main.py
View File

@ -209,10 +209,24 @@ def user_role(email: str, role: str) -> None:
help="监听端口")
@click.option("--reload/--no-reload", default=False,
help="dev:文件改动自动重启(uvicorn 工厂模式)")
def web(host: str, port: int, reload: bool) -> None:
"""启动 Web 服务(JSON API + dev SPA)。Auth 需 PLATFORM_KEY / JWT_SECRET env。"""
@click.option("--ssl-certfile", default=None,
help="TLS 证书链(fullchain.pem);与 --ssl-keyfile 同时给即在本端口跑 HTTPS")
@click.option("--ssl-keyfile", default=None,
help="TLS 私钥(privkey.pem)")
def web(host: str, port: int, reload: bool,
ssl_certfile: str | None, ssl_keyfile: str | None) -> None:
"""启动 Web 服务(JSON API + dev SPA)。Auth 需 PLATFORM_KEY / JWT_SECRET env。
HTTPS:`--ssl-certfile <fullchain.pem> --ssl-keyfile <privkey.pem>`(uvicorn 原生 TLS,
无需 nginx)两者都不给 = 明文 HTTP(默认,向后兼容)
"""
import uvicorn
# 两者都给才算启用 TLS;只给其一报错提醒(避免半配置悄悄退回 http)
if bool(ssl_certfile) ^ bool(ssl_keyfile):
raise click.UsageError("--ssl-certfile 与 --ssl-keyfile 必须同时提供")
tls = {"ssl_certfile": ssl_certfile, "ssl_keyfile": ssl_keyfile} if ssl_certfile else {}
# timeout_graceful_shutdown=5:SIGTERM 后 uvicorn 至多等 5s 关掉在连的 HTTP 请求
# (主要是长连 SSE GET,断开后客户端会重连,run 不受影响),再进 lifespan shutdown
# 跑真正的 run drain(见 web/app.py finally + config/agent.yaml `shutdown` 段)。
@ -221,11 +235,11 @@ def web(host: str, port: int, reload: bool) -> None:
# reload 模式需要 import string + factory,uvicorn 才能监听文件
uvicorn.run("web.app:create_app", host=host, port=port,
reload=True, factory=True, log_level="info",
timeout_graceful_shutdown=5)
timeout_graceful_shutdown=5, **tls)
else:
from web.app import create_app
uvicorn.run(create_app(), host=host, port=port, log_level="info",
timeout_graceful_shutdown=5)
timeout_graceful_shutdown=5, **tls)
# ─────────────── Sandbox(Stage C 部署前置对账) ───────────────