115 lines
3.9 KiB
Python
115 lines
3.9 KiB
Python
# -*- coding: utf-8 -*-
|
||
from datetime import datetime
|
||
from flask import Blueprint, current_app, jsonify, render_template, request
|
||
|
||
from .auth import login_required
|
||
|
||
|
||
plugin_schedules_bp = Blueprint("plugin_schedules", __name__, url_prefix="/plugin_schedules")
|
||
|
||
|
||
def _normalize_datetime_text(value):
|
||
"""统一时间文本格式为 `YYYY-MM-DD HH:MM:SS`。
|
||
|
||
兼容场景:
|
||
1. 数据库返回 datetime 对象;
|
||
2. 已经是 ISO 字符串(包含 T);
|
||
3. 兜底保留原值字符串,避免因为解析失败把数据抹掉。
|
||
"""
|
||
if value is None:
|
||
return value
|
||
if isinstance(value, datetime):
|
||
return value.strftime("%Y-%m-%d %H:%M:%S")
|
||
text = str(value)
|
||
# 先处理常见 ISO 格式,直接去掉 T 并截到秒,避免前端显示异常。
|
||
if "T" in text:
|
||
return text.replace("T", " ")[:19]
|
||
return text
|
||
|
||
|
||
@plugin_schedules_bp.route("/")
|
||
@login_required
|
||
def page_plugin_schedules():
|
||
return render_template("plugin_schedules.html")
|
||
|
||
|
||
@plugin_schedules_bp.route("/api/schedules", methods=["GET"])
|
||
@login_required
|
||
def api_list_schedules():
|
||
server = current_app.dashboard_server
|
||
data = server.plugin_schedule_manager.list_schedules_with_runtime()
|
||
# 后端统一格式化时间字段,避免前端出现 Fri, 17 Apr 2026 ... 这类 RFC 时间串。
|
||
for row in data:
|
||
for key in ("next_run_at", "last_run_at", "created_at", "updated_at"):
|
||
if key in row:
|
||
row[key] = _normalize_datetime_text(row.get(key))
|
||
return jsonify({"success": True, "data": data})
|
||
|
||
|
||
@plugin_schedules_bp.route("/api/actions", methods=["GET"])
|
||
@login_required
|
||
def api_list_actions():
|
||
server = current_app.dashboard_server
|
||
data = server.plugin_schedule_manager.get_available_plugin_actions()
|
||
return jsonify({"success": True, "data": data})
|
||
|
||
|
||
@plugin_schedules_bp.route("/api/schedules/<int:schedule_id>", methods=["PUT"])
|
||
@login_required
|
||
def api_update_schedule(schedule_id: int):
|
||
server = current_app.dashboard_server
|
||
payload = request.get_json(silent=True) or {}
|
||
|
||
updates = {}
|
||
for key in (
|
||
"action_name",
|
||
"description",
|
||
"trigger_type",
|
||
"trigger_config",
|
||
"target_scope",
|
||
"target_config",
|
||
"payload",
|
||
"enabled",
|
||
):
|
||
if key in payload:
|
||
updates[key] = payload[key]
|
||
|
||
if not updates:
|
||
return jsonify({"success": False, "message": "没有可更新字段"}), 400
|
||
|
||
ok = server.plugin_schedule_manager.update_schedule(schedule_id, updates)
|
||
if not ok:
|
||
return jsonify({"success": False, "message": "更新失败"}), 500
|
||
|
||
return jsonify({"success": True, "message": "更新成功"})
|
||
|
||
|
||
@plugin_schedules_bp.route("/api/schedules/<int:schedule_id>/trigger", methods=["POST"])
|
||
@login_required
|
||
def api_trigger_schedule(schedule_id: int):
|
||
server = current_app.dashboard_server
|
||
ok, msg = server.plugin_schedule_manager.trigger_now(schedule_id)
|
||
code = 200 if ok else 400
|
||
return jsonify({"success": ok, "message": msg}), code
|
||
|
||
|
||
@plugin_schedules_bp.route("/api/schedules/<int:schedule_id>/logs", methods=["GET"])
|
||
@login_required
|
||
def api_schedule_logs(schedule_id: int):
|
||
server = current_app.dashboard_server
|
||
limit = int(request.args.get("limit", 100))
|
||
logs = server.plugin_schedule_manager.get_logs(schedule_id, limit=limit)
|
||
# 日志时间统一成固定格式,避免被 Flask JSON 序列化成 RFC 字符串。
|
||
for row in logs:
|
||
if "triggered_at" in row:
|
||
row["triggered_at"] = _normalize_datetime_text(row.get("triggered_at"))
|
||
return jsonify({"success": True, "data": logs})
|
||
|
||
|
||
@plugin_schedules_bp.route("/api/reload", methods=["POST"])
|
||
@login_required
|
||
def api_reload_schedules():
|
||
server = current_app.dashboard_server
|
||
server.plugin_schedule_manager.reload_from_db()
|
||
return jsonify({"success": True, "message": "已按数据库配置重载插件调度"})
|