import asyncio import os import threading import time from flask import Blueprint, request, jsonify, render_template from server.database import get_db from server.services.auth_service import ( get_auth_path, has_auth, login_with_password, login_with_sms ) bp = Blueprint('accounts', __name__, url_prefix='/accounts') @bp.route('/') def list_accounts(): db = get_db() accounts = db.execute('SELECT * FROM accounts ORDER BY id DESC').fetchall() db.close() return render_template('accounts.html', accounts=accounts) @bp.route('/add', methods=['POST']) def add_account(): name = request.form.get('name', '').strip() phone = request.form.get('phone', '').strip() password = request.form.get('password', '').strip() if not name or not phone: return jsonify(success=False, msg='请填写名称和手机号'), 400 db = get_db() cursor = db.execute( 'INSERT INTO accounts (name, phone, password, auth_file, login_msg) VALUES (?, ?, ?, ?, ?)', (name, phone, password, '', '待登录') ) account_id = cursor.lastrowid auth_file = get_auth_path(account_id) db.execute('UPDATE accounts SET auth_file = ? WHERE id = ?', (auth_file, account_id)) db.commit() db.close() return jsonify(success=True, id=account_id) @bp.route('/delete/', methods=['POST']) def delete_account(account_id): db = get_db() db.execute('DELETE FROM tasks WHERE account_id = ?', (account_id,)) db.execute('DELETE FROM accounts WHERE id = ?', (account_id,)) db.commit() db.close() path = get_auth_path(account_id) if os.path.exists(path): os.remove(path) return jsonify(success=True) @bp.route('/login/', methods=['POST']) def do_login(account_id): """密码登录(自动,无需人参与)""" db = get_db() account = db.execute('SELECT * FROM accounts WHERE id = ?', (account_id,)).fetchone() db.close() if not account: return jsonify(success=False, msg='账号不存在'), 404 phone = account['phone'] password = account['password'] if not phone or not password: return jsonify(success=False, msg='该账号未设置手机号或密码'), 400 # 标记为登录中 db = get_db() db.execute( "UPDATE accounts SET is_logged_in = 0, login_msg = '登录中...', " "updated_at = datetime('now','localtime') WHERE id = ?", (account_id,) ) db.commit() db.close() # 后台异步登录 _start_bg_login(account_id, phone, password) return jsonify(success=True, msg='登录中...') @bp.route('/login_sms/', methods=['POST']) def do_sms_login(account_id): """ 短信验证码登录(需要人机交互) 会弹出浏览器窗口,需要人拖滑块 + 输入验证码。 """ db = get_db() account = db.execute('SELECT * FROM accounts WHERE id = ?', (account_id,)).fetchone() db.close() if not account: return jsonify(success=False, msg='账号不存在'), 404 phone = account['phone'] if not phone: return jsonify(success=False, msg='该账号未设置手机号'), 400 # 标记为等待交互 db = get_db() db.execute( "UPDATE accounts SET is_logged_in = 0, login_msg = '等待人机交互...', " "updated_at = datetime('now','localtime') WHERE id = ?", (account_id,) ) db.commit() db.close() # 后台启动短信登录(会弹出浏览器窗口) _start_bg_sms_login(account_id, phone) return jsonify(success=True, msg='已启动短信登录,请在弹出的浏览器中完成滑块验证,并在终端输入验证码') @bp.route('/status/') def get_status(account_id): """轮询账号登录状态""" db = get_db() account = db.execute( 'SELECT is_logged_in, login_msg FROM accounts WHERE id = ?', (account_id,) ).fetchone() db.close() if not account: return jsonify(is_logged_in=False, login_msg='账号不存在', done=True) msg = account['login_msg'] or '' is_logged_in = bool(account['is_logged_in']) done = msg not in ('登录中...', '等待人机交互...') return jsonify(is_logged_in=is_logged_in, login_msg=msg, done=done) def _start_bg_login(account_id, phone, password): """后台线程执行密码登录""" def _run(): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) try: ok, msg = loop.run_until_complete( login_with_password(account_id, phone, password) ) except Exception as e: ok, msg = False, str(e) finally: loop.close() db = get_db() if ok: db.execute( "UPDATE accounts SET is_logged_in = 1, login_msg = '登录成功', " "updated_at = datetime('now','localtime') WHERE id = ?", (account_id,) ) else: db.execute( "UPDATE accounts SET is_logged_in = 0, login_msg = ?, " "updated_at = datetime('now','localtime') WHERE id = ?", (msg, account_id) ) db.commit() db.close() t = threading.Thread(target=_run, daemon=True) t.start() def _start_bg_sms_login(account_id, phone): """后台线程执行短信登录""" def _run(): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) try: ok, msg = loop.run_until_complete( login_with_sms(phone, account_id=account_id) ) except Exception as e: ok, msg = False, str(e) finally: loop.close() db = get_db() if ok: db.execute( "UPDATE accounts SET is_logged_in = 1, login_msg = '登录成功', " "updated_at = datetime('now','localtime') WHERE id = ?", (account_id,) ) else: db.execute( "UPDATE accounts SET is_logged_in = 0, login_msg = ?, " "updated_at = datetime('now','localtime') WHERE id = ?", (msg, account_id) ) db.commit() db.close() t = threading.Thread(target=_run, daemon=True) t.start()