feat(白名单): 账号级登录口令——管理员设明文口令,用户凭 /login/<用户名>?password= 直登
白名单管理页可给单个账号设明文登录口令(users.login_password,可空):
设置后该账号用户名直登(/login/username)须带匹配 ?password=,且优先于全局共享口令门;
未设口令的账号行为不变。前端登录 URL 已支持透传 ?password=,无需改登录页。
- 后端:UserRow/User 新增 login_password + 仓储映射;/login/username 账号级口令闸门
(_lookup_user_by_username 仅查不建);新管理员接口 PUT /users/{id}/login-password;
UserListItem 暴露明文(仅 admin 列表);migration 20260625_04 + 幂等补列 SQL。
- 前端:白名单页新增「登录口令」列(设置/修改/清除 + 复制登录链接);
core/auth api+hooks 增 setUserLoginPassword/useSetUserLoginPassword(含 Y-Log)。
- 文档:根 + 后端 CLAUDE.md 同步。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
bce0abba6e
commit
9572b66ecb
11 files changed
+402
-44
No files matched your search
@@ -13,6 +13,12 @@ export interface UserListItem {
|
||||
qa_count: number;
|
||||
/** 登录白名单放行标记:开关开启后仅 approved 用户(或 admin)可登录。 */
|
||||
approved: boolean;
|
||||
/**
|
||||
* 账号级「登录口令」(明文,仅管理员可见)。非空 → 该账号用户名直登必须带匹配的
|
||||
* `?password=`;为空/null → 走原有共享口令门/免口令逻辑。管理员据此拼出登录链接
|
||||
* `…/login/<用户名>?password=xxxx` 发给用户。
|
||||
*/
|
||||
login_password: string | null;
|
||||
}
|
||||
|
||||
export async function listAllUsers(): Promise<UserListItem[]> {
|
||||
@@ -35,6 +41,25 @@ export async function setUserApproval(userId: string, approved: boolean): Promis
|
||||
return res.json() as Promise<UserListItem>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 / 清除某个账号的「登录口令」(明文)。Admin only。
|
||||
*
|
||||
* 传非空字符串 → 设置;传 `null` 或空字符串 → 清除。设置后该账号用户名直登需带匹配的
|
||||
* `?password=`。返回更新后的用户项(含明文 `login_password`)。
|
||||
*/
|
||||
export async function setUserLoginPassword(userId: string, password: string | null): Promise<UserListItem> {
|
||||
const res = await apiFetch(`${getBackendBaseURL()}/api/v1/auth/users/${encodeURIComponent(userId)}/login-password`, {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ password }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => null);
|
||||
throw new Error(extractErrorMessage(data, "保存失败"));
|
||||
}
|
||||
return res.json() as Promise<UserListItem>;
|
||||
}
|
||||
|
||||
// Normalize a FastAPI error body into a human-readable string. `detail` may be
|
||||
// a plain string, an object like AuthErrorResponse ({ code, message }), or an
|
||||
// array of validation errors — anything but a string would otherwise render as
|
||||
|
||||
Reference in new issue
Block a user