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
123 lines
4.7 KiB
Python
123 lines
4.7 KiB
Python
"""Unified diff generator for squid.conf changes.
|
|
|
|
Provides a small, pure-Python unified-diff renderer. We avoid Python's
|
|
``difflib.unified_diff`` because we want tight control over hunk sizing
|
|
and color classes used by the UI.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import difflib
|
|
from typing import Iterable
|
|
|
|
|
|
def unified_diff(old: str, new: str, context: int = 3, lineterm: str = "\n") -> list[dict]:
|
|
"""Return a list of diff lines as dicts: {kind, text, old_lineno, new_lineno}.
|
|
|
|
kind: 'equal' | 'add' | 'del' | 'hunk' | 'meta'
|
|
"""
|
|
old_lines = old.splitlines(keepends=False)
|
|
new_lines = new.splitlines(keepends=False)
|
|
|
|
sm = difflib.SequenceMatcher(a=old_lines, b=new_lines, autojunk=False)
|
|
out: list[dict] = []
|
|
|
|
old_no = 0
|
|
new_no = 0
|
|
|
|
def push_meta(text: str):
|
|
out.append({"kind": "meta", "text": text, "old_lineno": None, "new_lineno": None})
|
|
|
|
push_meta("--- 当前配置 (existing)")
|
|
push_meta("+++ 新配置 (proposed)")
|
|
|
|
for tag, i1, i2, j1, j2 in sm.get_opcodes():
|
|
if tag == "equal":
|
|
# show context lines (limit to first/last of long equal runs)
|
|
block = old_lines[i1:i2]
|
|
if len(block) <= context * 2 + 1:
|
|
for k, ln in enumerate(block):
|
|
old_no += 1
|
|
new_no += 1
|
|
out.append({
|
|
"kind": "equal", "text": ln,
|
|
"old_lineno": old_no, "new_lineno": new_no,
|
|
})
|
|
else:
|
|
# head
|
|
for k in range(context):
|
|
old_no += 1; new_no += 1
|
|
out.append({
|
|
"kind": "equal", "text": old_lines[i1 + k],
|
|
"old_lineno": old_no, "new_lineno": new_no,
|
|
})
|
|
out.append({
|
|
"kind": "meta", "text": f"... ({i2 - i1 - 2 * context} equal lines skipped) ...",
|
|
"old_lineno": None, "new_lineno": None,
|
|
})
|
|
old_no += (i2 - i1 - 2 * context)
|
|
new_no += (i2 - i1 - 2 * context)
|
|
# tail
|
|
for k in range(i2 - context, i2):
|
|
old_no += 1; new_no += 1
|
|
out.append({
|
|
"kind": "equal", "text": old_lines[k],
|
|
"old_lineno": old_no, "new_lineno": new_no,
|
|
})
|
|
elif tag == "replace":
|
|
for ln in old_lines[i1:i2]:
|
|
old_no += 1
|
|
out.append({
|
|
"kind": "del", "text": ln,
|
|
"old_lineno": old_no, "new_lineno": None,
|
|
})
|
|
for ln in new_lines[j1:j2]:
|
|
new_no += 1
|
|
out.append({
|
|
"kind": "add", "text": ln,
|
|
"old_lineno": None, "new_lineno": new_no,
|
|
})
|
|
elif tag == "delete":
|
|
for ln in old_lines[i1:i2]:
|
|
old_no += 1
|
|
out.append({
|
|
"kind": "del", "text": ln,
|
|
"old_lineno": old_no, "new_lineno": None,
|
|
})
|
|
elif tag == "insert":
|
|
for ln in new_lines[j1:j2]:
|
|
new_no += 1
|
|
out.append({
|
|
"kind": "add", "text": ln,
|
|
"old_lineno": None, "new_lineno": new_no,
|
|
})
|
|
return out
|
|
|
|
|
|
def summarize(diff: list[dict]) -> dict:
|
|
"""Return {adds, dels, total_changed}."""
|
|
adds = sum(1 for d in diff if d["kind"] == "add")
|
|
dels = sum(1 for d in diff if d["kind"] == "del")
|
|
return {"adds": adds, "dels": dels, "total_changed": adds + dels}
|
|
|
|
|
|
def render_html(diff: list[dict]) -> str:
|
|
"""Render diff to HTML with classes used by static/style.css."""
|
|
parts = ['<div class="diff-container">']
|
|
for d in diff:
|
|
kind = d["kind"]
|
|
text = d["text"]
|
|
# escape HTML
|
|
esc = (text.replace("&", "&").replace("<", "<").replace(">", ">"))
|
|
old_n = d.get("old_lineno") or ""
|
|
new_n = d.get("new_lineno") or ""
|
|
if kind == "add":
|
|
parts.append(f'<div class="diff-line diff-add"><span class="diff-ln">{old_n:>4}</span><span class="diff-ln">{new_n:>4}</span> + {esc}</div>')
|
|
elif kind == "del":
|
|
parts.append(f'<div class="diff-line diff-del"><span class="diff-ln">{old_n:>4}</span><span class="diff-ln">{new_n:>4}</span> - {esc}</div>')
|
|
elif kind == "equal":
|
|
parts.append(f'<div class="diff-line diff-ctx"><span class="diff-ln">{old_n:>4}</span><span class="diff-ln">{new_n:>4}</span> {esc}</div>')
|
|
else: # meta
|
|
parts.append(f'<div class="diff-line diff-hunk">{esc}</div>')
|
|
parts.append("</div>")
|
|
return "\n".join(parts)
|