import threading from flask import Blueprint, flash, redirect, render_template, request, url_for, current_app from flask_login import current_user, login_required from app.extensions import db, scheduler from app.forms import ApiConfigForm from app.models import ApiConfig from app.services.api_executor import execute_api_by_id from app.services.scheduler import SchedulerService apis_bp = Blueprint("apis", __name__, url_prefix="/apis") def _scheduler_service() -> SchedulerService: return SchedulerService(scheduler) @apis_bp.route("", methods=["GET"]) @login_required def list_apis(): name = request.args.get("name") enabled = request.args.get("enabled") query = ApiConfig.query if name: query = query.filter(ApiConfig.name.ilike(f"%{name}%")) if enabled in {"0", "1"}: query = query.filter(ApiConfig.enabled == (enabled == "1")) apis = query.order_by(ApiConfig.updated_at.desc()).all() return render_template("apis/list.html", apis=apis) @apis_bp.route("/new", methods=["GET", "POST"]) @login_required def create_api(): form = ApiConfigForm() if form.validate_on_submit(): api = ApiConfig( name=form.name.data, description=form.description.data, url=form.url.data, http_method=form.http_method.data, headers=form.headers.data, query_params=form.query_params.data, body=form.body.data, schedule_type=form.schedule_type.data, schedule_expression=form.schedule_expression.data, timeout_seconds=form.timeout_seconds.data, retry_times=form.retry_times.data, retry_interval_seconds=form.retry_interval_seconds.data, enabled=form.enabled.data, created_by=current_user.id, ) db.session.add(api) db.session.commit() if api.enabled: _scheduler_service().add_job_for_api(api) flash("API 配置已创建", "success") return redirect(url_for("apis.list_apis")) return render_template("apis/form.html", form=form, api=None) @apis_bp.route("//edit", methods=["GET", "POST"]) @login_required def edit_api(api_id: int): api = ApiConfig.query.get_or_404(api_id) form = ApiConfigForm(obj=api) if form.validate_on_submit(): form.populate_obj(api) db.session.commit() if api.enabled: _scheduler_service().reschedule_job_for_api(api) else: _scheduler_service().remove_job_for_api(api.id) flash("API 配置已更新", "success") return redirect(url_for("apis.list_apis")) return render_template("apis/form.html", form=form, api=api) @apis_bp.route("//toggle", methods=["POST"]) @login_required def toggle_api(api_id: int): api = ApiConfig.query.get_or_404(api_id) api.enabled = not api.enabled db.session.commit() svc = _scheduler_service() if api.enabled: svc.add_job_for_api(api) flash("API 已启用并加入调度", "success") else: svc.remove_job_for_api(api.id) flash("API 已停用并移除调度", "info") return redirect(url_for("apis.list_apis")) @apis_bp.route("//delete", methods=["POST"]) @login_required def delete_api(api_id: int): api = ApiConfig.query.get_or_404(api_id) db.session.delete(api) db.session.commit() _scheduler_service().remove_job_for_api(api_id) flash("API 配置已删除", "info") return redirect(url_for("apis.list_apis")) @apis_bp.route("//copy", methods=["POST"]) @login_required def copy_api(api_id: int): src = ApiConfig.query.get_or_404(api_id) new_api = ApiConfig( name=f"{src.name}-副本", description=src.description, url=src.url, http_method=src.http_method, headers=src.headers, query_params=src.query_params, body=src.body, schedule_type=src.schedule_type, schedule_expression=src.schedule_expression, timeout_seconds=src.timeout_seconds, retry_times=src.retry_times, retry_interval_seconds=src.retry_interval_seconds, enabled=False, # 复制后默认不启用,避免未确认即自动调度 created_by=current_user.id, ) db.session.add(new_api) db.session.commit() flash("已复制为新配置,请查看并启用。", "success") return redirect(url_for("apis.edit_api", api_id=new_api.id)) @apis_bp.route("//run", methods=["POST"]) @login_required def run_now(api_id: int): api = ApiConfig.query.get_or_404(api_id) app_obj = current_app._get_current_object() threading.Thread(target=execute_api_by_id, args=(api.id, app_obj), daemon=True).start() flash("已触发立即执行,稍后在日志中查看结果。", "success") return redirect(url_for("apis.list_apis"))