from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify from datetime import datetime, timezone from auth import login_required from models import Proxy, Group, Log, db from health import check_proxy, run_health_checks bp = Blueprint("main", __name__) def _log(level, action, target=None, detail=None): db.session.add(Log(level=level, action=action, target=target, detail=detail)) db.session.commit() # ── Dashboard ────────────────────────────────────────────────── @bp.route("/") @bp.route("/dashboard") @login_required def dashboard(): total = Proxy.query.count() online = Proxy.query.filter_by(status="online", enabled=True).count() offline = Proxy.query.filter_by(status="offline").count() unknown = Proxy.query.filter_by(status="unknown").count() groups = Group.query.count() recent = Log.query.order_by(Log.timestamp.desc()).limit(20).all() proxies = Proxy.query.filter_by(enabled=True).order_by(Proxy.updated_at.desc()).limit(10).all() return render_template("dashboard.html", total=total, online=online, offline=offline, unknown=unknown, groups=groups, recent=recent, proxies=proxies) # ── Proxy list ───────────────────────────────────────────────── @bp.route("/proxies") @login_required def proxy_list(): g = request.args.get("group") query = Proxy.query.order_by(Proxy.updated_at.desc()) if g: query = query.filter_by(group_id=g) groups = Group.query.all() return render_template("proxies.html", proxies=query.all(), groups=groups, active_group=g) @bp.route("/proxies/", methods=["DELETE"]) @login_required def delete_proxy(pid): p = Proxy.query.get_or_404(pid) _log("WARN", "delete", p.name, f"删除代理 {p.host}:{p.port}") db.session.delete(p) db.session.commit() return redirect(url_for("main.proxy_list")) @bp.route("/proxies/toggle/") @login_required def toggle_proxy(pid): p = Proxy.query.get_or_404(pid) p.enabled = not p.enabled db.session.commit() _log("INFO", "toggle", p.name, f"{'启用' if p.enabled else '禁用'}") return redirect(url_for("main.proxy_list")) # ── Add / Edit ───────────────────────────────────────────────── @bp.route("/proxies/add", methods=["GET", "POST"]) @login_required def add_proxy(): if request.method == "POST": p = Proxy( name=request.form["name"], host=request.form["host"], port=int(request.form["port"]), username=request.form.get("username"), password=request.form.get("password"), group_id=int(request.form["group_id"]) if request.form.get("group_id") else None, notes=request.form.get("notes"), ) db.session.add(p) db.session.commit() _log("INFO", "add", p.name, f"添加代理 {p.host}:{p.port}") return redirect(url_for("main.proxy_list")) groups = Group.query.all() return render_template("proxy_form.html", p=None, groups=groups, action="add") @bp.route("/proxies//edit", methods=["GET", "POST"]) @login_required def edit_proxy(pid): p = Proxy.query.get_or_404(pid) if request.method == "POST": p.name = request.form["name"] p.host = request.form["host"] p.port = int(request.form["port"]) p.username = request.form.get("username") p.password = request.form.get("password") p.group_id = int(request.form["group_id"]) if request.form.get("group_id") else None p.notes = request.form.get("notes") db.session.commit() _log("INFO", "update", p.name, f"更新代理 {p.host}:{p.port}") return redirect(url_for("main.proxy_list")) groups = Group.query.all() return render_template("proxy_form.html", p=p, groups=groups, action="edit") # ── Group ────────────────────────────────────────────────────── @bp.route("/groups") @login_required def group_list(): groups = Group.query.all() return render_template("groups.html", groups=groups) @bp.route("/groups/add", methods=["POST"]) @login_required def add_group(): name = request.form["name"] if Group.query.filter_by(name=name).first(): return jsonify({"error": "分组已存在"}), 400 g = Group(name=name, color=request.form.get("color", "#6366f1"), description=request.form.get("description")) db.session.add(g) db.session.commit() _log("INFO", "add", name, "添加分组") return jsonify({"id": g.id, "name": g.name}) @bp.route("/groups/", methods=["DELETE"]) @login_required def delete_group(gid): g = Group.query.get_or_404(gid) if g.proxies: return jsonify({"error": "分组下仍有代理,请先移动"}), 400 _log("WARN", "delete", g.name, "删除分组") db.session.delete(g) db.session.commit() return jsonify({"ok": True}) # ── Logs ─────────────────────────────────────────────────────── @bp.route("/logs") @login_required def logs(): page = int(request.args.get("page", 1)) ps = db.paginate(Log.query.order_by(Log.timestamp.desc()), page=page, per_page=50) return render_template("logs.html", pages=ps, page_start=max(1, ps.page - 2), page_end=min(ps.pages, ps.page + 2)) @bp.route("/health/check-all") @login_required def health_check_all(): results = run_health_checks() return jsonify(results)