Files
squid-manager/templates/ops_logrotate.html
T
Hermes Agent 4a1d949e41 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
2026-07-15 10:19:21 +08:00

148 lines
6.9 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}日志轮转 - Squid Manager{% endblock %}
{% block content %}
<div class="page-header">
<h1>🔁 日志轮转 (logrotate)</h1>
<div class="page-actions">
<span class="meta-text">配置: <code>{{ logrotate_path }}</code></span>
</div>
</div>
<div class="card">
<h3 class="card-title">📘 logrotate 怎么配合 Squid</h3>
<p class="card-desc">本页面会生成 <code>/etc/logrotate.d/squid</code>,内容由 Squid Manager 维护。流程:</p>
<ol>
<li>logrotate 检测到当前日志达到 <code>size</code> 阈值(或到 <code>daily</code> 时间)时,把当前文件改名为 <code>.1</code>;</li>
<li>logrotate 用 <code>create 0640 proxy proxy</code> 创建新空文件(原属主为 Squid 用户);</li>
<li>logrotate 执行 <code>postrotate</code> 块里的 <code>squid -k rotate</code>,让 Squid 关闭旧 fd 并重开新文件 — 这是核心联动;</li>
<li>老日志按 <code>rotate N</code> 数量保留,多余的会被删除/压缩 (<code>compress</code>);</li>
<li>当本页面「卸载」配置时,只删除 <code>/etc/logrotate.d/squid</code>,不会动 Squid 本身。</li>
</ol>
<p class="meta-text">
{% if logrotate_available %}
<span class="badge badge-green">● logrotate 可用</span>
{% else %}
<span class="badge badge-red">● logrotate 未安装</span> 请先 <code>apt install logrotate</code>
{% endif %}
</p>
</div>
<div class="grid-2">
<div class="card">
<h3 class="card-title">⚙️ 安装 / 重新生成配置</h3>
<p class="card-desc">目标文件: <code>{{ logrotate_path }}</code></p>
{% if not paths %}
<div class="card-empty">
<div class="empty-icon">📂</div>
<p>未在「<a href="{{ url_for('config_paths') }}">路径设置</a>」里配置任何日志路径,无法生成。</p>
</div>
{% else %}
<form method="post" action="{{ url_for('ops_logrotate_install') }}">
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
<div class="filter-grid">
<div class="form-group">
<label>轮转阈值 (size) <span class="hint">单文件达到此大小立即轮转,如 <code>100M</code> / <code>500K</code></span></label>
<input type="text" name="rotate_size" value="{{ install_size }}" class="form-input"
placeholder="100M" pattern="^\d+[KMG]?$">
</div>
<div class="form-group">
<label>保留份数 (rotate) <span class="hint">最多保留 N 份旧日志,超出自动删除</span></label>
<input type="number" name="rotate_count" value="{{ install_count }}" class="form-input"
min="1" max="365" step="1">
</div>
<div class="form-group">
<label>压缩 (compress)</label>
<select name="compress" class="form-input">
<option value="1" {% if install_compress %}selected{% endif %}>启用 — 旧日志用 gzip 压缩</option>
<option value="0" {% if not install_compress %}selected{% endif %}>禁用 — 保留为明文</option>
</select>
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">💾 写入配置</button>
{% if is_installed %}
<button type="button" class="btn btn-danger" id="btn-uninstall">🗑 卸载配置</button>
{% endif %}
</div>
</form>
{% if is_installed %}
<form id="uninstall-form" method="post" action="{{ url_for('ops_logrotate_uninstall') }}" style="display:none;">
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
</form>
{% endif %}
{% endif %}
</div>
<div class="card">
<h3 class="card-title">📋 当前已安装</h3>
{% if is_installed %}
<p class="meta-text">{{ current_msg }}</p>
<table class="data-table">
<tbody>
<tr><th style="width: 160px;">状态</th>
<td><span class="badge badge-green">● 已安装</span></td>
</tr>
<tr><th>路径</th><td><code>{{ logrotate_path }}</code></td></tr>
<tr><th>管理日志</th>
<td>
{% for p in paths %}
<div><code>{{ p }}</code></div>
{% endfor %}
</td>
</tr>
</tbody>
</table>
{% else %}
<p class="meta-text">
{% if not current_msg %}{% endif %}
尚未安装 logrotate 配置 ({{ current_msg or '文件不存在' }}),点击左侧表单写入。
</p>
{% endif %}
</div>
</div>
<div class="card">
<h3 class="card-title">📄 当前 {{ logrotate_path }} 内容</h3>
{% if current_content %}
<pre style="background: var(--bg-elevated); padding: 12px; border-radius: 6px; overflow-x: auto; font-size: 12px;"><code>{{ current_content }}</code></pre>
{% else %}
<p class="meta-text">暂无内容 (尚未安装)。</p>
{% endif %}
</div>
{% if preview %}
<div class="card">
<h3 class="card-title">🧪 预览 (尚未写入磁盘)</h3>
<p class="card-desc">下面是根据当前表单参数生成的预览,仅供检查。点「写入配置」才会落盘。</p>
<pre style="background: var(--bg-elevated); padding: 12px; border-radius: 6px; overflow-x: auto; font-size: 12px;"><code>{{ preview }}</code></pre>
</div>
{% endif %}
<div class="card">
<h3 class="card-title">️ 提示</h3>
<ul>
<li>权限不足时,写入会自动尝试 <code>sudo tee</code> — 需要本平台进程具备无密码 sudo,否则会回显失败原因。</li>
<li><code>size</code> 接受 logrotate 单位后缀 <code>K</code> / <code>M</code> / <code>G</code>;可与 <code>daily</code> 共存(任一条件满足即触发)。</li>
<li>卸载配置不会触碰日志文件本身 — 已存在的旧日志文件 <code>access.log.1.gz</code> 等仍保留。</li>
<li>每次「写入配置」都会记录到 <a href="{{ url_for('audit_view') }}">审计日志</a></li>
</ul>
</div>
{% endblock %}
{% block scripts %}
{% if is_installed %}
<script>
(function() {
const btn = document.getElementById('btn-uninstall');
const form = document.getElementById('uninstall-form');
if (!btn || !form) return;
btn.addEventListener('click', function() {
if (!confirm('确认卸载 logrotate 配置?\\n文件 {{ logrotate_path }} 将被删除,日志本身不会被影响。')) {
return;
}
form.submit();
});
})();
</script>
{% endif %}
{% endblock %}