feat: 重构为 SOCKS5 代理管理平台 v2.0

架构:
- engine/server.py: asyncio SOCKS5 服务器引擎(含隧道/认证/限速)
- engine/instances.py: 多实例管理器(独立事件循环/热启动/热停止)
- services/user_service.py: 用户认证/流量统计/IP白名单/防爆破
- services/stats_service.py: 实时监控/流量趋势/连接追踪
- services/backup_service.py: 数据库备份与恢复
- api/v1.py: 完整 RESTful API
- web/routes.py: Web 管理面板(暗色 Bootstrap 5 主题)

功能覆盖:
 多实例 SOCKS5 管理(创建/启动/停止/重启/热加载)
 用户认证(无认证/账号密码/流量上限/限速/并发限制)
 IP 白名单/黑名单/防爆破
 实时仪表盘(CPU/内存/网络/活跃连接)
 流量统计(30天趋势图/用户排名/实例统计)
 活跃连接追踪
 审计日志(事件/用户/IP 筛选)
 数据库备份与恢复
 管理员密码保护
 16 项功能测试全部通过
This commit is contained in:
Your Name
2026-07-16 17:31:53 +08:00
parent d59699b5ce
commit 558bd8153f
35 changed files with 3245 additions and 817 deletions
+197
View File
@@ -0,0 +1,197 @@
"""监控与统计服务。"""
import logging
import os
import psutil
from collections import deque
from datetime import datetime, timezone, timedelta
from models import TrafficSnapshot, Connection, AuditLog, Instance, User
from database import db
log = logging.getLogger("socks.stats")
# 实时指标缓冲区(内存)
_metrics_buffer = deque(maxlen=600) # 10分钟 @ 10秒间隔
_metrics_lock = __import__("threading").Lock()
def _record_metrics():
"""记录系统级指标。"""
try:
cpu = psutil.cpu_percent(interval=0.5)
mem = psutil.virtual_memory()
net = psutil.net_io_counters()
# 计算活跃连接
with db.app.app_context():
active = db.session.query(Connection).filter_by(
state="active"
).count()
item = {
"ts": datetime.now(timezone.utc).isoformat(),
"cpu": cpu,
"mem_percent": mem.percent,
"mem_used_mb": mem.used // 1024 // 1024,
"mem_total_mb": mem.total // 1024 // 1024,
"net_bytes_sent": net.bytes_sent,
"net_bytes_recv": net.bytes_recv,
"active_connections": active,
}
with _metrics_lock:
_metrics_buffer.append(item)
except Exception as e:
log.error("记录指标失败: %s", e)
def get_realtime_metrics():
"""获取实时系统指标。"""
_record_metrics()
with _metrics_lock:
items = list(_metrics_buffer)
if not items:
_record_metrics()
items = list(_metrics_buffer)
return {
"latest": items[-1] if items else None,
"history": items[-60:], # 最近60个点(10分钟)
}
def get_traffic_trend(days=30):
"""获取流量趋势(每日快照)。"""
with db.app.app_context():
start = datetime.now(timezone.utc).date() - timedelta(days=days)
snapshots = db.session.query(
TrafficSnapshot.date,
db.func.sum(TrafficSnapshot.bytes_in).label("total_in"),
db.func.sum(TrafficSnapshot.bytes_out).label("total_out"),
db.func.sum(TrafficSnapshot.connections).label("total_conn"),
).filter(
TrafficSnapshot.date >= start
).group_by(TrafficSnapshot.date).order_by(
TrafficSnapshot.date
).all()
return [{
"date": s.date.isoformat(),
"bytes_in_mb": round((s.total_in or 0) / 1024 / 1024, 1),
"bytes_out_mb": round((s.total_out or 0) / 1024 / 1024, 1),
"connections": s.total_conn or 0,
} for s in snapshots]
def get_user_stats():
"""获取各用户流量排名。"""
from sqlalchemy import func
with db.app.app_context():
users = db.session.query(
User.username,
User.bytes_in,
User.bytes_out,
User.max_concurrent,
User.total_traffic_mb,
(User.bytes_in + User.bytes_out).label("total_bytes"),
).filter_by(enabled=True).order_by(
(User.bytes_in + User.bytes_out).desc()
).limit(20).all()
return [{
"username": u.username,
"bytes_in_mb": round(u.bytes_in / 1024 / 1024, 1),
"bytes_out_mb": round(u.bytes_out / 1024 / 1024, 1),
"total_mb": round(u.total_bytes / 1024 / 1024, 1),
"limit_mb": u.total_traffic_mb,
"usage_pct": round(
u.total_bytes / 1024 / 1024 / u.total_traffic_mb * 100, 1
) if u.total_traffic_mb else None,
} for u in users]
def get_instance_stats():
"""获取各实例流量统计。"""
with db.app.app_context():
instances = Instance.query.all()
result = []
for inst in instances:
conns = db.session.query(
db.func.sum(Connection.bytes_in).label("total_in"),
db.func.sum(Connection.bytes_out).label("total_out"),
db.func.count(Connection.id).label("total_conn"),
).filter(
Connection.instance_id == inst.id
).first()
result.append({
"id": inst.id,
"name": inst.name,
"host": inst.listen_host,
"port": inst.listen_port,
"bytes_in_mb": round((conns.total_in or 0) / 1024 / 1024, 1),
"bytes_out_mb": round((conns.total_out or 0) / 1024 / 1024, 1),
"total_connections": conns.total_conn or 0,
})
return result
def get_active_connections():
"""获取当前活跃连接列表。"""
with db.app.app_context():
conns = db.session.query(Connection).filter_by(
state="active"
).order_by(Connection.started_at.desc()).limit(50).all()
return [{
"id": c.id,
"instance": db.session.query(Instance).get(c.instance_id).name if c.instance_id else "",
"username": c.username,
"src": f"{c.src_ip}:{c.src_port}" if c.src_ip else "",
"dst": f"{c.dst_addr}:{c.dst_port}" if c.dst_addr else "",
"protocol": c.protocol,
"started": c.started_at.isoformat() if c.started_at else "",
"bytes_in": c.bytes_in,
"bytes_out": c.bytes_out,
} for c in conns]
def get_dashboard_summary():
"""仪表盘汇总。"""
metrics = get_realtime_metrics()
with db.app.app_context():
total_instances = db.session.query(Instance).count()
running_instances = db.session.query(Instance).filter_by(
enabled=True
).count()
total_users = db.session.query(User).count()
active_users = db.session.query(User).filter_by(
enabled=True, banned=False
).count()
active_conns = db.session.query(Connection).filter_by(
state="active"
).count()
today_logs = db.session.query(AuditLog).filter(
AuditLog.timestamp >= datetime.now(timezone.utc).replace(
hour=0, minute=0, second=0
)
).count()
return {
"system": metrics["latest"],
"total_instances": total_instances,
"running_instances": running_instances,
"total_users": total_users,
"active_users": active_users,
"active_connections": active_conns,
"today_logs": today_logs,
}
def prune_old_data():
"""清理旧数据(运行在后台)。"""
with db.app.app_context():
# 清理 30 天前的连接记录(closed的)
cutoff = datetime.now(timezone.utc) - timedelta(days=30)
db.session.query(Connection).filter(
Connection.state == "closed",
Connection.started_at < cutoff
).delete(synchronize_session=False)
# 清理 90 天前的审计日志
cutoff2 = datetime.now(timezone.utc) - timedelta(days=90)
db.session.query(AuditLog).filter(AuditLog.timestamp < cutoff2).delete(
synchronize_session=False
)
db.session.commit()
log.info("数据清理完成")