界面(去 AI 味): - 大屏页清除 114 处生成器残留属性 data-page-node-id - 视觉系统改回工程控制台风格:去 radial/linear-gradient、去辉光、 去标题前彩色装饰条,改为中性灰阶 + 单一蓝色强调色;KPI 色条改状态点 - 精简各页说教式长提示;修掉 profile.html 泄漏到页面上的 Markdown 星号 - 删除登录页过时的「默认账号 admin / admin123」提示(1.4.0 起已无默认口令) 安全与隐私(按「将会被公网访问」收口): - 内部异常只回 8 位事件号,完整堆栈进服务端日志(web/api.py::_internal) - 导出文件名收敛:防响应头注入与路径穿越;manage.py passwd 补用户名校验 - 登录对不存在的账号也走一次哑哈希,抹平用户名枚举的时序差异 - /api/* 读接口限速 240 次 / 60 秒 / 账号(挡住循环调 /api/bundle) - 进程 umask 0077 + 目录 0700 / 文件 0600:对话正文与主密钥的落盘权限 - 表名与库文件路径只对管理员下发;大屏页所有数据插值转义 - --debug 只允许绑定回环地址;新增 Permissions-Policy 与 413 处理器 文档: - DEPLOYMENT 新增第十三节「安全与隐私基线」;迁移表补 1.4.0 → 1.5.0 行 - SECURITY 更新支持范围、新增「信息泄漏收敛」小节与上线检查项 - .codebuddy/ 加入 .gitignore(助手工作记忆不进仓库) 版本:1.4.0 → 1.5.0(无库结构变更,user_version 仍为 4) 验证:python tools/smoke.py → ok=264 fail=0;python tools/check_docs.py → 0 处问题
80 行
3.3 KiB
HTML
80 行
3.3 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta name="color-scheme" content="dark">
|
||
<meta name="robots" content="noindex, nofollow">
|
||
<title>{% block title %}{{ project_title }}{% endblock %}</title>
|
||
<link rel="icon" href="{{ url_for('static', filename='favicon.svg') }}">
|
||
<link rel="stylesheet" href="{{ url_for('static', filename='css/app.css') }}">
|
||
</head>
|
||
<body>
|
||
{% set nav = active|default('') %}
|
||
{% set on_dash = request.path.startswith('/dashboard') %}
|
||
{# 变量名刻意叫 cur 而不是 me:模板里的 {% set %} 会覆盖子模板传入的同名变量,
|
||
而 current_user() 只含 id/username/display_name/is_admin —— 曾因此让
|
||
个人中心把 me.created_at 渲染成空(子模板的 me 是完整的用户行)。 #}
|
||
{% set cur = current_user() %}
|
||
{% if cur %}
|
||
<header class="topbar">
|
||
<div class="brand">
|
||
<span class="dot"></span>
|
||
<a href="{{ url_for('views.overview') }}"><b>{{ project_title }}</b></a>
|
||
<span class="ver">v{{ app_version }}</span>
|
||
</div>
|
||
<nav>
|
||
<a href="{{ url_for('views.overview') }}" class="{{ 'on' if nav=='overview' }}">概览</a>
|
||
<a href="{{ url_for('views.dashboard') }}" class="{{ 'on' if on_dash }}">用量大屏</a>
|
||
<a href="{{ url_for('views.records') }}" class="{{ 'on' if nav=='records' }}">数据明细</a>
|
||
<a href="{{ url_for('views.tasks') }}" class="{{ 'on' if nav=='tasks' }}">任务管理</a>
|
||
<a href="{{ url_for('views.config_page') }}" class="{{ 'on' if nav=='config' }}">配置管理</a>
|
||
{# 日志管理里是实例运行信息(数据库路径 / 账号名 / 来源 IP),仅管理员可见;
|
||
服务端另有 @admin_required 兜底,这里隐藏只是不给出会 403 的死链。 #}
|
||
{% if cur.is_admin %}
|
||
<a href="{{ url_for('views.backups_page') }}" class="{{ 'on' if nav=='backups' }}">备份管理</a>
|
||
{% endif %}
|
||
{% if cur.is_admin %}
|
||
<a href="{{ url_for('views.logs') }}" class="{{ 'on' if nav=='logs' }}">日志管理</a>
|
||
{% endif %}
|
||
{% if cur.is_admin %}
|
||
<a href="{{ url_for('views.users_page') }}" class="{{ 'on' if nav=='users' }}">用户管理</a>
|
||
{% endif %}
|
||
</nav>
|
||
<div class="me">
|
||
<a class="who" href="{{ url_for('views.profile_page') }}" title="个人中心">
|
||
<b>{{ cur.display_name }}</b>
|
||
{% if cur.is_admin %}<span class="tag accent">管理员</span>
|
||
{% else %}<span class="tag mute">普通账号</span>{% endif %}
|
||
</a>
|
||
{# 退出用 POST + CSRF:GET 型退出会被 <img src="/logout"> 这类请求静默触发 #}
|
||
<form method="post" action="{{ url_for('views.logout_post') }}" style="margin:0">
|
||
<input type="hidden" name="_csrf" value="{{ csrf_token() }}">
|
||
<button class="btn ghost sm" type="submit">退出</button>
|
||
</form>
|
||
</div>
|
||
</header>
|
||
{% endif %}
|
||
|
||
<main class="wrap">
|
||
{% with msgs = get_flashed_messages(with_categories=true) %}
|
||
{% if msgs %}
|
||
<div class="flashes">
|
||
{% for cat, m in msgs %}<div class="flash {{ cat }}">{{ m }}</div>{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
{% endwith %}
|
||
{% block body %}{% endblock %}
|
||
</main>
|
||
|
||
<footer class="foot">
|
||
{{ project_title }} · {{ project_name }} · 数据正本 <code>data/usage.sqlite</code>
|
||
</footer>
|
||
|
||
<script>
|
||
window.WB_CSRF = "{{ csrf_token() }}";
|
||
</script>
|
||
{% block scripts %}{% endblock %}
|
||
</body>
|
||
</html>
|