Files
ai_mip/app.py

355 lines
12 KiB
Python
Raw Normal View History

2026-01-13 18:59:26 +08:00
from flask import Flask, request, jsonify, send_from_directory, redirect
from flask_cors import CORS
from loguru import logger
import sys
from pathlib import Path
from config import Config
from scheduler import ClickScheduler
# 配置日志
Config.ensure_dirs()
logger.remove()
logger.add(
sys.stdout,
format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
level="INFO"
)
logger.add(
Path(Config.LOG_DIR) / "mip_ad_service_{time}.log",
rotation="500 MB",
retention="10 days",
encoding="utf-8",
level="DEBUG"
)
# 创建Flask应用
app = Flask(__name__, static_folder='static', static_url_path='')
CORS(app)
# 创建调度器实例
scheduler = ClickScheduler()
@app.route('/')
def index():
"""首页 - 重定向到数据概览"""
return redirect('/dashboard.html')
@app.route('/health', methods=['GET'])
def health():
"""健康检查"""
return jsonify({'status': 'ok', 'message': '服务运行正常'})
@app.route('/api/urls', methods=['POST'])
def add_urls():
"""添加URL支持单个或批量"""
try:
data = request.get_json()
if not data:
return jsonify({'success': False, 'message': '请求数据为空'}), 400
# 支持单个URL或URL列表
if 'url' in data:
# 单个URL
url = data['url']
if not url:
return jsonify({'success': False, 'message': 'URL不能为空'}), 400
success = scheduler.add_url(url)
if success:
return jsonify({'success': True, 'message': '添加成功'})
else:
return jsonify({'success': False, 'message': 'URL已存在或添加失败'}), 400
elif 'urls' in data:
# 批量URL
urls = data['urls']
if not isinstance(urls, list) or not urls:
return jsonify({'success': False, 'message': 'URLs必须是非空列表'}), 400
count = scheduler.add_urls(urls)
return jsonify({
'success': True,
'message': f'成功添加 {count}/{len(urls)} 个URL',
'added_count': count,
'total_count': len(urls)
})
else:
return jsonify({'success': False, 'message': '请提供url或urls参数'}), 400
except Exception as e:
logger.error(f"添加URL异常: {str(e)}")
return jsonify({'success': False, 'message': f'服务异常: {str(e)}'}), 500
@app.route('/api/urls', methods=['GET'])
def get_urls():
"""获取所有URL列表"""
try:
urls = scheduler.data_manager.get_all_urls()
return jsonify({'success': True, 'data': urls})
except Exception as e:
logger.error(f"获取URL列表异常: {str(e)}")
return jsonify({'success': False, 'message': f'服务异常: {str(e)}'}), 500
@app.route('/api/urls/<path:url>', methods=['GET'])
def get_url_detail(url: str):
"""获取URL详细信息"""
try:
url_info = scheduler.get_url_detail(url)
if url_info:
return jsonify({'success': True, 'data': url_info})
else:
return jsonify({'success': False, 'message': 'URL不存在'}), 404
except Exception as e:
logger.error(f"获取URL详情异常: {str(e)}")
return jsonify({'success': False, 'message': f'服务异常: {str(e)}'}), 500
@app.route('/api/urls/<path:url>', methods=['DELETE'])
def delete_url(url: str):
"""删除URL"""
try:
success = scheduler.data_manager.delete_url(url)
if success:
return jsonify({'success': True, 'message': '删除成功'})
else:
return jsonify({'success': False, 'message': 'URL不存在'}), 404
except Exception as e:
logger.error(f"删除URL异常: {str(e)}")
return jsonify({'success': False, 'message': f'服务异常: {str(e)}'}), 500
@app.route('/api/urls/<path:url>/reset', methods=['POST'])
def reset_url(url: str):
"""重置URL重新开始点击"""
try:
success = scheduler.data_manager.reset_url(url)
if success:
return jsonify({'success': True, 'message': '重置成功'})
else:
return jsonify({'success': False, 'message': 'URL不存在'}), 404
except Exception as e:
logger.error(f"重置URL异常: {str(e)}")
return jsonify({'success': False, 'message': f'服务异常: {str(e)}'}), 500
@app.route('/api/statistics', methods=['GET'])
def get_statistics():
"""获取统计数据"""
try:
stats = scheduler.get_statistics()
return jsonify({'success': True, 'data': stats})
except Exception as e:
logger.error(f"获取统计数据异常: {str(e)}")
return jsonify({'success': False, 'message': f'服务异常: {str(e)}'}), 500
@app.route('/api/scheduler/start', methods=['POST'])
def start_scheduler():
"""启动调度器"""
try:
scheduler.start_scheduler()
return jsonify({'success': True, 'message': '调度器已启动'})
except Exception as e:
logger.error(f"启动调度器异常: {str(e)}")
return jsonify({'success': False, 'message': f'服务异常: {str(e)}'}), 500
@app.route('/api/scheduler/stop', methods=['POST'])
def stop_scheduler():
"""停止调度器"""
try:
scheduler.stop_scheduler()
return jsonify({'success': True, 'message': '调度器已停止'})
except Exception as e:
logger.error(f"停止调度器异常: {str(e)}")
return jsonify({'success': False, 'message': f'服务异常: {str(e)}'}), 500
@app.route('/api/scheduler/status', methods=['GET'])
def get_scheduler_status():
"""获取调度器状态"""
try:
status = 'running' if scheduler.running else 'stopped'
return jsonify({'success': True, 'data': {'status': status}})
except Exception as e:
logger.error(f"获取调度器状态异常: {str(e)}")
return jsonify({'success': False, 'message': f'服务异常: {str(e)}'}), 500
# AdsPower 接口调试
@app.route('/api/adspower/profiles', methods=['GET'])
def adspower_list_profiles():
"""查询Profile列表"""
try:
from adspower_client import AdsPowerClient
client = AdsPowerClient()
result = client.list_profiles()
return jsonify({'success': True, 'data': result})
except Exception as e:
logger.error(f"AdsPower查询Profile异常: {str(e)}")
return jsonify({'success': False, 'message': str(e)}), 500
@app.route('/api/adspower/browser/start', methods=['POST'])
def adspower_start_browser():
"""启动浏览器"""
try:
from adspower_client import AdsPowerClient
data = request.get_json() or {}
user_id = data.get('user_id')
client = AdsPowerClient()
result = client.start_browser(user_id=user_id)
return jsonify({'success': True, 'data': result})
except Exception as e:
logger.error(f"AdsPower启动浏览器异常: {str(e)}")
return jsonify({'success': False, 'message': str(e)}), 500
@app.route('/api/adspower/browser/stop', methods=['POST'])
def adspower_stop_browser():
"""停止浏览器"""
try:
from adspower_client import AdsPowerClient
data = request.get_json() or {}
user_id = data.get('user_id')
client = AdsPowerClient()
result = client.stop_browser(user_id=user_id)
return jsonify({'success': True, 'data': result})
except Exception as e:
logger.error(f"AdsPower停止浏览器异常: {str(e)}")
return jsonify({'success': False, 'message': str(e)}), 500
@app.route('/api/adspower/proxy/damai', methods=['GET'])
def adspower_get_damai_proxy():
"""获取大麦IP代理"""
try:
from adspower_client import AdsPowerClient
client = AdsPowerClient()
result = client.get_damai_proxy()
return jsonify({'success': True, 'data': result})
except Exception as e:
logger.error(f"获取大麦IP异常: {str(e)}")
return jsonify({'success': False, 'message': str(e)}), 500
@app.route('/api/adspower/proxy/create', methods=['POST'])
def adspower_create_proxy():
"""创建代理"""
try:
from adspower_client import AdsPowerClient
data = request.get_json() or {}
proxy_config = data.get('proxy_config')
if not proxy_config:
return jsonify({'success': False, 'message': '缺少代理配置'}), 400
client = AdsPowerClient()
proxy_id = client.create_proxy(proxy_config)
return jsonify({'success': True, 'data': {'proxy_id': proxy_id}})
except Exception as e:
logger.error(f"AdsPower创建代理异常: {str(e)}")
return jsonify({'success': False, 'message': str(e)}), 500
@app.route('/api/adspower/proxy/list', methods=['GET'])
def adspower_list_proxies():
"""查询代理列表"""
try:
from adspower_client import AdsPowerClient
page = request.args.get('page', 1, type=int)
limit = request.args.get('limit', 100, type=int)
client = AdsPowerClient()
result = client.list_proxies(page=page, limit=limit)
if result is None:
return jsonify({'success': False, 'message': '查询代理列表失败请检查AdsPower是否运行'}), 500
return jsonify({'success': True, 'data': result})
except Exception as e:
logger.error(f"AdsPower查询代理列表异常: {str(e)}")
return jsonify({'success': False, 'message': str(e)}), 500
@app.route('/api/adspower/profile/update', methods=['POST'])
def adspower_update_profile():
"""更新Profile代理API v2方式"""
try:
from adspower_client import AdsPowerClient
data = request.get_json() or {}
profile_id = data.get('profile_id')
proxy_id = data.get('proxy_id')
if not profile_id or not proxy_id:
return jsonify({'success': False, 'message': '缺少profile_id或proxy_id'}), 400
client = AdsPowerClient()
result = client.update_profile_proxy(profile_id, proxy_id)
return jsonify({'success': True, 'data': {'updated': result}})
except Exception as e:
logger.error(f"AdsPower更新Profile异常: {str(e)}")
return jsonify({'success': False, 'message': str(e)}), 500
@app.route('/api/adspower/profile/update-v1', methods=['POST'])
def adspower_update_profile_v1():
"""更新Profile代理API v1方式直接传入proxy_config"""
try:
from adspower_client import AdsPowerClient
data = request.get_json() or {}
profile_id = data.get('profile_id')
proxy_config = data.get('proxy_config')
if not profile_id or not proxy_config:
return jsonify({'success': False, 'message': '缺少profile_id或proxy_config'}), 400
# 验证必要字段
if 'proxy_host' not in proxy_config or 'proxy_port' not in proxy_config:
return jsonify({'success': False, 'message': 'proxy_config中缺少proxy_host或proxy_port'}), 400
client = AdsPowerClient()
result = client.update_profile_proxy_v1(profile_id, proxy_config)
return jsonify({'success': True, 'data': {'updated': result}})
except Exception as e:
logger.error(f"AdsPower更新Profile异常 (v1): {str(e)}")
return jsonify({'success': False, 'message': str(e)}), 500
if __name__ == '__main__':
logger.info(f"启动MIP广告点击服务 - 环境: {Config.ENV}")
logger.info(f"服务地址: http://{Config.SERVER_HOST}:{Config.SERVER_PORT}")
logger.info(f"调试模式: {Config.DEBUG}")
# 自动启动调度器
scheduler.start_scheduler()
# 启动Flask应用
app.run(
host=Config.SERVER_HOST,
port=Config.SERVER_PORT,
debug=Config.DEBUG
)