mirror of
https://github.com/KingHWJ/WeReadBoard.git
synced 2026-11-03 18:24:58 +08:00
feat(footer): 展示网站 ICP 备案信息
This commit is contained in:
1 parent
c220dcad7a
commit
fb0cf30086
4 files changed
+26
-1
No files matched your search
@@ -57,6 +57,12 @@ GitHub Actions 的 `.github/workflows/deploy.yml` 只监听 `master` 分支。
|
||||
|
||||
会话签名密钥从 `WEREAD_SESSION_SECRET` 读取且必须稳定配置;缺失时服务对动态页面和 API 以 503 失败关闭。永久会话时长是固定 5 天,不因每次请求续期。Cookie 为 HttpOnly、SameSite=Lax,并默认 Secure;仅本机 HTTP 预览可以关闭 Secure。生产站点须使用 HTTPS。前端 JSON 写请求从页面 meta 标签读取 CSRF token 并放入 `X-CSRF-Token` 请求头,表单请求使用隐藏字段。
|
||||
|
||||
## 网站备案页脚
|
||||
|
||||
正式看板页脚保留数据状态,并展示可点击的 ICP 备案号,链接至工信部备案查询平台。窄屏时隐藏次要的数据快照,备案号仍可见。
|
||||
|
||||
例:正式域名为 `laohang.space`、备案号为 `浙ICP备202607916号-1`,首页页脚显示该备案号;点击后打开 `https://beian.miit.gov.cn/`。截图里的公安联网备案数据码不是公安备案号,不作为备案号码展示。
|
||||
|
||||
应用未内置登录尝试限流;公网部署应在可信反向代理上为 `POST /auth/login` 设置频率限制,且不要直接信任未校验的 `X-Forwarded-For`。
|
||||
|
||||
## 示例与真实接口的边界
|
||||
|
||||
@@ -148,6 +148,8 @@ DEPLOY_BRANCH=master ./deploy.sh
|
||||
|
||||
浏览器旧目标不会自动覆盖数据库。在原访问地址打开“阅读目标”,点击“导入已有目标”;数据库已有同一 ID 时跳过,整批成功才标记导入完成。`weread-goals` 原数据保留,`weread-goals-imported` 只记录已导入。不同浏览器、域名和端口的旧存储需要分别在原地址导入。
|
||||
|
||||
正式网站域名 `laohang.space` 的页脚展示工信部 ICP 备案号 `浙ICP备202607916号-1`,点击后跳转至工信部备案查询平台。公安联网备案数据码不是公安备案号,因此不在页脚作为公安备案信息展示。
|
||||
|
||||
保存失败会保留表单并提示重试;读取失败显示错误和重试按钮,不冒充空列表。若新建提示 ID 已存在,刷新目标列表确认上次请求是否已经保存。数据库不可用或尚未执行迁移时,目标接口返回 503,不退回浏览器保存。
|
||||
|
||||
验证命令:
|
||||
|
||||
@@ -4,13 +4,17 @@
|
||||
<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>阅间 · 微信读书看板</title>
|
||||
<link rel="stylesheet" href="/static/css/styles.css">
|
||||
<style>
|
||||
.footer{flex-wrap:wrap;gap:12px}.footer-meta{display:flex;align-items:center;gap:12px}.icp-link{color:inherit;text-decoration:none}.icp-link:hover{color:var(--accent)}
|
||||
@media(max-width:600px){.footer-state{display:none}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<svg style="display:none" aria-hidden="true"><defs>
|
||||
<symbol id="book" viewBox="0 0 24 24"><path d="M3 4c4-1 6 0 9 2 3-2 5-3 9-2v15c-4-1-6 0-9 2-3-2-5-3-9-2zM12 6v15"/></symbol><symbol id="grid" viewBox="0 0 24 24"><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></symbol><symbol id="note" viewBox="0 0 24 24"><path d="M13 4H5v16h14v-8M11 13l1-4 7-7 3 3-7 7z"/></symbol><symbol id="search-icon" viewBox="0 0 24 24"><circle cx="10" cy="10" r="6.5"/><path d="m15 15 6 6"/></symbol><symbol id="chart" viewBox="0 0 24 24"><path d="M4 3v17h17M8 15v-4m5 4V7m5 8V4"/></symbol><symbol id="clock" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 6v6l4 2"/></symbol><symbol id="moon" viewBox="0 0 24 24"><path d="M20 14A9 9 0 0 1 10 3a9 9 0 1 0 10 11Z"/></symbol><symbol id="sun" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2m0 16v2M2 12h2m16 0h2M5 5l1 1m12 12 1 1M5 19l1-1M18 6l1-1"/></symbol><symbol id="sync" viewBox="0 0 24 24"><path d="M20 7v5h-5M4 17v-5h5M6 6a8 8 0 0 1 14 6M4 12a8 8 0 0 0 14 6"/></symbol><symbol id="flag" viewBox="0 0 24 24"><path d="M5 21V3c5-4 9 4 14 0v11c-5 4-9-4-14 0"/></symbol>
|
||||
</defs></svg>
|
||||
<aside class="sidebar"><div class="logo"><div class="logo-mark"><svg><use href="#book"/></svg></div><div>阅间<small>READING SPACE</small></div></div><div class="navlabel">我的阅读空间</div><nav class="nav" aria-label="主导航"><button class="active" data-page="overview" title="阅读概览"><svg><use href="#grid"/></svg><span>阅读概览</span></button><button data-page="shelf" title="我的书架"><svg><use href="#book"/></svg><span>我的书架</span><em>8</em></button><button data-page="goals" title="阅读目标"><svg><use href="#flag"/></svg><span>阅读目标</span></button><button data-page="notes" title="笔记与划线"><svg><use href="#note"/></svg><span>笔记与划线</span></button><button data-page="discover" title="发现好书"><svg><use href="#search-icon"/></svg><span>发现好书</span></button></nav><div class="sidebar-bottom"><div class="connection"><span class="dot"></span>微信读书 · 个人空间<small>让每一页,都留下痕迹</small></div><div class="profile"><div class="avatar">读</div><div>我的阅读空间<small>数据来自你的微信读书账号</small></div></div></div></aside>
|
||||
<main class="main"><header class="topbar"><div class="breadcrumb">我的空间 <span>/ <b id="crumb" style="font-weight:400">阅读概览</b></span></div><div class="top-actions"><label class="searchbox"><svg><use href="#search-icon"/></svg><input id="global-search" placeholder="搜索书籍、书单…" aria-label="全局搜索"><span class="key">⌘ K</span></label><button class="iconbtn" id="theme" aria-label="切换至暗色主题"><svg><use href="#moon"/></svg></button><div class="avatar">L</div><form method="post" action="{{ url_for('auth.logout') }}"><input type="hidden" name="csrf_token" value="{{ csrf_token() }}"><button class="text-button logout-button" type="submit">退出</button></form></div></header><div class="demo-banner"><span><span class="dot"></span><span id="conn-state">正在连接微信读书…</span></span><span>阅读目标跨浏览器保存</span></div><div id="page-content"></div><footer class="footer"><span>阅间 · 与好书相伴,与自己相遇</span><span id="footer-state">数据来自微信读书个人账号</span></footer></main>
|
||||
<main class="main"><header class="topbar"><div class="breadcrumb">我的空间 <span>/ <b id="crumb" style="font-weight:400">阅读概览</b></span></div><div class="top-actions"><label class="searchbox"><svg><use href="#search-icon"/></svg><input id="global-search" placeholder="搜索书籍、书单…" aria-label="全局搜索"><span class="key">⌘ K</span></label><button class="iconbtn" id="theme" aria-label="切换至暗色主题"><svg><use href="#moon"/></svg></button><div class="avatar">L</div><form method="post" action="{{ url_for('auth.logout') }}"><input type="hidden" name="csrf_token" value="{{ csrf_token() }}"><button class="text-button logout-button" type="submit">退出</button></form></div></header><div class="demo-banner"><span><span class="dot"></span><span id="conn-state">正在连接微信读书…</span></span><span>阅读目标跨浏览器保存</span></div><div id="page-content"></div><footer class="footer"><span>阅间 · 与好书相伴,与自己相遇</span><div class="footer-meta"><span class="footer-state" id="footer-state">数据来自微信读书个人账号</span><a class="icp-link" href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer">浙ICP备202607916号-1</a></div></footer></main>
|
||||
<dialog id="detail" aria-label="内容详情"><div class="dialog-top"><h2 id="detail-title">书籍详情</h2><button class="iconbtn" data-action="close-detail" aria-label="关闭详情">✕</button></div><div id="detail-content"></div></dialog>
|
||||
<dialog id="goal-editor" aria-label="编辑阅读目标"><form id="goal-form"><div class="dialog-top"><div><div class="eyebrow">MAKE ROOM FOR READING</div><h2 id="editor-title">新建阅读目标</h2></div><button type="button" class="iconbtn" data-action="close-editor" aria-label="关闭目标编辑">✕</button></div><label class="field">目标名称<input id="goal-name" maxlength="60" placeholder="例如:秋季文学计划" required></label><div class="date-fields"><label class="field">开始日期<input id="goal-start" type="date" required></label><label class="field">结束日期<input id="goal-end" type="date" required></label></div><div class="section-head picker-head"><h3>想读些什么?</h3><span id="selection-count" class="pill"></span></div><p class="hint">选择书籍或加入书单当前书目,重复书籍只算一本,已读完直接计入完成。</p><label class="field"><input id="picker-search" placeholder="筛选书名、作者或书单…" aria-label="筛选目标书目"></label><div id="goal-picker"></div><p id="goal-error" class="form-error" role="alert"></p><div class="editor-footer"><span>保存后可在其他浏览器查看</span><button class="primary" type="submit">保存阅读目标</button></div></form></dialog>
|
||||
<div id="toast" class="hidden" role="status"></div><script src="/static/js/weapi.js"></script><script src="/static/js/app.js"></script></body></html>
|
||||
@@ -80,6 +80,19 @@ class AuthTests(unittest.TestCase):
|
||||
self.assertEqual(client.get("/api/reading/status").status_code, 401)
|
||||
self.assertEqual(client.get("/api/v2/status").status_code, 401)
|
||||
|
||||
def test_home_footer_displays_icp_record_link(self):
|
||||
with patch.dict(os.environ, {"WEREAD_SESSION_SECRET": "test-secret"}):
|
||||
web_app = importlib.import_module("app").app
|
||||
web_app.config.update(SECRET_KEY="test-secret", SESSION_COOKIE_SECURE=False)
|
||||
client = web_app.test_client()
|
||||
with client.session_transaction() as session:
|
||||
session["auth_user"] = "reader"
|
||||
|
||||
response = client.get("/")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn(b"https://beian.miit.gov.cn/", response.data)
|
||||
self.assertIn("浙ICP备202607916号-1".encode(), response.data)
|
||||
|
||||
def test_first_account_requires_setup_token_and_is_stored_as_hash(self):
|
||||
page = self.client.get("/login")
|
||||
csrf = self.csrf(page)
|
||||
|
||||
Reference in new issue
Block a user