增加密码

This commit is contained in:
dekun
2026-05-22 11:44:34 +08:00
parent 661305c26a
commit cd129b6a25
8 changed files with 704 additions and 141 deletions
+79
View File
@@ -20,6 +20,16 @@ from settings_store import (
load_settings,
save_settings,
)
from hub_web_auth import (
SESSION_COOKIE,
SESSION_MAX_AGE_SEC,
cookie_secure,
create_session_token,
is_public_path,
password_required,
validate_session_token,
verify_password,
)
from url_public import browser_url, default_review_url, public_origin
HUB_HOST = os.getenv("HUB_HOST", "0.0.0.0")
@@ -99,6 +109,24 @@ async def local_only(request: Request, call_next):
return await call_next(request)
@app.middleware("http")
async def hub_password_gate(request: Request, call_next):
if not password_required():
return await call_next(request)
path = request.url.path
if is_public_path(path, request.method):
return await call_next(request)
token = request.cookies.get(SESSION_COOKIE)
if validate_session_token(token):
return await call_next(request)
if path.startswith("/api/"):
return JSONResponse({"detail": "未登录", "login_required": True}, status_code=401)
from fastapi.responses import RedirectResponse
nxt = path if path.startswith("/") else "/monitor"
return RedirectResponse(f"/login?next={nxt}", status_code=302)
def _shell_page():
index = STATIC_DIR / "index.html"
if not index.is_file():
@@ -106,6 +134,56 @@ def _shell_page():
return FileResponse(index)
def _login_page():
login = STATIC_DIR / "login.html"
if not login.is_file():
return JSONResponse({"detail": "missing static/login.html"}, status_code=500)
return FileResponse(login)
class LoginBody(BaseModel):
password: str = ""
@app.get("/api/auth/status")
def api_auth_status(request: Request):
required = password_required()
logged_in = not required or validate_session_token(request.cookies.get(SESSION_COOKIE))
return {"required": required, "logged_in": logged_in}
@app.post("/api/auth/login")
def api_auth_login(body: LoginBody):
if not password_required():
return {"ok": True, "auth_disabled": True}
if not verify_password(body.password):
raise HTTPException(status_code=401, detail="密码错误")
token = create_session_token()
resp = JSONResponse({"ok": True})
resp.set_cookie(
SESSION_COOKIE,
token,
httponly=True,
samesite="lax",
path="/",
max_age=SESSION_MAX_AGE_SEC,
secure=cookie_secure(),
)
return resp
@app.post("/api/auth/logout")
def api_auth_logout():
resp = JSONResponse({"ok": True})
resp.delete_cookie(SESSION_COOKIE, path="/")
return resp
@app.get("/login")
def login_page():
return _login_page()
@app.get("/")
def root_redirect():
from fastapi.responses import RedirectResponse
@@ -155,6 +233,7 @@ def api_settings_meta():
if not po
else "复盘/展示链接已替换为对外地址"
),
"password_required": password_required(),
}