249 lines
8.6 KiB
Python
249 lines
8.6 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
import database
|
|
from flask import Blueprint, jsonify, request
|
|
from flask_login import current_user, login_required
|
|
from services.accounts_service import load_user_accounts
|
|
from services.browse_types import BROWSE_TYPE_SHOULD_READ, normalize_browse_type, validate_browse_type
|
|
from services.state import safe_get_account, safe_get_user_accounts_snapshot
|
|
from services.tasks import submit_account_task
|
|
|
|
api_schedules_bp = Blueprint("api_schedules", __name__)
|
|
|
|
|
|
@api_schedules_bp.route("/api/schedules", methods=["GET"])
|
|
@login_required
|
|
def get_user_schedules_api():
|
|
"""获取当前用户的所有定时任务"""
|
|
schedules = database.get_user_schedules(current_user.id)
|
|
import json
|
|
|
|
for s in schedules:
|
|
try:
|
|
s["account_ids"] = json.loads(s.get("account_ids", "[]") or "[]")
|
|
except (json.JSONDecodeError, TypeError):
|
|
s["account_ids"] = []
|
|
return jsonify(schedules)
|
|
|
|
|
|
@api_schedules_bp.route("/api/schedules", methods=["POST"])
|
|
@login_required
|
|
def create_user_schedule_api():
|
|
"""创建用户定时任务"""
|
|
data = request.json
|
|
|
|
name = data.get("name", "我的定时任务")
|
|
schedule_time = data.get("schedule_time", "08:00")
|
|
weekdays = data.get("weekdays", "1,2,3,4,5")
|
|
browse_type = validate_browse_type(data.get("browse_type", BROWSE_TYPE_SHOULD_READ), default=BROWSE_TYPE_SHOULD_READ)
|
|
if not browse_type:
|
|
return jsonify({"error": "浏览类型无效"}), 400
|
|
enable_screenshot = data.get("enable_screenshot", 1)
|
|
random_delay = int(data.get("random_delay", 0) or 0)
|
|
account_ids = data.get("account_ids", [])
|
|
|
|
if not re.match(r"^\\d{2}:\\d{2}$", schedule_time):
|
|
return jsonify({"error": "时间格式不正确,应为 HH:MM"}), 400
|
|
if random_delay not in (0, 1):
|
|
return jsonify({"error": "random_delay必须是0或1"}), 400
|
|
|
|
schedule_id = database.create_user_schedule(
|
|
user_id=current_user.id,
|
|
name=name,
|
|
schedule_time=schedule_time,
|
|
weekdays=weekdays,
|
|
browse_type=browse_type,
|
|
enable_screenshot=enable_screenshot,
|
|
random_delay=random_delay,
|
|
account_ids=account_ids,
|
|
)
|
|
|
|
if schedule_id:
|
|
return jsonify({"success": True, "id": schedule_id})
|
|
return jsonify({"error": "创建失败"}), 500
|
|
|
|
|
|
@api_schedules_bp.route("/api/schedules/<int:schedule_id>", methods=["GET"])
|
|
@login_required
|
|
def get_schedule_detail_api(schedule_id):
|
|
"""获取定时任务详情"""
|
|
schedule = database.get_schedule_by_id(schedule_id)
|
|
if not schedule:
|
|
return jsonify({"error": "定时任务不存在"}), 404
|
|
if schedule["user_id"] != current_user.id:
|
|
return jsonify({"error": "无权访问"}), 403
|
|
|
|
import json
|
|
|
|
try:
|
|
schedule["account_ids"] = json.loads(schedule.get("account_ids", "[]") or "[]")
|
|
except (json.JSONDecodeError, TypeError):
|
|
schedule["account_ids"] = []
|
|
return jsonify(schedule)
|
|
|
|
|
|
@api_schedules_bp.route("/api/schedules/<int:schedule_id>", methods=["PUT"])
|
|
@login_required
|
|
def update_schedule_api(schedule_id):
|
|
"""更新定时任务"""
|
|
schedule = database.get_schedule_by_id(schedule_id)
|
|
if not schedule:
|
|
return jsonify({"error": "定时任务不存在"}), 404
|
|
if schedule["user_id"] != current_user.id:
|
|
return jsonify({"error": "无权访问"}), 403
|
|
|
|
data = request.json
|
|
allowed_fields = [
|
|
"name",
|
|
"schedule_time",
|
|
"weekdays",
|
|
"browse_type",
|
|
"enable_screenshot",
|
|
"random_delay",
|
|
"account_ids",
|
|
"enabled",
|
|
]
|
|
|
|
update_data = {k: v for k, v in data.items() if k in allowed_fields}
|
|
|
|
if "schedule_time" in update_data:
|
|
if not re.match(r"^\\d{2}:\\d{2}$", update_data["schedule_time"]):
|
|
return jsonify({"error": "时间格式不正确"}), 400
|
|
if "random_delay" in update_data:
|
|
try:
|
|
update_data["random_delay"] = int(update_data.get("random_delay") or 0)
|
|
except Exception:
|
|
return jsonify({"error": "random_delay必须是0或1"}), 400
|
|
if update_data["random_delay"] not in (0, 1):
|
|
return jsonify({"error": "random_delay必须是0或1"}), 400
|
|
if "browse_type" in update_data:
|
|
normalized = validate_browse_type(update_data.get("browse_type"), default=BROWSE_TYPE_SHOULD_READ)
|
|
if not normalized:
|
|
return jsonify({"error": "浏览类型无效"}), 400
|
|
update_data["browse_type"] = normalized
|
|
|
|
success = database.update_user_schedule(schedule_id, **update_data)
|
|
if success:
|
|
return jsonify({"success": True})
|
|
return jsonify({"error": "更新失败"}), 500
|
|
|
|
|
|
@api_schedules_bp.route("/api/schedules/<int:schedule_id>", methods=["DELETE"])
|
|
@login_required
|
|
def delete_schedule_api(schedule_id):
|
|
"""删除定时任务"""
|
|
schedule = database.get_schedule_by_id(schedule_id)
|
|
if not schedule:
|
|
return jsonify({"error": "定时任务不存在"}), 404
|
|
if schedule["user_id"] != current_user.id:
|
|
return jsonify({"error": "无权访问"}), 403
|
|
|
|
success = database.delete_user_schedule(schedule_id)
|
|
if success:
|
|
return jsonify({"success": True})
|
|
return jsonify({"error": "删除失败"}), 500
|
|
|
|
|
|
@api_schedules_bp.route("/api/schedules/<int:schedule_id>/toggle", methods=["POST"])
|
|
@login_required
|
|
def toggle_schedule_api(schedule_id):
|
|
"""启用/禁用定时任务"""
|
|
schedule = database.get_schedule_by_id(schedule_id)
|
|
if not schedule:
|
|
return jsonify({"error": "定时任务不存在"}), 404
|
|
if schedule["user_id"] != current_user.id:
|
|
return jsonify({"error": "无权访问"}), 403
|
|
|
|
data = request.json
|
|
enabled = data.get("enabled", not schedule["enabled"])
|
|
|
|
success = database.toggle_user_schedule(schedule_id, enabled)
|
|
if success:
|
|
return jsonify({"success": True, "enabled": enabled})
|
|
return jsonify({"error": "操作失败"}), 500
|
|
|
|
|
|
@api_schedules_bp.route("/api/schedules/<int:schedule_id>/run", methods=["POST"])
|
|
@login_required
|
|
def run_schedule_now_api(schedule_id):
|
|
"""立即执行定时任务"""
|
|
import json
|
|
|
|
schedule = database.get_schedule_by_id(schedule_id)
|
|
if not schedule:
|
|
return jsonify({"error": "定时任务不存在"}), 404
|
|
if schedule["user_id"] != current_user.id:
|
|
return jsonify({"error": "无权访问"}), 403
|
|
|
|
try:
|
|
account_ids = json.loads(schedule.get("account_ids", "[]") or "[]")
|
|
except (json.JSONDecodeError, TypeError):
|
|
account_ids = []
|
|
|
|
if not account_ids:
|
|
return jsonify({"error": "没有配置账号"}), 400
|
|
|
|
user_id = current_user.id
|
|
browse_type = normalize_browse_type(schedule.get("browse_type", BROWSE_TYPE_SHOULD_READ))
|
|
enable_screenshot = schedule["enable_screenshot"]
|
|
|
|
if not safe_get_user_accounts_snapshot(user_id):
|
|
load_user_accounts(user_id)
|
|
|
|
started = []
|
|
for account_id in account_ids:
|
|
account = safe_get_account(user_id, account_id)
|
|
if not account:
|
|
continue
|
|
if account.is_running:
|
|
continue
|
|
|
|
ok, msg = submit_account_task(
|
|
user_id=user_id,
|
|
account_id=account_id,
|
|
browse_type=browse_type,
|
|
enable_screenshot=enable_screenshot,
|
|
source="user_scheduled",
|
|
)
|
|
if ok:
|
|
started.append(account_id)
|
|
|
|
database.update_schedule_last_run(schedule_id)
|
|
|
|
return jsonify({"success": True, "started_count": len(started), "message": f"已启动 {len(started)} 个账号"})
|
|
|
|
|
|
@api_schedules_bp.route("/api/schedules/<int:schedule_id>/logs", methods=["GET"])
|
|
@login_required
|
|
def get_schedule_logs_api(schedule_id):
|
|
"""获取定时任务执行日志"""
|
|
try:
|
|
schedule = database.get_schedule_by_id(schedule_id)
|
|
if not schedule or schedule["user_id"] != current_user.id:
|
|
return jsonify([])
|
|
|
|
limit = request.args.get("limit", 20, type=int)
|
|
logs = database.get_schedule_execution_logs(schedule_id, limit)
|
|
return jsonify(logs if logs else [])
|
|
except Exception:
|
|
return jsonify([])
|
|
|
|
|
|
@api_schedules_bp.route("/api/schedules/<int:schedule_id>/logs", methods=["DELETE"])
|
|
@login_required
|
|
def delete_schedule_logs_api(schedule_id):
|
|
"""清空定时任务执行日志"""
|
|
try:
|
|
schedule = database.get_schedule_by_id(schedule_id)
|
|
if not schedule or schedule["user_id"] != current_user.id:
|
|
return jsonify({"error": "无权限"}), 403
|
|
|
|
deleted = database.delete_schedule_logs(schedule_id, current_user.id)
|
|
return jsonify({"success": True, "deleted": deleted})
|
|
except Exception as e:
|
|
return jsonify({"error": str(e)}), 500
|