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
+161
View File
@@ -0,0 +1,161 @@
{% extends "base.html" %}
{% block title %}TLS 证书 - Squid Manager{% endblock %}
{% block content %}
<div class="page-header">
<h1>🔐 TLS 证书管理</h1>
<div class="page-actions">
<span class="meta-text">共 {{ certs | length }} 个证书 · 目录 <code>{{ cert_dir }}</code></span>
</div>
</div>
<div class="card">
<p class="card-desc">
HTTPS 反代需要 TLS 证书。这里可以一键生成<strong>自签名证书</strong>用于快速测试,
也可以上传由 Let's Encrypt / 商业 CA 签发的正式证书(<code>.pem</code> / <code>.crt</code> + <code>.key</code>)。
<br>
<strong>⚠️ 生产环境强烈建议</strong>: 使用 <code>nginx</code> + <code>Let's Encrypt</code> 反向代理并强制 HTTPS,
而不是把本 Web 平台直接暴露在公网。
</p>
</div>
<div class="card">
<h3 class="card-title">🪄 生成自签名证书</h3>
<p class="card-desc">立即可用的测试证书,有效期可选 (1 ~ 3650 天)。建议仅用于内网 / 临时调试。</p>
<form method="post" action="{{ url_for('config_tls_generate') }}">
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
<div class="filter-grid">
<div class="form-group">
<label>Common Name (CN) <span class="hint">主机名 / 域名, 例如 proxy.example.com</span></label>
<input type="text" name="common_name" required minlength="1" maxlength="253"
placeholder="proxy.example.com" class="form-input">
</div>
<div class="form-group">
<label>有效期 (天) <span class="hint">默认 365, 最大 3650</span></label>
<input type="number" name="days" value="365" min="1" max="3650" class="form-input">
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">🔧 生成自签名证书</button>
</div>
</form>
</div>
<div class="card">
<h3 class="card-title">⬆ 上传证书</h3>
<p class="card-desc">
支持 <code>.pem</code> / <code>.crt</code> / <code>.key</code>。单个文件上限 <strong>1 MB</strong>
上传后请在表中核对<strong>主题 (CN)</strong><strong>到期时间</strong>
</p>
<form method="post" action="{{ url_for('config_tls_upload') }}"
enctype="multipart/form-data">
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
<div class="form-group">
<label>证书 / 私钥文件 (.pem / .crt / .key)</label>
<input type="file" name="certfile" required accept=".pem,.crt,.key" class="form-input">
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">📤 上传</button>
</div>
</form>
</div>
<div class="card">
<h3 class="card-title">📜 已有证书</h3>
{% if certs %}
<table class="data-table">
<thead>
<tr>
<th>文件名</th>
<th>类型</th>
<th>主题 (CN)</th>
<th>到期</th>
<th>状态</th>
<th>SHA-256 指纹</th>
<th>大小</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for c in certs %}
<tr>
<td><code>{{ c.name }}</code></td>
<td>
{% if c.is_key %}
<span class="badge badge-blue">KEY</span>
{% elif c.ext == '.pem' %}
<span class="badge badge-purple">PEM</span>
{% else %}
<span class="badge badge-cyan">CRT</span>
{% endif %}
</td>
<td>
{% if c.parsed_ok and c.cn %}
{{ c.cn }}
{% elif c.parsed_ok %}
<span class="meta-text"></span>
{% else %}
<span class="badge badge-gray">{{ c.parse_error[:30] }}</span>
{% endif %}
</td>
<td>
{% if c.parsed_ok and c.not_after %}
<span title="{{ c.expires_iso }}">{{ c.not_after }}</span>
{% else %}
<span class="meta-text"></span>
{% endif %}
</td>
<td>
{% if c.parsed_ok %}
{% if c.status == 'valid' %}
<span class="badge badge-green">有效 ({{ c.days_remaining }} 天)</span>
{% elif c.status == 'expiring' %}
<span class="badge badge-yellow">即将过期 ({{ c.days_remaining }} 天)</span>
{% elif c.status == 'expired' %}
<span class="badge badge-red">已过期</span>
{% else %}
<span class="badge badge-gray">未知</span>
{% endif %}
{% else %}
<span class="badge badge-gray">未识别</span>
{% endif %}
</td>
<td>
{% if c.sha256 %}
<code class="meta-text" title="{{ c.sha256 }}">{{ c.sha256[:16] }}…</code>
{% else %}
<span class="meta-text"></span>
{% endif %}
</td>
<td>{{ c.size }} B</td>
<td>
<form method="post" action="{{ url_for('config_tls_delete') }}"
style="display:inline;"
onsubmit="return confirm('确认删除证书 {{ c.name }}? 此操作不可恢复。');">
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
<input type="hidden" name="name" value="{{ c.name }}">
<button type="submit" class="btn btn-small btn-warning">🗑 删除</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<div class="card-empty">
<div class="empty-icon">🔐</div>
<p>暂无证书。先点击上方「生成自签名证书」或上传一个 .pem / .crt 文件。</p>
</div>
{% endif %}
</div>
<div class="card">
<h3 class="card-title">️ 使用提示</h3>
<ul>
<li>所有证书都存储在 <code>{{ cert_dir }}/</code>,文件权限建议 <code>600</code></li>
<li>自签名证书浏览器会提示<strong>不受信任</strong>,仅适合内网 / 临时测试</li>
<li>推荐的正式方案见 README 中的 <strong>TLS / HTTPS 部署指南</strong></li>
<li>删除私钥 (<code>.key</code>) 后,对应的证书将无法继续用于 HTTPS</li>
<li>上传的 .pem 同时包含证书和私钥时,<strong>请确保只有 root 才能访问本目录</strong></li>
</ul>
</div>
{% endblock %}