aa8af6c2e0
Flask + SQLite + Bootstrap5 暗色主题 - 仪表盘: 在线/离线统计 + 最近代理 + 操作日志 - 代理 CRUD: 增删改 + 启用/禁用 + 单/批量健康检测(SOCKS5) - 分组管理: 创建/删除/筛选 - 操作日志: 分页查询 - RESTful API + Web 界面 - 管理员登录认证 (SM_ADMIN_PASSWORD 环境变量)
96 lines
2.8 KiB
Python
96 lines
2.8 KiB
Python
from flask import Blueprint, request, jsonify
|
|
from models import Proxy, Group, Log, db
|
|
from auth import login_required
|
|
from health import check_proxy
|
|
|
|
bp = Blueprint("api", __name__)
|
|
|
|
|
|
@bp.route("/proxies", methods=["GET"])
|
|
@login_required
|
|
def list_proxies():
|
|
g = request.args.get("group")
|
|
query = Proxy.query
|
|
if g:
|
|
query = query.filter_by(group_id=g)
|
|
items = query.order_by(Proxy.updated_at.desc()).all()
|
|
return jsonify([{
|
|
"id": p.id, "name": p.name, "host": p.host, "port": p.port,
|
|
"username": p.username, "group": p.group.name if p.group else None,
|
|
"enabled": p.enabled, "status": p.status,
|
|
"latency_ms": round(p.latency_ms, 1) if p.latency_ms else None,
|
|
"last_check": p.last_check.isoformat() if p.last_check else None,
|
|
} for p in items])
|
|
|
|
|
|
@bp.route("/proxies", methods=["POST"])
|
|
@login_required
|
|
def create_proxy():
|
|
data = request.get_json()
|
|
p = Proxy(
|
|
name=data["name"], host=data["host"], port=int(data["port"]),
|
|
username=data.get("username"), password=data.get("password"),
|
|
group_id=int(data["group_id"]) if data.get("group_id") else None,
|
|
notes=data.get("notes"),
|
|
)
|
|
db.session.add(p)
|
|
db.session.commit()
|
|
return jsonify({"id": p.id}), 201
|
|
|
|
|
|
@bp.route("/proxies/<int:pid>", methods=["PUT"])
|
|
@login_required
|
|
def update_proxy(pid):
|
|
p = Proxy.query.get_or_404(pid)
|
|
data = request.get_json()
|
|
for k in ("name", "host", "port", "username", "password", "group_id", "notes"):
|
|
v = data.get(k)
|
|
if v is not None:
|
|
setattr(p, k, int(v) if k in ("port", "group_id") and v else v)
|
|
db.session.commit()
|
|
return jsonify({"ok": True})
|
|
|
|
|
|
@bp.route("/proxies/<int:pid>", methods=["DELETE"])
|
|
@login_required
|
|
def delete_proxy(pid):
|
|
p = Proxy.query.get_or_404(pid)
|
|
db.session.delete(p)
|
|
db.session.commit()
|
|
return jsonify({"ok": True})
|
|
|
|
|
|
@bp.route("/proxies/<int:pid>/check", methods=["POST"])
|
|
@login_required
|
|
def check_single(pid):
|
|
p = Proxy.query.get_or_404(pid)
|
|
result = check_proxy(p)
|
|
return jsonify(result)
|
|
|
|
|
|
@bp.route("/proxies/<int:pid>/toggle", methods=["POST"])
|
|
@login_required
|
|
def toggle(pid):
|
|
p = Proxy.query.get_or_404(pid)
|
|
p.enabled = not p.enabled
|
|
db.session.commit()
|
|
return jsonify({"enabled": p.enabled})
|
|
|
|
|
|
@bp.route("/groups", methods=["GET"])
|
|
@login_required
|
|
def list_groups():
|
|
groups = Group.query.all()
|
|
return jsonify([{"id": g.id, "name": g.name, "color": g.color,
|
|
"description": g.description,
|
|
"count": len(g.proxies)} for g in groups])
|
|
|
|
|
|
@bp.route("/stats")
|
|
@login_required
|
|
def stats():
|
|
total = Proxy.query.count()
|
|
online = Proxy.query.filter_by(status="online", enabled=True).count()
|
|
offline = Proxy.query.filter_by(status="offline").count()
|
|
return jsonify({"total": total, "online": online, "offline": offline})
|