增加密码
This commit is contained in:
@@ -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(),
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user