From 12c31c2e0954d848f706a5fb105208af246155bb Mon Sep 17 00:00:00 2001 From: yml2213 Date: Tue, 23 Jun 2026 07:14:08 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E6=B6=88=E9=99=A4=E5=89=8D?= =?UTF-8?q?=E7=AB=AF52=E5=A4=84any=E7=B1=BB=E5=9E=8B+=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E5=86=97=E4=BD=99requirements.txt+=E8=B0=83=E8=AF=95print?= =?UTF-8?q?=E6=94=B9=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 requirements.txt,pyproject.toml 为唯一依赖源(含 requests[socks] extra) - core/geetest 下 5 处 print() 替换为 loguru logger.debug() - api/modules.ts 新增 13 个响应接口,消除所有 api.xxx - 新建 utils/error.ts 提供 getErrorMessage(e: unknown) 安全取消息 - 11 个页面组件 catch (e: any) 改为 catch (e: unknown) - useState、columns: any[]、render 参数全部类型化 - TypeScript 类型检查零错误通过 --- core/geetest/common/imaging.py | 3 +- core/geetest/common/network.py | 12 +- core/geetest/v3_slide/solver.py | 3 +- requirements.txt | 13 -- web/frontend/src/api/modules.ts | 193 ++++++++++++++++++--- web/frontend/src/layouts/MainLayout.tsx | 2 +- web/frontend/src/pages/AccountsPage.tsx | 49 +++--- web/frontend/src/pages/AssignmentsPage.tsx | 51 ++---- web/frontend/src/pages/CookiePage.tsx | 35 ++-- web/frontend/src/pages/DashboardPage.tsx | 6 +- web/frontend/src/pages/HttpLogsPage.tsx | 28 +-- web/frontend/src/pages/LoginPage.tsx | 5 +- web/frontend/src/pages/LoginTasksPage.tsx | 38 ++-- web/frontend/src/pages/ProxyPage.tsx | 21 +-- web/frontend/src/pages/UsersPage.tsx | 29 ++-- web/frontend/src/utils/error.ts | 6 + 16 files changed, 305 insertions(+), 189 deletions(-) delete mode 100644 requirements.txt create mode 100644 web/frontend/src/utils/error.ts diff --git a/core/geetest/common/imaging.py b/core/geetest/common/imaging.py index 11c0da8..54f8367 100644 --- a/core/geetest/common/imaging.py +++ b/core/geetest/common/imaging.py @@ -2,6 +2,7 @@ import requests import cv2 import numpy as np from PIL import Image +from loguru import logger REQUEST_TIMEOUT = (3.05, 12) @@ -60,7 +61,7 @@ def restore_geetest_image(input_path:str, output_path:str) -> None: new_img.paste(l, box=(_ % 26 * 10, 80 if _ > 25 else 0)) new_img.save(output_path) - print(f"图像已还原并保存到: {output_path}") + logger.debug("图像已还原并保存到: {}", output_path) # 下载图片 def download_picture(bg:str, fullbg:str, slice:str) -> int: diff --git a/core/geetest/common/network.py b/core/geetest/common/network.py index d329379..0ddd554 100644 --- a/core/geetest/common/network.py +++ b/core/geetest/common/network.py @@ -3,6 +3,7 @@ import requests import json import re from typing import Mapping, Optional, Tuple +from loguru import logger REQUEST_TIMEOUT = (10, 30) PASSPORT_REFERER = "https://passport.douyu.com/" @@ -263,13 +264,8 @@ def get_picture(gt:str, challenge:str) -> tuple[str, str, list[int], str, str, s timeout=REQUEST_TIMEOUT, ) response.raise_for_status() - print(response.text) + logger.debug("极验 get.php 原始响应: {}", response.text[:500]) match = re.search(r'\((.*)\)$', response.text) - if match: - json_str = match.group(1) - data = json.loads(json_str) - - # 检查验证码类型 if 'data' in data and isinstance(data['data'], dict): # 新版极验格式 inner_data = data['data'] @@ -317,12 +313,12 @@ def req_end(gt:str, challenge:str, w:str) -> dict: timeout=REQUEST_TIMEOUT, ) response.raise_for_status() - print(response.text) + logger.debug("极验 ajax.php 原始响应: {}", response.text[:500]) match = re.search(r'\((.*)\)$', response.text) if match: json_str = match.group(1) data = json.loads(json_str) - print(f"极验验证响应: {data}") + logger.debug("极验验证响应: {}", data) # 检查验证是否成功 if data.get('success') == 1: diff --git a/core/geetest/v3_slide/solver.py b/core/geetest/v3_slide/solver.py index 8461d90..cac3270 100644 --- a/core/geetest/v3_slide/solver.py +++ b/core/geetest/v3_slide/solver.py @@ -1,6 +1,7 @@ import time import random import json +from loguru import logger from core.geetest.common.trajectory import generate_realistic_trajectory, process_mouse_trajectory, compress_trajectory, TrajectoryEncoder, \ H from core.geetest.common.crypto import four_random_chart, RSA_jiami_r, AES_O, geetest_base64_encode, encrypt_string, simple_md5 @@ -106,7 +107,7 @@ def get_w3(str_16:str, challenge:str, hkjl:int, c:list[int], s:str, gt:str) -> s trajectory = get_slide_track(hkjl)[0] - print(trajectory) + logger.debug("滑动轨迹: {}", trajectory) # trajectory = generator.generate(target_x) userresponse = H(trajectory[-1][0], challenge) diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 13fa0a9..0000000 --- a/requirements.txt +++ /dev/null @@ -1,13 +0,0 @@ -requests>=2.31.0 -pycryptodome>=3.19.0 -numpy>=1.24.0 -opencv-python-headless>=4.8.0 -Pillow>=10.0.0 -loguru>=0.7.0 -fastapi>=0.110.0 -uvicorn[standard]>=0.27.0 -sqlalchemy>=2.0.0 -python-jose[cryptography]>=3.3.0 -bcrypt>=4.0.0 -pydantic>=2.0.0 -python-multipart>=0.0.9 diff --git a/web/frontend/src/api/modules.ts b/web/frontend/src/api/modules.ts index 2dd979e..1a16934 100644 --- a/web/frontend/src/api/modules.ts +++ b/web/frontend/src/api/modules.ts @@ -1,5 +1,22 @@ import api from './index'; +// ==================== 通用响应类型 ==================== + +interface MessageResponse { + message: string; + success: boolean; +} + +interface MessageCountResponse extends MessageResponse { + count: number; +} + +interface MessageDeletedResponse extends MessageResponse { + deleted: number; +} + +// ==================== Auth ==================== + export interface LoginResult { access_token: string; token_type: string; @@ -8,6 +25,16 @@ export interface LoginResult { permissions: string[]; } +export interface CurrentUser { + id: number; + username: string; + role: string; + role_label: string; + is_active: boolean; + remark: string | null; + permissions: string[]; +} + export interface UserInfo { id: number; username: string; @@ -18,13 +45,127 @@ export interface UserInfo { custom_permissions?: string[] | null; } +// ==================== Account ==================== + +export interface AccountItem { + id: number; + username: string; + password?: string | null; + email?: string | null; + email_password?: string | null; + tag: string; + assigned_to: number | null; + assigned_username: string | null; + remark: string; + created_at: string | null; +} + +export interface AssignmentsSummary { + support_users: SupportUserItem[]; + unassigned_count: number; +} + +export interface SupportUserItem { + id: number; + username: string; + assigned_count: number; +} + +// ==================== Login Task ==================== + +export interface LoginTaskItem { + id: number; + batch_id: string; + account_id: number; + account_username: string; + status: string; + cookie: string; + message: string; + created_by: number; + created_at: string | null; + finished_at: string | null; +} + +export interface BatchLoginResult { + batch_id: string; + count: number; + success: boolean; +} + +// ==================== Cookie ==================== + +export interface CookieItem { + id: number; + batch_id: string; + account_id: number; + account_username: string; + assigned_to: number | null; + assigned_username: string | null; + created_at: string | null; + cookie: string; + cookie_preview: string; +} + +// ==================== Proxy ==================== + +export interface ProxyConfig { + enabled: boolean; + api_url: string; + http: string; + https: string; + whitelist_enabled: boolean; + whitelist_uid: string; + whitelist_ukey: string; +} + +export interface ProxyTestResult { + test_id: string; + success: boolean; +} + +// ==================== Log ==================== + +export interface HttpLogEntry { + timestamp: string; + ts: number; + category: string; + tag: string; + method: string; + url: string; + proxy: string | null; + request: { headers: Record; body: string }; + response: { status_code: number | null; headers: Record; body: string }; + duration_ms: number | null; + error: string | null; + level: string; +} + +export interface HttpLogListResult { + items: HttpLogEntry[]; + total: number; +} + +export interface HttpLogClearResult { + success: boolean; + cleared: number; +} + +// ==================== Permissions ==================== + +export interface PermissionsListResult { + permissions: Record; + role_permissions: Record; +} + +// ==================== API 定义 ==================== + export const authApi = { login: (username: string, password: string) => api.post('/auth/login', { username, password }), - me: () => api.get('/auth/me'), + me: () => api.get('/auth/me'), - logout: () => api.post('/auth/logout'), + logout: () => api.post('/auth/logout'), }; export const userApi = { @@ -33,55 +174,55 @@ export const userApi = { api.post('/users', data), update: (id: number, data: { password?: string; role?: string; is_active?: boolean; remark?: string; custom_permissions?: string[] | null }) => api.put(`/users/${id}`, data), - delete: (id: number) => api.delete(`/users/${id}`), - listPermissions: () => api.get('/users/permissions/list'), + delete: (id: number) => api.delete(`/users/${id}`), + listPermissions: () => api.get('/users/permissions/list'), }; export const accountApi = { list: (params?: { assigned_only?: boolean; tag?: string; has_cookie?: boolean }) => - api.get('/accounts', { params }), - import: (text: string) => api.post('/accounts/import', { text }), + api.get('/accounts', { params }), + import: (text: string) => api.post('/accounts/import', { text }), assign: (id: number, assigned_to: number | null) => - api.put(`/accounts/${id}/assign`, { assigned_to }), + api.put(`/accounts/${id}/assign`, { assigned_to }), batchAssign: (account_ids: number[], assigned_to: number | null) => - api.post('/accounts/batch-assign', { account_ids, assigned_to }), + api.post('/accounts/batch-assign', { account_ids, assigned_to }), assignmentsSummary: () => - api.get('/accounts/assignments/summary'), + api.get('/accounts/assignments/summary'), setTag: (id: number, tag: string) => - api.put(`/accounts/${id}/tag`, { tag }), + api.put(`/accounts/${id}/tag`, { tag }), batchTag: (account_ids: number[], tag: string) => - api.put('/accounts/batch-tag', { account_ids, tag }), + api.put('/accounts/batch-tag', { account_ids, tag }), listTags: () => api.get('/accounts/tags/list'), - delete: (id: number) => api.delete(`/accounts/${id}`), + delete: (id: number) => api.delete(`/accounts/${id}`), batchDelete: (account_ids: number[]) => - api.delete('/accounts/batch/delete', { params: { account_ids: account_ids.join(',') } }), + api.delete('/accounts/batch/delete', { params: { account_ids: account_ids.join(',') } }), }; export const loginApi = { createBatch: (account_ids: number[], max_geetest_retries?: number, concurrency?: number, max_proxy_retries?: number) => - api.post('/login/batch', { account_ids, max_geetest_retries, concurrency, max_proxy_retries }), + api.post('/login/batch', { account_ids, max_geetest_retries, concurrency, max_proxy_retries }), listTasks: (batch_id?: string) => - api.get('/login/tasks', { params: batch_id ? { batch_id } : {} }), - stop: (batch_id: string) => api.post(`/login/stop/${batch_id}`), - deleteTask: (id: number) => api.delete(`/login/tasks/${id}`), - deleteTasks: (ids: number[]) => api.delete(`/login/tasks`, { params: { task_ids: ids.join(',') } }), + api.get('/login/tasks', { params: batch_id ? { batch_id } : {} }), + stop: (batch_id: string) => api.post(`/login/stop/${batch_id}`), + deleteTask: (id: number) => api.delete(`/login/tasks/${id}`), + deleteTasks: (ids: number[]) => api.delete(`/login/tasks`, { params: { task_ids: ids.join(',') } }), }; export const cookieApi = { - list: () => api.get('/cookies'), + list: () => api.get('/cookies'), exportCsv: (format?: string) => api.get('/cookies/export', { responseType: 'blob', params: format ? { format } : {} }), - delete: (id: number) => api.delete(`/cookies/${id}`), + delete: (id: number) => api.delete(`/cookies/${id}`), }; export const proxyApi = { - get: () => api.get('/proxy'), - update: (data: any) => api.put('/proxy', data), - test: () => api.post('/proxy/test'), - testWhitelist: () => api.post('/proxy/whitelist/test'), + get: () => api.get('/proxy'), + update: (data: ProxyConfig) => api.put('/proxy', data), + test: () => api.post('/proxy/test'), + testWhitelist: () => api.post('/proxy/whitelist/test'), }; export const logApi = { listHttp: (params?: { limit?: number; offset?: number; category?: string; level?: string; keyword?: string }) => - api.get('/logs/http', { params }), - clearHttp: () => api.delete('/logs/http'), + api.get('/logs/http', { params }), + clearHttp: () => api.delete('/logs/http'), }; diff --git a/web/frontend/src/layouts/MainLayout.tsx b/web/frontend/src/layouts/MainLayout.tsx index 0da85d9..1238c58 100644 --- a/web/frontend/src/layouts/MainLayout.tsx +++ b/web/frontend/src/layouts/MainLayout.tsx @@ -39,7 +39,7 @@ export default function MainLayout({ onLogout }: { onLogout?: () => void }) { if (!user) return null; - const menuItems: any[] = []; + const menuItems: { key: string; label: string; icon: React.ReactNode }[] = []; // Dashboard - 所有人可见 menuItems.push({ key: '/', label: '概览', icon: }); diff --git a/web/frontend/src/pages/AccountsPage.tsx b/web/frontend/src/pages/AccountsPage.tsx index db2712d..84c43d8 100644 --- a/web/frontend/src/pages/AccountsPage.tsx +++ b/web/frontend/src/pages/AccountsPage.tsx @@ -4,8 +4,9 @@ import { Row, Col, Card, Statistic, } from 'antd'; import { ImportOutlined, DeleteOutlined, TagOutlined, FilterOutlined } from '@ant-design/icons'; -import { accountApi, userApi } from '../api/modules'; +import { accountApi, userApi, type AccountItem, type UserInfo } from '../api/modules'; import { getUser, hasPerm } from '../store/auth'; +import { getErrorMessage } from '../utils/error'; const { TextArea } = Input; const { Text } = Typography; @@ -15,8 +16,8 @@ const TAG_COLORS = [ ]; export default function AccountsPage() { - const [accounts, setAccounts] = useState([]); - const [users, setUsers] = useState([]); + const [accounts, setAccounts] = useState([]); + const [users, setUsers] = useState([]); const [tags, setTags] = useState([]); const [loading, setLoading] = useState(false); const [importOpen, setImportOpen] = useState(false); @@ -36,12 +37,12 @@ export default function AccountsPage() { const loadAccounts = async () => { setLoading(true); try { - const params: any = {}; + const params: { tag?: string } = {}; if (tagFilter) params.tag = tagFilter; const data = await accountApi.list(params); setAccounts(data); - } catch (e: any) { - message.error(e.message); + } catch (e: unknown) { + message.error(getErrorMessage(e)); } finally { setLoading(false); } @@ -50,7 +51,7 @@ export default function AccountsPage() { const loadUsers = async () => { try { const data = await userApi.list(); - setUsers(data.filter((u: any) => u.role === 'support')); + setUsers(data.filter((u) => u.role === 'support')); } catch {} }; @@ -92,8 +93,8 @@ export default function AccountsPage() { setImportText(''); loadAccounts(); loadTags(); - } catch (e: any) { - message.error(e.message); + } catch (e: unknown) { + message.error(getErrorMessage(e)); } finally { setImporting(false); } @@ -104,8 +105,8 @@ export default function AccountsPage() { await accountApi.assign(accountId, assignedTo); message.success('已分配'); loadAccounts(); - } catch (e: any) { - message.error(e.message); + } catch (e: unknown) { + message.error(getErrorMessage(e)); } }; @@ -115,8 +116,8 @@ export default function AccountsPage() { message.success('标签已更新'); loadAccounts(); loadTags(); - } catch (e: any) { - message.error(e.message); + } catch (e: unknown) { + message.error(getErrorMessage(e)); } }; @@ -133,8 +134,8 @@ export default function AccountsPage() { setSelectedRowKeys([]); loadAccounts(); loadTags(); - } catch (e: any) { - message.error(e.message); + } catch (e: unknown) { + message.error(getErrorMessage(e)); } }; @@ -145,8 +146,8 @@ export default function AccountsPage() { setSelectedRowKeys((prev) => prev.filter((k) => k !== id)); loadAccounts(); loadTags(); - } catch (e: any) { - message.error(e.message); + } catch (e: unknown) { + message.error(getErrorMessage(e)); } }; @@ -161,19 +162,19 @@ export default function AccountsPage() { setSelectedRowKeys([]); loadAccounts(); loadTags(); - } catch (e: any) { - message.error(e.message); + } catch (e: unknown) { + message.error(getErrorMessage(e)); } }; - const columns: any[] = [ + const columns = [ { title: 'ID', dataIndex: 'id', width: 60 }, { title: '用户名', dataIndex: 'username' }, { title: '标签', dataIndex: 'tag', width: 120, - render: (tag: string, record: any) => { + render: (tag: string, record: AccountItem) => { if (!tag) { if (canImport) { return ( @@ -224,7 +225,7 @@ export default function AccountsPage() { columns.push({ title: '分配给', dataIndex: 'assigned_username', - render: (_: any, record: any) => { + render: (_: unknown, record: AccountItem) => { if (canAssign) { return (