取消运营后台注册功能

This commit is contained in:
kx_guo 2026-09-05 15:43:54 +08:00
parent 20ca5384fc
commit 970dc8bfee
2 changed files with 26 additions and 65 deletions

View File

@ -2019,14 +2019,8 @@ def dingtalk_login_callback(response: Response, code: str = "", state: str = "",
@app.post("/api/admin/auth/register", status_code=status.HTTP_201_CREATED) @app.post("/api/admin/auth/register", status_code=status.HTTP_201_CREATED)
def register(payload: RegisterRequest, db: Session = Depends(get_db)): def register(_: RegisterRequest):
if db.scalar(select(AdminUser).where(AdminUser.username == payload.username)): raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="运营后台已关闭自助注册,请联系管理员创建账号")
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="用户名已存在")
user = AdminUser(username=payload.username, password_hash=hash_password(payload.password), display_name=payload.display_name, role_code="operator")
db.add(user)
db.commit()
db.refresh(user)
return {"success": True, "data": {"user": public_user(user)}}
@app.get("/api/admin/auth/me") @app.get("/api/admin/auth/me")

View File

@ -80,13 +80,15 @@ async function readReferenceFile(file, requestApi = api) {
} }
function LoginPage({ onLogin }) { function LoginPage({ onLogin }) {
const [registering, setRegistering] = useState(false);
const [username, setUsername] = useState(""); const [username, setUsername] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [displayName, setDisplayName] = useState("");
const [error, setError] = useState(""); const [error, setError] = useState("");
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [dingtalkLoading, setDingtalkLoading] = useState(false); const [dingtalkLoading, setDingtalkLoading] = useState(false);
const [autoDingtalkLogin, setAutoDingtalkLogin] = useState(false);
const autoLoginStarted = useRef(false);
const query = new URLSearchParams(window.location.search);
const inDingtalkWorkbench = /DingTalk|AliApp\(DT/i.test(navigator.userAgent || "") || query.get("from") === "dingtalk";
async function loginWithDingTalk() { async function loginWithDingTalk() {
setDingtalkLoading(true); setDingtalkLoading(true);
setError(""); setError("");
@ -96,42 +98,36 @@ function LoginPage({ onLogin }) {
} catch (err) { } catch (err) {
setError(err.message); setError(err.message);
setDingtalkLoading(false); setDingtalkLoading(false);
setAutoDingtalkLogin(false);
} }
} }
async function submit(event) { async function submit(event) {
event.preventDefault(); event.preventDefault();
setError(""); setError("");
if (!username.trim() || !password || (registering && !displayName.trim())) if (!username.trim() || !password)
return setError("\u8bf7\u5b8c\u6574\u586b\u5199\u4fe1\u606f"); return setError("\u8bf7\u5b8c\u6574\u586b\u5199\u4fe1\u606f");
setLoading(true); setLoading(true);
try { try {
if (registering) {
await api("/api/admin/auth/register", {
method: "POST",
body: JSON.stringify({
username,
password,
display_name: displayName,
}),
});
setRegistering(false);
setPassword("");
setError(
"\u6ce8\u518c\u6210\u529f\uff0c\u8bf7\u4f7f\u7528\u65b0\u8d26\u53f7\u767b\u5f55",
);
} else {
const result = await api("/api/admin/auth/login", { const result = await api("/api/admin/auth/login", {
method: "POST", method: "POST",
body: JSON.stringify({ username, password }), body: JSON.stringify({ username, password }),
}); });
onLogin(result.data.user); onLogin(result.data.user);
}
} catch (err) { } catch (err) {
setError(err.message); setError(err.message);
} finally { } finally {
setLoading(false); setLoading(false);
} }
} }
useEffect(() => {
if (!inDingtalkWorkbench || query.get("dingtalk") === "success" || autoLoginStarted.current) return;
autoLoginStarted.current = true;
setAutoDingtalkLogin(true);
loginWithDingTalk();
}, []);
if (autoDingtalkLogin) {
return <div className="loading-screen">正在验证钉钉身份…</div>;
}
return ( return (
<main className="login-shell"> <main className="login-shell">
<div className="login-decoration"> <div className="login-decoration">
@ -144,28 +140,17 @@ function LoginPage({ onLogin }) {
<MessageCircle size={28} /> <MessageCircle size={28} />
</div> </div>
<div className="brand-title">{Z.brand}</div> <div className="brand-title">{Z.brand}</div>
<h1>{registering ? Z.register : Z.backend}</h1> <h1>{Z.backend}</h1>
<p className="login-subtitle">从钉钉工作台进入,使用钉钉身份安全登录</p> <p className="login-subtitle">从钉钉工作台进入,使用钉钉身份安全登录</p>
<button className="dingtalk-login-button" type="button" onClick={loginWithDingTalk} disabled={dingtalkLoading}> <button className="dingtalk-login-button" type="button" onClick={loginWithDingTalk} disabled={dingtalkLoading}>
{dingtalkLoading ? "正在跳转钉钉…" : "使用钉钉登录"} {dingtalkLoading ? "正在跳转钉钉…" : "使用钉钉登录"}
</button> </button>
<div className="login-divider"><span>或使用后台账号</span></div> <div className="login-divider"><span>或使用后台账号</span></div>
<form onSubmit={submit} className="login-form"> <form onSubmit={submit} className="login-form">
{registering && (
<label>
{Z.displayName}
<input
autoFocus
value={displayName}
onChange={(event) => setDisplayName(event.target.value)}
placeholder={"\u8bf7\u8f93\u5165\u663e\u793a\u540d\u79f0"}
/>
</label>
)}
<label> <label>
{Z.username} {Z.username}
<input <input
autoFocus={!registering} autoFocus
value={username} value={username}
onChange={(event) => setUsername(event.target.value)} onChange={(event) => setUsername(event.target.value)}
placeholder={"\u8bf7\u8f93\u5165\u7528\u6237\u540d"} placeholder={"\u8bf7\u8f93\u5165\u7528\u6237\u540d"}
@ -178,33 +163,15 @@ function LoginPage({ onLogin }) {
value={password} value={password}
onChange={(event) => setPassword(event.target.value)} onChange={(event) => setPassword(event.target.value)}
placeholder={ placeholder={
registering "\u8bf7\u8f93\u5165\u5bc6\u7801"
? "\u81f3\u5c11 8 \u4f4d\u5bc6\u7801"
: "\u8bf7\u8f93\u5165\u5bc6\u7801"
} }
/> />
</label> </label>
{error && <div className="form-error">{error}</div>} {error && <div className="form-error">{error}</div>}
<button className="primary-button login-button" disabled={loading}> <button className="primary-button login-button" disabled={loading}>
{loading {loading ? "\u767b\u5f55\u4e2d\u2026" : Z.login}
? registering
? "\u6ce8\u518c\u4e2d\u2026"
: "\u767b\u5f55\u4e2d\u2026"
: registering
? Z.register
: Z.login}
</button> </button>
</form> </form>
<button
className="text-button"
onClick={() => {
setRegistering(!registering);
setError("");
setPassword("");
}}
>
{registering ? "\u8fd4\u56de\u767b\u5f55" : Z.register}
</button>
<div className="login-hint"> <div className="login-hint">
{ {
"\u8fd0\u8425\u8d26\u53f7\u767b\u5f55 \u00b7 \u7528\u6237 A/B \u8bf7\u4f7f\u7528\u72ec\u7acb\u4efb\u52a1\u94fe\u63a5" "\u8fd0\u8425\u8d26\u53f7\u767b\u5f55 \u00b7 \u7528\u6237 A/B \u8bf7\u4f7f\u7528\u72ec\u7acb\u4efb\u52a1\u94fe\u63a5"