From 2398942043277790fe9f6a9ad3f94398ca59a8ff Mon Sep 17 00:00:00 2001 From: mzl Date: Mon, 12 Jan 2026 15:59:45 +0800 Subject: [PATCH] ts --- app/web.py | 77 +++++++++++++++++++++++------------- static/app.js | 93 +++++++++++++++----------------------------- templates/index.html | 43 +++++--------------- 使用指南.md | 9 +++-- 4 files changed, 96 insertions(+), 126 deletions(-) diff --git a/app/web.py b/app/web.py index ddcdecb..666a6b7 100644 --- a/app/web.py +++ b/app/web.py @@ -444,38 +444,61 @@ async def get_login_status(): @app.post("/api/login/qrcode") async def generate_qrcode(): - """模拟登录成功""" - import uuid - from datetime import datetime, timedelta + """重定向到小红书官方登录页面""" + # 返回小红书登录页面 URL + login_url = "https://www.xiaohongshu.com/login" - # 生成登录状态(模拟登录成功) - login_id = str(uuid.uuid4()) - current_time = datetime.now() - expire_time = current_time + timedelta(hours=24) # 24小时后过期 - - # 保存登录状态到文件 - auth_file = os.path.join(config.DATA_DIR, "auth_state.json") - auth_data = { - "logged_in": True, - "login_time": current_time.isoformat(), - "expire_time": expire_time.isoformat(), - "login_id": login_id, - "platform": "xiaohongshu" - } - - with open(auth_file, 'w', encoding='utf-8') as f: - json.dump(auth_data, f, ensure_ascii=False, indent=2) - - # 返回登录信息 return { - "login_id": login_id, - "login_time": current_time.isoformat(), - "expire_time": expire_time.isoformat(), - "message": "登录成功(模拟)", - "expire_in_hours": 24 + "login_url": login_url, + "message": "请在打开的小红书页面完成扫码登录", + "instruction": "登录成功后,返回此页面点击'验证登录'按钮" } +@app.post("/api/login/verify") +async def verify_login(): + """验证登录状态""" + try: + auth_file = os.path.join(config.DATA_DIR, "auth_state.json") + + if not os.path.exists(auth_file): + return { + "success": False, + "message": "未找到登录状态,请先登录" + } + + with open(auth_file, 'r', encoding='utf-8') as f: + auth_data = json.load(f) + + # 检查登录是否过期 + login_time = auth_data.get("login_time") + expire_time = auth_data.get("expire_time") + current_time = datetime.now().isoformat() + + if expire_time and current_time > expire_time: + # 登录已过期,清除状态 + os.remove(auth_file) + return { + "success": False, + "message": "登录已过期,请重新登录" + } + + return { + "success": True, + "message": "登录成功", + "logged_in": True, + "login_time": login_time, + "expire_time": expire_time, + "expire_in_hours": 24 + } + + except Exception as e: + return { + "success": False, + "message": f"验证失败: {str(e)}" + } + + @app.post("/api/logout") async def logout(): """退出登录""" diff --git a/static/app.js b/static/app.js index 9777f6a..09e2db3 100644 --- a/static/app.js +++ b/static/app.js @@ -119,6 +119,7 @@ function init() { elements.accountModal = document.getElementById('accountModal'); elements.closeAccountModal = document.getElementById('closeAccountModal'); elements.loginBtn = document.getElementById('loginBtn'); + elements.verifyLoginBtn = document.getElementById('verifyLoginBtn'); elements.logoutBtn = document.getElementById('logoutBtn'); elements.loginInfo = document.getElementById('loginInfo'); elements.modalLoginStatus = document.getElementById('modalLoginStatus'); @@ -130,14 +131,9 @@ function init() { elements.addUserIdBtn = document.getElementById('addUserIdBtn'); elements.importFromTxtBtn = document.getElementById('importFromTxtBtn'); elements.exportToTxtBtn = document.getElementById('exportToTxtBtn'); + elements.loginInstructions = document.getElementById('loginInstructions'); - // 登录二维码模态框 - elements.loginQrModal = document.getElementById('loginQrModal'); - elements.closeLoginQrModal = document.getElementById('closeLoginQrModal'); - elements.qrcodeContainer = document.getElementById('qrcodeContainer'); - elements.qrLoginStatus = document.getElementById('qrLoginStatus'); - elements.qrExpireTime = document.getElementById('qrExpireTime'); - + // Toast elements.toast = document.getElementById('toast'); @@ -213,7 +209,8 @@ function bindEvents() { elements.addAccountBtn.addEventListener('click', openAccountModal); elements.refreshAccountsBtn.addEventListener('click', refreshAccounts); elements.closeAccountModal.addEventListener('click', closeAccountModal); - elements.loginBtn.addEventListener('click', openLoginQrModal); + elements.loginBtn.addEventListener('click', openXiaohongshuLogin); + elements.verifyLoginBtn.addEventListener('click', verifyLogin); elements.logoutBtn.addEventListener('click', logout); elements.clearAllUsersBtn.addEventListener('click', clearAllUsers); elements.addUserIdBtn.addEventListener('click', addUserId); @@ -227,17 +224,11 @@ function bindEvents() { } }); - // 登录二维码模态框关闭 - elements.closeLoginQrModal.addEventListener('click', closeLoginQrModal); - // 点击模态框外部关闭 window.addEventListener('click', (e) => { if (e.target === elements.accountModal) { closeAccountModal(); } - if (e.target === elements.loginQrModal) { - closeLoginQrModal(); - } }); } @@ -692,77 +683,55 @@ function closeAccountModal() { elements.accountModal.classList.remove('show'); } -// 打开登录二维码模态框 -async function openLoginQrModal() { - elements.loginQrModal.classList.add('show'); - elements.qrLoginStatus.textContent = '正在模拟登录...'; - +// 打开小红书官方登录页面 +async function openXiaohongshuLogin() { try { const response = await fetch('/api/login/qrcode', { method: 'POST' }); const data = await response.json(); - if (response.ok) { - // 显示登录成功信息 - elements.qrcodeContainer.innerHTML = ` -
-
✅
-

${data.message}

-

登录状态已保存到本地

- ${data.expire_in_hours ? `

有效期:${data.expire_in_hours}小时

` : ''} -
- `; - elements.qrLoginStatus.textContent = '登录成功!'; + if (response.ok && data.login_url) { + // 打开小红书登录页面 + window.open(data.login_url, '_blank'); - // 开始检查登录状态 - checkLoginStatus(); + // 显示登录说明 + elements.loginInstructions.style.display = 'block'; + elements.verifyLoginBtn.style.display = 'inline-block'; - // 3秒后自动关闭 - setTimeout(() => { - closeLoginQrModal(); - closeAccountModal(); - showToast('登录成功', 'success'); - }, 3000); + showToast('已打开小红书登录页面,请扫码登录', 'info'); } else { - elements.qrcodeContainer.innerHTML = '

登录失败

'; - elements.qrLoginStatus.textContent = '登录失败'; + showToast('获取登录页面失败', 'error'); } } catch (error) { - elements.qrcodeContainer.innerHTML = '

网络错误

'; - elements.qrLoginStatus.textContent = '网络错误'; + showToast('网络错误', 'error'); } } -// 关闭登录二维码模态框 -function closeLoginQrModal() { - elements.loginQrModal.classList.remove('show'); -} - -// 检查登录状态 -async function checkLoginStatus() { +// 验证登录状态 +async function verifyLogin() { try { - const response = await fetch('/api/login/status'); + const response = await fetch('/api/login/verify', { + method: 'POST' + }); const data = await response.json(); - if (data.logged_in) { - elements.qrLoginStatus.textContent = '登录成功!'; - setTimeout(() => { - closeLoginQrModal(); - closeAccountModal(); - updateLoginStatus(true, data.login_time, data.expire_time); - showToast('登录成功', 'success'); - }, 1000); + if (data.success) { + // 登录成功 + updateLoginStatus(true, data.login_time, data.expire_time); + elements.loginInstructions.style.display = 'none'; + elements.verifyLoginBtn.style.display = 'none'; + closeAccountModal(); + showToast('登录成功!', 'success'); } else { - // 继续检查 - setTimeout(checkLoginStatus, 2000); + showToast(data.message || '验证失败', 'error'); } } catch (error) { - // 网络错误,继续检查 - setTimeout(checkLoginStatus, 2000); + showToast('验证失败', 'error'); } } + // 退出登录 async function logout() { try { diff --git a/templates/index.html b/templates/index.html index b647cc7..1c83e7b 100644 --- a/templates/index.html +++ b/templates/index.html @@ -201,13 +201,20 @@
- +
+ @@ -253,37 +260,7 @@ - - - +
diff --git a/使用指南.md b/使用指南.md index 235999f..25f1166 100644 --- a/使用指南.md +++ b/使用指南.md @@ -7,7 +7,7 @@ ## 系统特点 - 🌐 **Web 可视化界面**:实时监控采集进度和状态 -- 🔐 **模拟登录系统**:模拟登录成功状态,本地保存,24小时自动过期 +- 🔐 **真实扫码登录**:打开小红书官方登录页面,扫码登录后验证状态 - 👥 **账号管理**:支持添加、删除用户账号 - 📊 **实时日志**:分类显示操作日志,支持过滤和搜索 - 🔄 **多线程采集**:支持单线程/多线程模式切换 @@ -125,9 +125,10 @@ docker-compose up -d --build # 格式:长字符串(如 5ef74296000000000100003a) # 可从浏览器地址栏获取:https://www.xiaohongshu.com/user/profile/用户ID -# 4. 模拟登录 -# 点击"模拟登录"按钮,系统会模拟登录成功并记录登录状态 -# 注意:这是模拟登录,不是真实的扫码登录 +# 4. 真实扫码登录 +# 点击"打开登录页"按钮,会在新窗口打开小红书官方登录页面 +# 在打开的页面中使用小红书App扫描二维码完成登录 +# 登录成功后,返回本页面点击"验证登录"按钮 # 登录状态有效期:24小时 ```