feat: 安全设置展示有效登录会话,支持剔除其它设备
按设备指纹去重并直出 IP;非当前会话可踢下线,对方需重新输入密码。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1346,6 +1346,47 @@ export async function apiChangePassword(oldPassword: string, newPassword: string
|
||||
return res.json();
|
||||
}
|
||||
|
||||
/** 当前用户仍有效的登录会话(安全设置 SSR / 客户端) */
|
||||
export interface LoginDevice {
|
||||
id: number;
|
||||
ip: string;
|
||||
device_kind: "computer" | "mobile" | "tablet";
|
||||
device_label: string;
|
||||
browser: string;
|
||||
current: boolean;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface LoginDevicesResponse {
|
||||
devices: LoginDevice[];
|
||||
}
|
||||
|
||||
export async function fetchLoginDevices(
|
||||
cookieHeader?: string,
|
||||
viewer?: { userAgent?: string; clientIP?: string }
|
||||
): Promise<LoginDevice[]> {
|
||||
const headers: Record<string, string> = {};
|
||||
if (cookieHeader) headers.Cookie = cookieHeader;
|
||||
if (viewer?.userAgent) headers["X-J13-Client-UA"] = viewer.userAgent;
|
||||
if (viewer?.clientIP) headers["X-J13-Client-IP"] = viewer.clientIP;
|
||||
const res = await fetch(`${API_BASE}/api/me/login-devices`, ssrInit(headers));
|
||||
if (res.status === 401) return [];
|
||||
if (!res.ok) throw new Error("获取登录设备失败");
|
||||
const data = (await res.json()) as LoginDevicesResponse;
|
||||
return data.devices ?? [];
|
||||
}
|
||||
|
||||
/** 剔除其它登录设备,该设备需重新输入密码登录 */
|
||||
export async function apiRevokeLoginDevice(
|
||||
id: number
|
||||
): Promise<{ ok?: boolean; error?: string }> {
|
||||
const res = await fetchWithRefresh(`/api/me/login-devices/${id}`, {
|
||||
method: "DELETE",
|
||||
headers: clientHeaders(),
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
|
||||
// 更新当前用户资料(昵称、头像、邮箱、签名)
|
||||
export async function apiUpdateProfile(data: {
|
||||
nickname: string;
|
||||
|
||||
Reference in New Issue
Block a user