b5cba83df4
Co-authored-by: Cursor <cursoragent@cursor.com>
84 lines
2.5 KiB
Python
84 lines
2.5 KiB
Python
"""系统设置:管理员账号。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from pydantic import BaseModel, Field
|
|
|
|
from apps.api.auth import auth_disabled, require_auth, verify_credentials
|
|
from packages.config import get_settings, reload_settings
|
|
from packages.config.env_file import update_env_file
|
|
|
|
router = APIRouter(
|
|
prefix="/settings",
|
|
tags=["settings"],
|
|
dependencies=[Depends(require_auth)],
|
|
)
|
|
|
|
_USERNAME_RE = re.compile(r"^[a-zA-Z0-9_\-]{2,32}$")
|
|
|
|
|
|
class UpdateAccountBody(BaseModel):
|
|
current_password: str = Field(min_length=1, max_length=256)
|
|
new_username: str | None = Field(default=None, max_length=32)
|
|
new_password: str | None = Field(default=None, max_length=256)
|
|
|
|
|
|
@router.get("/account")
|
|
def get_account() -> dict:
|
|
s = get_settings()
|
|
return {
|
|
"username": s.admin_username,
|
|
"auth_required": not auth_disabled(),
|
|
"env_file": str(s.env_file_path),
|
|
}
|
|
|
|
|
|
@router.put("/account")
|
|
def update_account(body: UpdateAccountBody) -> dict:
|
|
s = get_settings()
|
|
if auth_disabled():
|
|
raise HTTPException(status_code=400, detail="auth disabled; edit .env manually")
|
|
|
|
if not verify_credentials(s.admin_username, body.current_password):
|
|
raise HTTPException(status_code=401, detail="current password incorrect")
|
|
|
|
if body.new_password is not None and len(body.new_password) < 6:
|
|
raise HTTPException(status_code=400, detail="password must be at least 6 characters")
|
|
|
|
new_user = (body.new_username or s.admin_username).strip()
|
|
new_pass = body.new_password or s.admin_password
|
|
|
|
if body.new_username is not None and not _USERNAME_RE.fullmatch(new_user):
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="username: 2-32 chars, letters/digits/_/- only",
|
|
)
|
|
|
|
if new_user == s.admin_username and new_pass == s.admin_password:
|
|
return {"ok": True, "changed": False, "message": "no changes", "username": new_user}
|
|
|
|
updates = {
|
|
"ADMIN_USERNAME": new_user,
|
|
"ADMIN_PASSWORD": new_pass,
|
|
}
|
|
path = s.env_file_path
|
|
try:
|
|
update_env_file(path, updates)
|
|
except OSError as e:
|
|
raise HTTPException(
|
|
status_code=500,
|
|
detail=f"failed to write {path}: {e}",
|
|
) from e
|
|
|
|
reload_settings()
|
|
return {
|
|
"ok": True,
|
|
"changed": True,
|
|
"username": new_user,
|
|
"relogin_required": True,
|
|
"message": "账号已更新,请重新登录",
|
|
}
|