feat: 完整 Squid Web Manager 平台 - P0-P3 全功能

完整 Web 管理平台,涵盖:

P0 生产加固:
- 会话超时(空闲+绝对) + CSRF 防护 + 登录失败限流
- HTTPS/TLS 证书管理(自签生成/上传/删除/过期提醒)
- SSL Bump (HTTPS 透明代理) 可视化配置
- 配置文件 diff 预览 + 二次确认才写入
- 服务控制二次确认 + 操作期间按钮锁定

P1 运维能力:
- 日志轮转管理(logrotate) + 日志状态监控
- 告警规则(5xx/命中率/磁盘/客户端流量)
- 多 Squid 实例管理 + 一键切换
- squidclient mgr 实时性能指标
- 流量异常检测(突增/大文件/高频小包/新客户端)

P2 日志分析增强:
- 日志持久化到 SQLite + 历史时间范围查询
- 导出 CSV/JSON(日志/KPI/异常/审计)
- GeoIP 地图(Leaflet + ip-api.com 离线可选 GeoLite2)
- 每客户端 24h 趋势图
- 自定义 Squid logformat 解析器

P3 体验锦上添花:
- 暗/亮主题切换(cookie 持久化)
- WebSSH 终端(xterm.js + paramiko)
- 完整审计日志 + 用户管理

技术栈: Flask 3.0 + SQLAlchemy 2.0 + SQLite + Chart.js + Leaflet
总代码量: ~16500 行 (15 Python 模块 + 34 模板 + CSS)
路由数: 73
This commit is contained in:
Hermes Agent
2026-07-15 10:19:21 +08:00
commit 4a1d949e41
54 changed files with 16895 additions and 0 deletions
+119
View File
@@ -0,0 +1,119 @@
{% extends "base.html" %}
{% block title %}ACL 规则表 - Squid Manager{% endblock %}
{% block content %}
<div class="page-header">
<h1>🚦 ACL 规则表</h1>
<div class="page-actions">
<button onclick="document.getElementById('add-form').style.display='block'; this.scrollIntoView()" class="btn btn-primary btn-small"> 添加 ACL</button>
</div>
</div>
<div class="card" id="add-form" style="display:none;">
<h3 class="card-title">添加新 ACL</h3>
<form method="post" class="inline-form">
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
<input type="hidden" name="action" value="add">
<div class="filter-grid">
<div class="form-group">
<label>名称 <span class="hint">*</span></label>
<input type="text" name="name" class="form-input" required placeholder="my_network">
</div>
<div class="form-group">
<label>类型 <span class="hint">*</span></label>
<select name="type" class="form-input" required>
{% for code, desc in acl_types %}
<option value="{{ code }}">{{ code }} - {{ desc }}</option>
{% endfor %}
</select>
</div>
<div class="form-group" style="grid-column: span 2;">
<label><span class="hint">(空格分隔多个值)</span></label>
<input type="text" name="values" class="form-input" required placeholder="192.168.1.0/24">
</div>
<div class="form-group" style="grid-column: span 2;">
<label>备注</label>
<input type="text" name="comment" class="form-input" placeholder="可选">
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">添加</button>
<button type="button" onclick="document.getElementById('add-form').style.display='none'" class="btn btn-secondary">取消</button>
</div>
</form>
</div>
<div class="card">
<form method="post">
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
<input type="hidden" name="action" value="save">
<p class="card-desc">共 {{ acls | length }} 条 ACL。修改后点击底部"保存"按钮。</p>
<div class="table-scroll">
<table class="data-table acl-table">
<thead>
<tr>
<th style="width:60px;">#</th>
<th>名称</th>
<th>类型</th>
<th></th>
<th>备注</th>
<th style="width:80px;">删除</th>
</tr>
</thead>
<tbody>
{% for acl in acls %}
<tr>
<td>{{ loop.index }}
<input type="hidden" name="id[]" value="{{ loop.index }}">
</td>
<td><input type="text" name="name[]" value="{{ acl.name }}" class="form-input"></td>
<td>
<select name="type[]" class="form-input">
{% for code, desc in acl_types %}
<option value="{{ code }}" {% if acl.type == code %}selected{% endif %}>{{ code }}</option>
{% endfor %}
</select>
</td>
<td><input type="text" name="values[]" value="{{ acl.values | join(' ') }}" class="form-input"></td>
<td><input type="text" name="comment[]" value="{{ acl.comment }}" class="form-input"></td>
<td>
<button type="button" class="btn btn-danger btn-tiny" onclick="this.closest('tr').remove()">删除</button>
</td>
</tr>
{% endfor %}
{% if not acls %}
<tr><td colspan="6" class="empty-row">暂无 ACL。点击上方"添加 ACL"按钮开始。</td></tr>
{% endif %}
</tbody>
</table>
</div>
{% if acls %}
<div class="form-actions">
<button type="submit" class="btn btn-primary">💾 保存所有 ACL</button>
</div>
{% endif %}
</form>
</div>
<div class="card">
<h3 class="card-title">📖 ACL 类型参考</h3>
<div class="table-scroll">
<table class="data-table">
<thead><tr><th>类型</th><th>说明</th><th>示例</th></tr></thead>
<tbody>
<tr><td><code>src</code></td><td>源 IP / CIDR</td><td>192.168.1.0/24</td></tr>
<tr><td><code>dst</code></td><td>目的 IP / CIDR</td><td>10.0.0.0/8</td></tr>
<tr><td><code>dstdomain</code></td><td>目的域名 (含子域)</td><td>.example.com</td></tr>
<tr><td><code>dstdom_regex</code></td><td>目的域名正则</td><td>^www\.</td></tr>
<tr><td><code>port</code></td><td>目的端口</td><td>80 443</td></tr>
<tr><td><code>method</code></td><td>HTTP 方法</td><td>GET POST</td></tr>
<tr><td><code>proto</code></td><td>协议</td><td>http https ftp</td></tr>
<tr><td><code>time</code></td><td>时间段 (周/时:分)</td><td>MTWHF 09:00-18:00</td></tr>
<tr><td><code>url_regex</code></td><td>URL 正则</td><td>\.mp3$</td></tr>
<tr><td><code>proxy_auth</code></td><td>认证用户</td><td>REQUIRED 或 user1 user2</td></tr>
<tr><td><code>maxconn</code></td><td>每客户端最大连接</td><td>16</td></tr>
<tr><td><code>browser</code></td><td>User-Agent 正则</td><td>Mozilla</td></tr>
</tbody>
</table>
</div>
</div>
{% endblock %}