重构Cookie监控系统:添加Web管理后台、Vue3前端界面、Cookie Cloud参数支持、5秒倒计时自动刷新、专业UI配色
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
"""Cookie监控管理系统 - 检测客户端"""
|
||||
import time
|
||||
import requests
|
||||
from datetime import datetime
|
||||
from typing import Dict, List
|
||||
from DrissionPage import ChromiumPage, ChromiumOptions
|
||||
from utils.config import BACKEND_URL, BROWSER_TYPE, HEADLESS_MODE
|
||||
@@ -0,0 +1,512 @@
|
||||
"""
|
||||
Cookie监控管理系统 - 检测客户端
|
||||
|
||||
前端启动时,不需要登录后端,直接读取所有用户需要登录的网站,
|
||||
然后分用户、分网站登录。
|
||||
"""
|
||||
import time
|
||||
import requests
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional
|
||||
from DrissionPage import ChromiumPage, ChromiumOptions
|
||||
from config.config import BACKEND_URL, BROWSER_TYPE, HEADLESS_MODE, PAGE_LOAD_TIMEOUT
|
||||
|
||||
|
||||
class DetectionClient:
|
||||
"""检测客户端"""
|
||||
|
||||
def __init__(self):
|
||||
self.backend_url = BACKEND_URL
|
||||
self.browser = None
|
||||
self.failed_websites = [] # 记录失败的网站
|
||||
|
||||
def init_browser(self):
|
||||
"""初始化浏览器"""
|
||||
co = ChromiumOptions()
|
||||
if BROWSER_TYPE == 'edge':
|
||||
co.set_browser_path(r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe")
|
||||
co.set_argument('--no-sandbox')
|
||||
co.set_argument('--disable-dev-shm-usage')
|
||||
if HEADLESS_MODE:
|
||||
co.set_argument('--headless')
|
||||
self.browser = ChromiumPage(addr_or_opts=co)
|
||||
print("浏览器初始化成功")
|
||||
|
||||
def close_browser(self):
|
||||
"""关闭浏览器"""
|
||||
if self.browser:
|
||||
try:
|
||||
self.browser.quit()
|
||||
except Exception as e:
|
||||
print(f"关闭浏览器失败: {e}")
|
||||
finally:
|
||||
self.browser = None
|
||||
|
||||
def get_all_users(self) -> List[Dict]:
|
||||
"""获取所有用户"""
|
||||
try:
|
||||
response = requests.get(f"{self.backend_url}/api/users")
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except requests.RequestException as e:
|
||||
print(f"获取用户失败: {e}")
|
||||
return []
|
||||
|
||||
def get_user_websites(self, user_id: int) -> List[Dict]:
|
||||
"""获取用户的网站列表"""
|
||||
try:
|
||||
response = requests.get(f"{self.backend_url}/api/websites?user_id={user_id}")
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except requests.RequestException as e:
|
||||
print(f"获取用户 {user_id} 的网站失败: {e}")
|
||||
return []
|
||||
|
||||
def get_website_cookies(self, website_id: int) -> List[Dict]:
|
||||
"""获取网站的cookie"""
|
||||
try:
|
||||
response = requests.get(f"{self.backend_url}/api/cookies/websites/{website_id}")
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except requests.RequestException as e:
|
||||
print(f"获取网站 {website_id} 的cookie失败: {e}")
|
||||
return []
|
||||
|
||||
def update_website_cookies(self, website_id: int, cookies: List[Dict]):
|
||||
"""更新网站的cookie"""
|
||||
try:
|
||||
response = requests.put(
|
||||
f"{self.backend_url}/api/cookies/websites/{website_id}",
|
||||
json={'cookies': cookies}
|
||||
)
|
||||
response.raise_for_status()
|
||||
print(f"网站 {website_id} 的cookie已更新")
|
||||
except requests.RequestException as e:
|
||||
print(f"更新网站 {website_id} 的cookie失败: {e}")
|
||||
|
||||
def save_detection_result(self, website_id: int, status: int, response_time: float,
|
||||
http_status: int, message: str):
|
||||
"""保存检测结果"""
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{self.backend_url}/api/detections",
|
||||
json={
|
||||
'website_id': website_id,
|
||||
'status': status,
|
||||
'response_time': response_time,
|
||||
'http_status': http_status,
|
||||
'message': message
|
||||
}
|
||||
)
|
||||
response.raise_for_status()
|
||||
except requests.RequestException as e:
|
||||
print(f"保存检测结果失败: {e}")
|
||||
|
||||
def send_notification(self, user_id: int, website_id: int, title: str, content: str):
|
||||
"""发送通知"""
|
||||
try:
|
||||
response = requests.post(
|
||||
f"{self.backend_url}/api/notifications",
|
||||
json={
|
||||
'user_id': user_id,
|
||||
'website_id': website_id,
|
||||
'title': title,
|
||||
'content': content
|
||||
}
|
||||
)
|
||||
response.raise_for_status()
|
||||
print(f"通知已发送")
|
||||
except requests.RequestException as e:
|
||||
print(f"发送通知失败: {e}")
|
||||
|
||||
def import_cookies_to_browser(self, cookies: List[Dict], domain: str) -> int:
|
||||
"""导入cookies到浏览器"""
|
||||
if not self.browser or not cookies:
|
||||
return 0
|
||||
|
||||
success_count = 0
|
||||
for cookie in cookies:
|
||||
try:
|
||||
if not cookie.get('name') or not cookie.get('value'):
|
||||
continue
|
||||
|
||||
cookie_dict = {
|
||||
'name': cookie.get('name'),
|
||||
'value': cookie.get('value'),
|
||||
'domain': cookie.get('domain', domain),
|
||||
'path': cookie.get('path', '/')
|
||||
}
|
||||
|
||||
self.browser.set.cookies(cookie_dict)
|
||||
success_count += 1
|
||||
except Exception as e:
|
||||
print(f"导入cookie失败: {e}")
|
||||
|
||||
return success_count
|
||||
|
||||
def verify_login(self, url: str, check_selector: str = "", success_text: str = "") -> bool:
|
||||
"""
|
||||
验证登录状态
|
||||
|
||||
Args:
|
||||
url: 网站地址
|
||||
check_selector: 登录检测选择器
|
||||
success_text: 登录成功时显示的文本
|
||||
|
||||
Returns:
|
||||
是否已登录
|
||||
"""
|
||||
if not self.browser:
|
||||
return False
|
||||
|
||||
try:
|
||||
self.browser.get(url)
|
||||
self.browser.wait.doc_loaded(timeout=PAGE_LOAD_TIMEOUT)
|
||||
time.sleep(3)
|
||||
|
||||
# 刷新页面几次确保登录状态
|
||||
for i in range(3):
|
||||
time.sleep(3)
|
||||
self.browser.refresh()
|
||||
self.browser.wait.doc_loaded(timeout=PAGE_LOAD_TIMEOUT)
|
||||
time.sleep(3)
|
||||
|
||||
if not check_selector and not success_text:
|
||||
return False
|
||||
|
||||
# 检查选择器
|
||||
if check_selector:
|
||||
try:
|
||||
element = self.browser.ele(check_selector, timeout=10)
|
||||
if element:
|
||||
element_text = element.text or ""
|
||||
if not success_text:
|
||||
return True
|
||||
if success_text in element_text:
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
# 检查成功文本
|
||||
if success_text:
|
||||
try:
|
||||
page_text = self.browser.html or ""
|
||||
if success_text in page_text:
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"验证登录状态失败: {e}")
|
||||
return False
|
||||
|
||||
def get_current_cookies(self) -> List[Dict]:
|
||||
"""获取当前浏览器的所有cookies"""
|
||||
if not self.browser:
|
||||
return []
|
||||
|
||||
try:
|
||||
return self.browser.cookies()
|
||||
except Exception as e:
|
||||
print(f"获取cookies失败: {e}")
|
||||
return []
|
||||
|
||||
def check_website(self, user: Dict, website: Dict) -> bool:
|
||||
"""
|
||||
检测单个网站
|
||||
|
||||
Args:
|
||||
user: 用户信息
|
||||
website: 网站信息
|
||||
|
||||
Returns:
|
||||
是否登录成功
|
||||
"""
|
||||
website_id = website.get('id')
|
||||
website_name = website.get('name')
|
||||
url = website.get('url')
|
||||
check_selector = website.get('login_check_selector', '')
|
||||
success_text = website.get('success_text', '')
|
||||
|
||||
print(f"\n检测网站: {website_name} ({url})")
|
||||
print(f"用户: {user.get('name')}")
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
try:
|
||||
# 获取网站的cookie
|
||||
cookies = self.get_website_cookies(website_id)
|
||||
if not cookies:
|
||||
print(f" 警告: 未找到网站 {website_name} 的cookie")
|
||||
self.save_detection_result(
|
||||
website_id=website_id,
|
||||
status=0,
|
||||
response_time=time.time() - start_time,
|
||||
http_status=0,
|
||||
message="未找到cookie"
|
||||
)
|
||||
return False
|
||||
|
||||
# 导入cookie到浏览器
|
||||
from urllib.parse import urlparse
|
||||
parsed = urlparse(url)
|
||||
domain = parsed.netloc
|
||||
self.import_cookies_to_browser(cookies, domain)
|
||||
|
||||
# 访问网站
|
||||
self.browser.get(url)
|
||||
self.browser.wait.doc_loaded(timeout=PAGE_LOAD_TIMEOUT)
|
||||
time.sleep(3)
|
||||
|
||||
# 验证登录状态,如果未登录则以30秒为周期连续检测6次
|
||||
login_success = self._check_login_with_retry(url, check_selector, success_text)
|
||||
response_time = time.time() - start_time
|
||||
|
||||
if login_success:
|
||||
print(f" ✓ 登录验证成功")
|
||||
|
||||
# 获取最新cookie并保存到后台
|
||||
current_cookies = self.get_current_cookies()
|
||||
if current_cookies:
|
||||
self.update_website_cookies(website_id, current_cookies)
|
||||
print(f" 已更新cookie ({len(current_cookies)} 个)")
|
||||
|
||||
# 保存检测结果
|
||||
self.save_detection_result(
|
||||
website_id=website_id,
|
||||
status=1,
|
||||
response_time=response_time,
|
||||
http_status=200,
|
||||
message="登录验证成功"
|
||||
)
|
||||
return True
|
||||
else:
|
||||
print(f" ✗ 登录验证失败(连续6次检测未登录)")
|
||||
|
||||
# 记录失败信息
|
||||
self.failed_websites.append({
|
||||
'user': user,
|
||||
'website': website,
|
||||
'reason': '登录验证失败(连续6次检测未登录)'
|
||||
})
|
||||
|
||||
# 保存检测结果
|
||||
self.save_detection_result(
|
||||
website_id=website_id,
|
||||
status=0,
|
||||
response_time=response_time,
|
||||
http_status=200,
|
||||
message="登录验证失败(连续6次检测未登录)"
|
||||
)
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
response_time = time.time() - start_time
|
||||
print(f" ✗ 检测异常: {e}")
|
||||
|
||||
# 记录失败信息
|
||||
self.failed_websites.append({
|
||||
'user': user,
|
||||
'website': website,
|
||||
'reason': str(e)
|
||||
})
|
||||
|
||||
# 保存检测结果
|
||||
self.save_detection_result(
|
||||
website_id=website_id,
|
||||
status=0,
|
||||
response_time=response_time,
|
||||
http_status=0,
|
||||
message=f"检测异常: {e}"
|
||||
)
|
||||
return False
|
||||
|
||||
def _check_login_with_retry(self, url: str, check_selector: str, success_text: str,
|
||||
retry_count: int = 6, retry_interval: int = 30) -> bool:
|
||||
"""
|
||||
检查登录状态,如果未登录则重复检测
|
||||
|
||||
Args:
|
||||
url: 网站地址
|
||||
check_selector: 登录检测选择器
|
||||
success_text: 登录成功文本
|
||||
retry_count: 最大重试次数
|
||||
retry_interval: 重试间隔(秒)
|
||||
|
||||
Returns:
|
||||
是否登录成功
|
||||
"""
|
||||
# 第一次检测
|
||||
if self._is_logged_in(check_selector, success_text):
|
||||
return True
|
||||
|
||||
print(f" 首次检测未登录,开始周期性检测(最多{retry_count}次,间隔{retry_interval}秒)")
|
||||
|
||||
for i in range(1, retry_count + 1):
|
||||
print(f" 第 {i}/{retry_count} 次检测...")
|
||||
time.sleep(retry_interval)
|
||||
|
||||
# 刷新页面
|
||||
self.browser.refresh()
|
||||
self.browser.wait.doc_loaded(timeout=PAGE_LOAD_TIMEOUT)
|
||||
time.sleep(3)
|
||||
|
||||
if self._is_logged_in(check_selector, success_text):
|
||||
print(f" 第 {i} 次检测登录成功")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _is_logged_in(self, check_selector: str, success_text: str) -> bool:
|
||||
"""
|
||||
判断是否已登录
|
||||
|
||||
Returns:
|
||||
是否已登录
|
||||
"""
|
||||
if not check_selector and not success_text:
|
||||
return False
|
||||
|
||||
# 检查选择器
|
||||
if check_selector:
|
||||
try:
|
||||
element = self.browser.ele(check_selector, timeout=10)
|
||||
if element:
|
||||
element_text = element.text or ""
|
||||
if not success_text:
|
||||
return True
|
||||
if success_text in element_text:
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
# 检查成功文本
|
||||
if success_text:
|
||||
try:
|
||||
page_text = self.browser.html or ""
|
||||
if success_text in page_text:
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
def send_failed_notifications(self):
|
||||
"""发送失败网站的通知"""
|
||||
if not self.failed_websites:
|
||||
return
|
||||
|
||||
print(f"\n发现 {len(self.failed_websites)} 个失败的网站,发送通知")
|
||||
|
||||
# 按用户分组
|
||||
user_notifications = {}
|
||||
for failed in self.failed_websites:
|
||||
user_id = failed['user'].get('id')
|
||||
if user_id not in user_notifications:
|
||||
user_notifications[user_id] = {
|
||||
'user': failed['user'],
|
||||
'websites': []
|
||||
}
|
||||
user_notifications[user_id]['websites'].append(failed)
|
||||
|
||||
# 为每个用户发送通知
|
||||
for user_id, notif_data in user_notifications.items():
|
||||
user = notif_data['user']
|
||||
websites = notif_data['websites']
|
||||
|
||||
# 构建通知内容
|
||||
content_lines = [
|
||||
f"**{user.get('name')} Cookie登录报告**",
|
||||
f"",
|
||||
f"检测时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
|
||||
f"失败数量: {len(websites)} 个网站",
|
||||
f"",
|
||||
"失败详情:"
|
||||
]
|
||||
|
||||
for failed in websites:
|
||||
website = failed['website']
|
||||
reason = failed['reason']
|
||||
content_lines.append(f"- {website.get('name')}: {reason}")
|
||||
|
||||
content = "\n".join(content_lines)
|
||||
|
||||
# 发送通知
|
||||
if websites:
|
||||
self.send_notification(
|
||||
user_id=user_id,
|
||||
website_id=websites[0]['website'].get('id'),
|
||||
title=f"Cookie登录失败 ({len(websites)} 个网站)",
|
||||
content=content
|
||||
)
|
||||
|
||||
def run_detection_cycle(self):
|
||||
"""执行一轮检测"""
|
||||
print(f"\n{'='*50}")
|
||||
print(f"开始执行检测任务 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print(f"{'='*50}")
|
||||
|
||||
self.failed_websites = []
|
||||
|
||||
# 初始化浏览器
|
||||
self.init_browser()
|
||||
|
||||
try:
|
||||
# 获取所有用户
|
||||
users = self.get_all_users()
|
||||
if not users:
|
||||
print("未找到用户,跳过检测")
|
||||
return
|
||||
|
||||
print(f"找到 {len(users)} 个用户")
|
||||
|
||||
# 分用户处理
|
||||
for user in users:
|
||||
print(f"\n处理用户: {user.get('name')}")
|
||||
|
||||
# 获取用户的网站
|
||||
websites = self.get_user_websites(user.get('id'))
|
||||
if not websites:
|
||||
print(f" 用户 {user.get('name')} 没有网站,跳过")
|
||||
continue
|
||||
|
||||
print(f" 找到 {len(websites)} 个网站")
|
||||
|
||||
# 分网站处理
|
||||
for website in websites:
|
||||
if website.get('status', 1) != 1:
|
||||
print(f" 网站 {website.get('name')} 已禁用,跳过")
|
||||
continue
|
||||
|
||||
self.check_website(user, website)
|
||||
|
||||
# 发送失败通知
|
||||
self.send_failed_notifications()
|
||||
|
||||
except Exception as e:
|
||||
print(f"检测任务执行异常: {e}")
|
||||
finally:
|
||||
self.close_browser()
|
||||
|
||||
print(f"\n{'='*50}")
|
||||
print(f"检测任务完成 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print(f"{'='*50}")
|
||||
|
||||
def run(self):
|
||||
"""运行检测客户端"""
|
||||
print("Cookie监控检测客户端启动")
|
||||
|
||||
# 执行一轮检测
|
||||
self.run_detection_cycle()
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
client = DetectionClient()
|
||||
client.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,15 @@
|
||||
"""
|
||||
配置文件
|
||||
"""
|
||||
# 后台服务地址
|
||||
BACKEND_URL = "http://localhost:5000"
|
||||
|
||||
# 浏览器配置
|
||||
BROWSER_TYPE = "edge" # edge 或 chrome
|
||||
HEADLESS_MODE = False # 是否无头模式
|
||||
|
||||
# 检测间隔(秒)
|
||||
CHECK_INTERVAL = 30
|
||||
|
||||
# 页面加载超时时间(秒)
|
||||
PAGE_LOAD_TIMEOUT = 30
|
||||
Reference in New Issue
Block a user