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
195 lines
9.9 KiB
HTML
195 lines
9.9 KiB
HTML
{% extends "base.html" %}
|
||
{% block title %}多实例管理 - Squid Manager{% endblock %}
|
||
{% block content %}
|
||
<div class="page-header">
|
||
<h1>🖥️ 多实例管理</h1>
|
||
<div class="page-actions">
|
||
<button onclick="document.getElementById('add-form').style.display='block'" class="btn btn-primary btn-small">➕ 添加实例</button>
|
||
</div>
|
||
</div>
|
||
|
||
<p class="meta-text">
|
||
在数据库中保存多个 Squid 实例。切换"当前活动实例"后,所有配置、日志、服务控制页面都会基于该实例的路径与服务名。<br>
|
||
当前活动实例: <span class="badge badge-blue">{{ active_instance_name }}</span>
|
||
{% if active %}<span class="meta-text">(ID #{{ active.id }})</span>{% endif %}
|
||
</p>
|
||
|
||
{# -------- add new instance form (hidden by default) -------- #}
|
||
<div class="card" id="add-form" style="display:none;">
|
||
<h3 class="card-title">添加新实例</h3>
|
||
<form method="post" action="{{ url_for('instances_add') }}" class="inline-form">
|
||
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
|
||
<div class="filter-grid">
|
||
<div class="form-group">
|
||
<label>名称 <span class="hint">(唯一)</span></label>
|
||
<input type="text" name="name" class="form-input" required maxlength="80" placeholder="例如: prod-edge-01">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>描述</label>
|
||
<input type="text" name="description" class="form-input" maxlength="200" placeholder="可选">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>本机实例</label>
|
||
<select name="is_local" class="form-input">
|
||
<option value="1" selected>是 (本地)</option>
|
||
<option value="0">否 (远程 SSH)</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="config-section">
|
||
<h3 class="config-section-title">🦑 Squid 路径</h3>
|
||
<div class="filter-grid">
|
||
<div class="form-group">
|
||
<label>squid.conf 路径</label>
|
||
<input type="text" name="squid_conf_path" class="form-input" value="/etc/squid/squid.conf">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>access.log 路径</label>
|
||
<input type="text" name="access_log_path" class="form-input" value="/var/log/squid/access.log">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>cache.log 路径</label>
|
||
<input type="text" name="cache_log_path" class="form-input" value="/var/log/squid/cache.log">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>squid 二进制</label>
|
||
<input type="text" name="squid_binary" class="form-input" value="squid">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>systemctl</label>
|
||
<input type="text" name="systemctl" class="form-input" value="systemctl">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Systemd 服务名</label>
|
||
<input type="text" name="squid_service" class="form-input" value="squid">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="config-section">
|
||
<h3 class="config-section-title">🔐 SSH (仅远程实例)</h3>
|
||
<div class="filter-grid">
|
||
<div class="form-group">
|
||
<label>SSH 主机</label>
|
||
<input type="text" name="ssh_host" class="form-input" placeholder="例如: 10.0.0.5">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>SSH 用户</label>
|
||
<input type="text" name="ssh_user" class="form-input" placeholder="root">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>SSH 端口</label>
|
||
<input type="number" name="ssh_port" class="form-input" value="22" min="1" max="65535">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>SSH 私钥路径</label>
|
||
<input type="text" name="ssh_key_path" class="form-input" placeholder="/root/.ssh/id_rsa">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-actions">
|
||
<label style="display:inline-flex; align-items:center; gap:6px; margin-right:auto;">
|
||
<input type="checkbox" name="is_active" value="1">
|
||
添加后立即激活
|
||
</label>
|
||
<button type="button" onclick="document.getElementById('add-form').style.display='none'" class="btn btn-secondary btn-small">取消</button>
|
||
<button type="submit" class="btn btn-primary btn-small">💾 添加</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
{# -------- existing instances table -------- #}
|
||
<div class="card">
|
||
<h3 class="card-title">📋 已配置实例 ({{ instances|length }})</h3>
|
||
{% if not instances %}
|
||
<p class="card-empty">尚未配置任何实例。点击右上角"添加实例"开始。</p>
|
||
{% else %}
|
||
<div class="table-scroll">
|
||
<form method="post" action="{{ url_for('instances_save') }}">
|
||
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
|
||
<table class="data-table">
|
||
<thead>
|
||
<tr>
|
||
<th>名称</th>
|
||
<th>描述</th>
|
||
<th>主机</th>
|
||
<th>配置文件</th>
|
||
<th>access.log</th>
|
||
<th>状态</th>
|
||
<th>操作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for inst in instances %}
|
||
{% set is_current = (active and active.id == inst.id) %}
|
||
<tr {% if is_current %}style="background: rgba(59,130,246,0.08);"{% endif %}>
|
||
<td>
|
||
<input type="text" name="name_{{ inst.id }}" value="{{ inst.name }}" class="form-input" required maxlength="80">
|
||
</td>
|
||
<td>
|
||
<input type="text" name="description_{{ inst.id }}" value="{{ inst.description or '' }}" class="form-input" maxlength="200" placeholder="(无)">
|
||
</td>
|
||
<td>
|
||
{% if inst.is_local %}
|
||
<span class="badge badge-blue">本地</span>
|
||
{% else %}
|
||
<code>{{ inst.ssh_host or '?' }}:{{ inst.ssh_port }}</code>
|
||
<span class="meta-text">({{ inst.ssh_user or '?' }})</span>
|
||
{% endif %}
|
||
</td>
|
||
<td><code class="meta-text">{{ inst.squid_conf_path }}</code></td>
|
||
<td><code class="meta-text">{{ inst.access_log_path }}</code></td>
|
||
<td>
|
||
{% if is_current %}
|
||
<span class="badge badge-green">★ 当前活动</span>
|
||
{% elif inst.is_active %}
|
||
<span class="badge badge-yellow">默认</span>
|
||
{% else %}
|
||
<span class="meta-text">—</span>
|
||
{% endif %}
|
||
</td>
|
||
<td style="white-space:nowrap;">
|
||
{% if not is_current %}
|
||
<form method="post" action="{{ url_for('instances_activate', inst_id=inst.id) }}" style="display:inline;">
|
||
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
|
||
<input type="hidden" name="next" value="{{ request.path }}">
|
||
<button type="submit" class="btn btn-primary btn-small">▶ 激活</button>
|
||
</form>
|
||
{% endif %}
|
||
<a href="{{ url_for('instances_test', inst_id=inst.id) }}" class="btn btn-secondary btn-small" target="_blank">🔍 测试</a>
|
||
<form method="post" action="{{ url_for('instances_delete') }}" style="display:inline;" onsubmit="return confirm('确定要删除实例 {{ inst.name }} 吗?');">
|
||
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
|
||
<input type="hidden" name="id" value="{{ inst.id }}">
|
||
<button type="submit" class="btn btn-danger btn-small">🗑</button>
|
||
</form>
|
||
{# hidden checkbox used by /instances/save to set the active row #}
|
||
{% if not is_current %}
|
||
<input type="checkbox" name="is_active_{{ inst.id }}" value="1" style="display:none;">
|
||
{% endif %}
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
<div class="form-actions" style="margin-top: 12px;">
|
||
<span class="meta-text">提示: 表格中可编辑名称/描述/路径。修改后点击"保存所有修改"。</span>
|
||
<button type="submit" class="btn btn-primary">💾 保存所有修改</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h3 class="card-title">ℹ️ 说明</h3>
|
||
<ul>
|
||
<li>当前活动实例的路径/服务名会覆盖 <code>/config/paths</code> 中的全局设置</li>
|
||
<li>切换实例后,所有现有页面 (config/*、logs、service) 自动基于新实例的路径</li>
|
||
<li>应用级参数 (日志解析上限、缓存秒数等) 仍然是全局的,不会因实例切换而变化</li>
|
||
<li>远程实例的 SSH 凭据当前仅记录,配置读写仍需通过本地文件系统或 SSHFS 挂载</li>
|
||
<li>"测试"按钮检查 conf_path 是否可读、access/cache log 是否存在</li>
|
||
</ul>
|
||
</div>
|
||
{% endblock %} |