feat(cookies): Cookie 失效后可账号重新登录替换, 并修复 Cookie 列空白过多
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import api from './client';
|
||||
import type { BasicSummary, CookieCheckResult, CookieItem, PageParams, PaginatedResponse, MessageDeletedResponse, MessageResponse } from './types';
|
||||
import type { BasicSummary, CookieCheckResult, CookieItem, LoginTaskItem, PageParams, PaginatedResponse, MessageDeletedResponse, MessageResponse } from './types';
|
||||
|
||||
export const cookieApi = {
|
||||
list: () => api.get<CookieItem[], CookieItem[]>('/cookies'),
|
||||
@@ -10,6 +10,10 @@ export const cookieApi = {
|
||||
exportCsv: (format?: string) => api.get<Blob, Blob>('/cookies/export', { responseType: 'blob', params: format ? { format } : {} }),
|
||||
check: (ids: number[]) =>
|
||||
api.post<{ results: CookieCheckResult[] }, { results: CookieCheckResult[] }>('/cookies/check', null, { params: { ids: ids.join(',') } }),
|
||||
relogin: (ids: number[]) =>
|
||||
api.post<{ batch_id: string; count: number; skipped: number; success: boolean }, { batch_id: string; count: number; skipped: number; success: boolean }>('/cookies/relogin', { ids }),
|
||||
loginTasks: (batchId: string) =>
|
||||
api.get<LoginTaskItem[], LoginTaskItem[]>('/login/tasks', { params: { batch_id: batchId } }),
|
||||
delete: (id: number) => api.delete<MessageResponse, MessageResponse>(`/cookies/${id}`),
|
||||
deleteBatch: (ids: number[]) => api.delete<MessageDeletedResponse, MessageDeletedResponse>('/cookies/batch', { params: { task_ids: ids.join(',') } }),
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { Table, Button, Card, Row, Col, Statistic, Tag, Popconfirm, Space, Typography, Input, theme, Dropdown, Tooltip } from 'antd';
|
||||
import { message } from '../utils/antdMessage';
|
||||
import { DownloadOutlined, DeleteOutlined, CopyOutlined, SearchOutlined, LoadingOutlined } from '@ant-design/icons';
|
||||
import { DownloadOutlined, DeleteOutlined, CopyOutlined, SearchOutlined, LoadingOutlined, ReloadOutlined } from '@ant-design/icons';
|
||||
import { cookieApi, type BasicSummary, type CookieCheckResult, type CookieItem } from '../api/modules';
|
||||
import { usePermissions } from '../hooks/usePermissions';
|
||||
import { formatTime } from '../utils/time';
|
||||
@@ -52,10 +52,64 @@ export default function CookiePage() {
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [checkingIds, setCheckingIds] = useState<Set<number>>(new Set());
|
||||
const [checkResults, setCheckResults] = useState<Map<number, CookieCheckResult>>(new Map());
|
||||
const [reloginIds, setReloginIds] = useState<Set<number>>(new Set());
|
||||
const { can } = usePermissions();
|
||||
|
||||
const canView = can('cookie:view');
|
||||
const canExport = can('cookie:export');
|
||||
const canRelogin = can('login:batch');
|
||||
|
||||
const pollRelogin = (batchId: string, count: number) => {
|
||||
let attempts = 0;
|
||||
const timer = window.setInterval(async () => {
|
||||
attempts += 1;
|
||||
try {
|
||||
const tasks = await cookieApi.loginTasks(batchId);
|
||||
const done = tasks.every((t) => !['pending', 'running'].includes(t.status));
|
||||
if (done) {
|
||||
window.clearInterval(timer);
|
||||
message.success(`重新登录完成: ${tasks.filter((t) => t.status === 'success').length}/${tasks.length} 个成功`);
|
||||
loadCookies();
|
||||
loadSummary();
|
||||
} else if (attempts > 120) {
|
||||
window.clearInterval(timer);
|
||||
message.warning('重新登录超时,请到登录任务页查看进度');
|
||||
}
|
||||
} catch {
|
||||
window.clearInterval(timer);
|
||||
message.warning('查询重登进度失败,请稍后手动刷新');
|
||||
}
|
||||
}, 3000);
|
||||
void count;
|
||||
};
|
||||
|
||||
const handleRelogin = async (ids: number[]) => {
|
||||
if (ids.length === 0) return;
|
||||
setReloginIds((prev) => new Set([...prev, ...ids]));
|
||||
try {
|
||||
const res = await cookieApi.relogin(ids);
|
||||
const tip = res.skipped > 0 ? `,${res.skipped} 条因账号缺少登录凭据被跳过` : '';
|
||||
message.success(`已开始重新登录 ${res.count} 个账号${tip}`);
|
||||
pollRelogin(res.batch_id, res.count);
|
||||
} catch (e: unknown) {
|
||||
message.error(getErrorMessage(e));
|
||||
} finally {
|
||||
setReloginIds((prev) => {
|
||||
const next = new Set(prev);
|
||||
for (const id of ids) next.delete(id);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleReloginSelected = () => {
|
||||
const ids = selectedRowKeys.map((k) => Number(k));
|
||||
if (ids.length === 0) {
|
||||
message.warning('请先选择要重新登录的 Cookie');
|
||||
return;
|
||||
}
|
||||
void handleRelogin(ids);
|
||||
};
|
||||
|
||||
const handleCheck = async (ids: number[]) => {
|
||||
if (ids.length === 0) return;
|
||||
@@ -234,6 +288,7 @@ export default function CookiePage() {
|
||||
{
|
||||
title: 'Cookie',
|
||||
dataIndex: 'cookie_preview',
|
||||
width: 300,
|
||||
ellipsis: true,
|
||||
render: (val: string) => {
|
||||
if (!canView) return <Tag>***</Tag>;
|
||||
@@ -306,6 +361,21 @@ export default function CookiePage() {
|
||||
检测
|
||||
</Button>
|
||||
)}
|
||||
{canRelogin && (
|
||||
<Popconfirm
|
||||
title="用账号信息重新登录并替换失效 Cookie?"
|
||||
onConfirm={() => handleRelogin([record.id])}
|
||||
>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<ReloadOutlined />}
|
||||
loading={reloginIds.has(record.id)}
|
||||
disabled={reloginIds.has(record.id)}
|
||||
>
|
||||
重登
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
)}
|
||||
{canExport && (
|
||||
<Popconfirm title="确认删除?" onConfirm={() => handleDelete(record.id)}>
|
||||
<Button danger size="small" icon={<DeleteOutlined />} />
|
||||
@@ -337,6 +407,20 @@ export default function CookiePage() {
|
||||
检测选中 {selectedRowKeys.length > 0 && `(${selectedRowKeys.length})`}
|
||||
</Button>
|
||||
)}
|
||||
{canRelogin && (
|
||||
<Popconfirm
|
||||
title={`确认重新登录选中的 ${selectedRowKeys.length} 条 Cookie?将用账号信息重新登录并替换`}
|
||||
onConfirm={handleReloginSelected}
|
||||
disabled={selectedRowKeys.length === 0}
|
||||
>
|
||||
<Button
|
||||
icon={<ReloadOutlined />}
|
||||
disabled={selectedRowKeys.length === 0}
|
||||
>
|
||||
重登选中 {selectedRowKeys.length > 0 && `(${selectedRowKeys.length})`}
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
)}
|
||||
{canExport && (
|
||||
<Popconfirm
|
||||
title={`确认删除选中的 ${selectedRowKeys.length} 条 Cookie?`}
|
||||
@@ -431,7 +515,7 @@ export default function CookiePage() {
|
||||
}
|
||||
},
|
||||
}}
|
||||
scroll={{ x: 900 }}
|
||||
scroll={{ x: 1210 }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user