4a1d949e41
完整 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
148 lines
6.9 KiB
HTML
148 lines
6.9 KiB
HTML
{% 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 %} |