Conversation
Critical/High: - Sanitize custom_header/footer through DOMPurify before injection, forbid inline scripts, only allow external script src (fixes #1) - Add security headers: X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, HSTS (fixes #5) - Remove authorEmail from public comment API, use DiceBear avatars based on nickname instead of Gravatar hash (fixes #2, #14) - Add login rate limiting: 5 attempts per 15 min per IP (fixes #3) - Reflect request Origin in CORS instead of wildcard (fixes #4) - Add SSRF protection for WebDAV backup and image localization: only allow https://, block private/internal IPs (fixes #6, #13) - Filter javascript: URIs in markdown link renderer (fixes #7) Medium: - Replace hardcoded reaction salt with REACTION_SALT env var (fixes #9) - Add .env.* to .gitignore, remove .env.production from tracking (fixes #10) - Disable source maps in production build (fixes #11) - Remove infrastructure details from health endpoint (fixes #15)
…PR notice - Add CookieConsent component: bottom banner with accept/reject, stores consent in localStorage, fires 'cookie-consent-accepted' event for third-party script injection gating - Add /privacy route with full privacy policy page (bilingual) - Gate custom_header/footer script injection behind cookie consent: scripts only load after user accepts; non-script HTML injects immediately as before - Add GDPR warning in admin settings 'Extensions & Injection' tab - Add PRIVACY.md to repo root - Add privacy page link in cookie consent banner
- code-block-wrapper 增加 -webkit-overflow-scrolling: touch(iOS 惯性滚动) - pre 和 code 元素增加 white-space: pre 强制不换行,内容真实撑开 wrapper - 移动端 pre 增加 width: max-content + min-width: 100% 确保可超出容器宽度 - code-line 从 display: inline-block 改为 display: block,避免 min-width: 100% 在移动端按父容器宽度错误计算导致代码不撑开横向滚动区域 - 兜底 pre:not(.hljs) 同步修复
code-line 改回 display: inline-block,移除 width: max-content 和 min-width: calc(100%+8px) 的循环依赖写法(该写法导致移动端代码 内容完全消失)。pre 的 white-space: pre 已足够阻止换行, wrapper 的 overflow-x: auto 负责横向滚动。
- navbar: 从 /api/pages 获取 showInNav=true 的页面动态注入导航栏 - footer: 底部新增独立页面链接 - admin-layout: 侧栏改用 fixed 定位,彻底脱离文档流 - dashboard: 标签区折叠展开,默认显示2行+展开按钮 - globals.css: 移动端代码块优化(行号flex布局、顶栏sticky、尺寸缩减) - .gitignore: 重写并清理不存在的条目 - 清理 .kilo 和 .playwright-mcp 残留 - 新增 AGENTS.md 和 opencode.json 项目配置
📝 WalkthroughSummary by CodeRabbit发布说明
Walkthrough本次更新通过引入动态导航页面获取能力、重构前端组件结构和样式,以及补充项目文档和开发配置来增强应用功能。新增API接口支持按需加载展示在导航栏中的页面,同时优化了admin布局、代码块样式和移动端响应式表现。 Changes
Sequence Diagram(s)sequenceDiagram
participant Browser as Browser<br/>(Client)
participant Navbar as Navbar<br/>Component
participant Footer as Footer<br/>Component
participant API as API<br/>(/api/pages)
participant Cache as Cache<br/>(60s TTL)
Browser->>Navbar: Mount Navbar component
Navbar->>API: fetchNavPages()
API->>Cache: Check cache
alt Cache Hit
Cache-->>API: Return cached data
else Cache Miss
API-->>API: Fetch /api/pages
API->>Cache: Store with 60s TTL
end
API-->>Navbar: NavPage[] (filtered by showInNav)
Navbar->>Navbar: useMemo: Build nav links<br/>(/, /page/*, /archive, /about)
Navbar-->>Browser: Render navigation
Browser->>Footer: Mount Footer component
Footer->>API: fetchNavPages()
API->>Cache: Check cache
Cache-->>API: Return cached data
API-->>Footer: NavPage[]
Footer->>Footer: Conditional render<br/>nav section
Footer-->>Browser: Render nav pages + footer text
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
✨ Simplify code
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
client/src/components/admin-layout.tsx (1)
65-135: 🛠️ Refactor suggestion | 🟠 Major子组件定义在父组件内部会导致每次渲染重新挂载
SidebarFooter和SidebarContent定义在AdminLayout内部,每次AdminLayout渲染(如mobileMenuOpen、location变化)时它们都是全新的组件类型,React 会卸载旧树并重新挂载内部所有子节点(Link、ThemeToggle等均会丢失内部状态/动画)。建议提升为模块级组件,或改为普通渲染函数(renderSidebar())直接返回 JSX,避免组件身份每次变化。建议改动
- const SidebarFooter = () => ( + const renderSidebarFooter = () => ( <div className="border-t border-border/40 p-[12px] space-y-[2px]"> ... </div> ); - const SidebarContent = () => ( + const renderSidebarContent = () => ( <div className="flex flex-col h-full"> ... - <SidebarFooter /> + {renderSidebarFooter()} </div> );并在使用处改为
{renderSidebarContent()}。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/src/components/admin-layout.tsx` around lines 65 - 135, SidebarFooter and SidebarContent are declared inside AdminLayout which causes them to be recreated on every render and forces React to remount their subtrees; move SidebarFooter and SidebarContent out of the AdminLayout function to module-level components (or convert them to pure render functions like renderSidebarContent/renderSidebarFooter that return JSX) and update AdminLayout to use the top-level SidebarFooter and SidebarContent (or call the renderSidebar... functions) so their identity is stable across renders and child state/animations are preserved.
🧹 Nitpick comments (3)
client/src/components/footer.tsx (1)
18-20:fetchNavPages的 Promise rejection 未处理虽然
fetchNavPages内部已 try/catch 并返回[],但如果未来实现变化,这里没有.catch会抛未处理拒绝。建议与上面fetch一致显式补一个.catch(() => {})防御。此外,navbar 与 footer 都在挂载时独立调用fetchNavPages,得益于fetchJsonWithCache的 in-flight 去重,实际只发一次请求——改动合理。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/src/components/footer.tsx` around lines 18 - 20, The useEffect in footer calls fetchNavPages().then(setNavPages) without handling Promise rejections; update the effect to mirror navbar by appending a .catch(() => {}) to the fetchNavPages() promise chain so any rejection is swallowed and cannot become an unhandled rejection (keep setNavPages in the then branch); this change touches the useEffect that calls fetchNavPages and aligns with the in-flight dedupe behavior of fetchJsonWithCache.client/src/lib/api.ts (1)
123-130: 建议:服务端直接过滤 + 排序,避免客户端重复处理目前
/api/pages返回所有已发布页面(含showInNav=false),客户端再过滤。这会把不需要的页面数据打包进缓存并暴露到公共 API。建议在getPublishedPages()中按showInNav=true过滤,或新增专用的/api/pages/nav端点。另外,此处依赖服务端按sortOrder升序返回,但客户端类型已暴露sortOrder字段却未做兜底排序,建议.sort((a,b)=>a.sortOrder-b.sortOrder)以防后端实现变更。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/src/lib/api.ts` around lines 123 - 130, fetchNavPages currently fetches all pages from "/api/pages" and filters client-side; change the server to return only nav pages (filter showInNav=true in getPublishedPages or add a new endpoint /api/pages/nav) so fetchNavPages calls that endpoint (via fetchJsonWithCache) to avoid caching/exposing non-nav pages, and also add a defensive client-side sort fallback in fetchNavPages by sorting the returned NavPage[] by sortOrder (e.g., .sort((a,b)=>a.sortOrder - b.sortOrder)) to guard against backend order changes.opencode.json (1)
14-14: 固定@playwright/mcp版本,避免使用@latest。使用
@latest会导致行为随时间漂移。官方发布历史显示存在多个破坏性变化:--no-sandbox标志重命名、--isolated→--in-memory参数变更、移除--shared-browser-context选项等。建议固定至当前稳定版本(如@playwright/mcp@0.0.70)以确保可复现性和可审计性。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@opencode.json` at line 14, The command entry currently uses the unstable floating tag "@playwright/mcp@latest"; change the package spec in the "command" array to a pinned, stable version (e.g., "@playwright/mcp@0.0.70") to prevent behavioral drift—update the JSON "command" value where the package string appears so the CI/local runs use a fixed mcp version.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@AGENTS.md`:
- Line 7: 行内 Markdown 对 `/api/* /cdn/* /rss.xml` 中的星号触发 MD037 报警;在 AGENTS.md
中定位包含文本 "代理层:Pages Functions(反向代理 /api/* /cdn/* /rss.xml)"
的行,然后将每个路径或整个路径序列包裹成代码样式(例如用反引号)或对星号进行转义,以消除 MD037 lint 警告并保持可读性。
- Around line 61-63: Remove the sensitive entries in AGENTS.md: delete the
explicit "GitHub PAT:已配置在 system_config.md" line and the plaintext backend
password "monolith2026", and replace them with a short instruction stating that
credentials must never be stored in repo docs and should be injected at runtime
via the organization's secret management solution (e.g., "Use secret manager /
environment-injected credentials only"). Ensure any mention of branch protection
stays but does not reference credential locations; also run a repo-wide search
for the strings "GitHub PAT" and "monolith2026" and remove or rotate any other
occurrences.
- Line 47: AGENTS.md 中第 47 行关于 Hono v4 的 verify() 用法不准确;更新该行将声明从“verify() 需要 3
个参数:verify(token, secret, "HS256")”改为说明 verify(token: string, secret: string,
alg?: 'HS256'),即算法参数为可选且默认 HS256,因此可以直接调用 verify(token, secret),并在同处添加对 Hono
当前锁定版本官方文档链接 https://hono.dev/docs/helpers/jwt 作为参考以防版本变化。
In `@client/src/components/admin-layout.tsx`:
- Around line 149-184: The mobile menu button uses
aria-controls="admin-mobile-navigation" but no element has that id; update the
mobile drawer (the <aside> that renders SidebarContent in admin-layout.tsx) to
include id="admin-mobile-navigation" (or add the id to the internal <nav> inside
SidebarContent) and also add aria-modal="true" alongside role="dialog" to
reflect modal semantics; ensure the id you add matches the aria-controls on the
button and keep the existing setMobileMenuOpen state logic unchanged.
In `@client/src/components/navbar.tsx`:
- Around line 28-35: navLinks builds hrefs by concatenating p.slug into
`/page/${p.slug}` which breaks for spaces/Chinese/special characters; update the
construction inside the useMemo (and the analogous footer code) to URL-encode
the slug using encodeURIComponent(p.slug) so generated hrefs are valid and
routes resolve correctly, and keep the dependency array unchanged (still depends
on navPages).
In `@client/src/globals.css`:
- Around line 898-902: The mobile-only border-radius override is currently
scoped to :root[data-theme="light"] .prose-monolith .code-block-wrapper which
leaves the dark/default theme unaffected; change the media-query rule so it
applies regardless of theme by moving/removing the [data-theme="light"]
qualifier (i.e., use `@media` (max-width: 768px) { .prose-monolith
.code-block-wrapper { border-radius: 0; } }) or add an equivalent rule for the
dark/default theme to ensure both themes get border-radius: 0 on mobile.
- Around line 699-712: The highlighted backgrounds are getting clipped when
lines scroll horizontally because .code-line is constrained to the container
width while .line-content can shrink; update .code-line (not .line-content) to
allow its box to grow with content and still fill short lines by setting it to
an inline-flex layout with width: max-content and min-width: 100% (keep existing
padding/margins/border-radius), so long lines expand the background past the
viewport while short lines still occupy the full container width; reference
.code-line and .line-content and ensure .line-content retains white-space: pre
and min-width: 0.
In `@client/src/pages/admin/dashboard.tsx`:
- Around line 340-348: The collapse behavior is inconsistent: the UI decides
collapse by allTags.length > 8 but actually clips by CSS max-h-[64px], which can
silently hide tags when labels wrap; change the collapsed rendering in the tags
block (where allTags, tagExpanded and setTagExpanded are used) to explicitly
render only the first N items when !tagExpanded (e.g. render allTags.slice(0, 8)
and keep the "+N" button logic) and remove the pixel height clip (drop
max-h-[64px] overflow-hidden) or replace it with a proper line-clamp approach if
you prefer; additionally, update the right column that uses lg:sticky
lg:top-[24px] to include a height constraint and scrolling (e.g. add
max-h-[calc(100vh-48px)] overflow-y-auto) so the sticky sidebar can scroll when
its content exceeds the viewport.
In `@opencode.json`:
- Around line 23-33: The opencode.json "instructions" array references files in
the ignored memory_bank directory (entries under the memory_bank folder), which
will be missing on clean clones/CI; update the "instructions" array to point to
a tracked location or to a public read-only mirror (or add a checked-in copy) so
CI and new dev machines can access them reliably, and ensure any loader falls
back to an alternate source (env var or URL) if those entries are unavailable.
---
Outside diff comments:
In `@client/src/components/admin-layout.tsx`:
- Around line 65-135: SidebarFooter and SidebarContent are declared inside
AdminLayout which causes them to be recreated on every render and forces React
to remount their subtrees; move SidebarFooter and SidebarContent out of the
AdminLayout function to module-level components (or convert them to pure render
functions like renderSidebarContent/renderSidebarFooter that return JSX) and
update AdminLayout to use the top-level SidebarFooter and SidebarContent (or
call the renderSidebar... functions) so their identity is stable across renders
and child state/animations are preserved.
---
Nitpick comments:
In `@client/src/components/footer.tsx`:
- Around line 18-20: The useEffect in footer calls
fetchNavPages().then(setNavPages) without handling Promise rejections; update
the effect to mirror navbar by appending a .catch(() => {}) to the
fetchNavPages() promise chain so any rejection is swallowed and cannot become an
unhandled rejection (keep setNavPages in the then branch); this change touches
the useEffect that calls fetchNavPages and aligns with the in-flight dedupe
behavior of fetchJsonWithCache.
In `@client/src/lib/api.ts`:
- Around line 123-130: fetchNavPages currently fetches all pages from
"/api/pages" and filters client-side; change the server to return only nav pages
(filter showInNav=true in getPublishedPages or add a new endpoint
/api/pages/nav) so fetchNavPages calls that endpoint (via fetchJsonWithCache) to
avoid caching/exposing non-nav pages, and also add a defensive client-side sort
fallback in fetchNavPages by sorting the returned NavPage[] by sortOrder (e.g.,
.sort((a,b)=>a.sortOrder - b.sortOrder)) to guard against backend order changes.
In `@opencode.json`:
- Line 14: The command entry currently uses the unstable floating tag
"@playwright/mcp@latest"; change the package spec in the "command" array to a
pinned, stable version (e.g., "@playwright/mcp@0.0.70") to prevent behavioral
drift—update the JSON "command" value where the package string appears so the
CI/local runs use a fixed mcp version.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6fe863e3-15ec-4d98-9a8e-bfa07bc09e50
⛔ Files ignored due to path filters (3)
.kilo/package-lock.jsonis excluded by!**/package-lock.json,!**/package-lock.json.playwright-mcp/console-2026-04-19T04-35-34-885Z.logis excluded by!**/*.log.playwright-mcp/page-2026-04-19T04-35-58-544Z.pngis excluded by!**/*.png
📒 Files selected for processing (11)
.gitignore.playwright-mcp/page-2026-04-19T04-35-40-096Z.yml.playwright-mcp/page-2026-04-19T04-35-49-532Z.ymlAGENTS.mdclient/src/components/admin-layout.tsxclient/src/components/footer.tsxclient/src/components/navbar.tsxclient/src/globals.cssclient/src/lib/api.tsclient/src/pages/admin/dashboard.tsxopencode.json
💤 Files with no reviewable changes (2)
- .playwright-mcp/page-2026-04-19T04-35-40-096Z.yml
- .playwright-mcp/page-2026-04-19T04-35-49-532Z.yml
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
client/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use DOMPurify to sanitize Markdown rendered output with whitelist allowing iframe/video embedding
Files:
client/src/pages/admin/dashboard.tsxclient/src/components/navbar.tsxclient/src/components/footer.tsxclient/src/lib/api.tsclient/src/components/admin-layout.tsx
client/src/pages/**
⚙️ CodeRabbit configuration file
client/src/pages/**: 页面级组件。审查时请关注: 1. 数据加载和错误处理是否完善 2. SEO 相关(页面标题、meta 标签) 3. 导航和路由是否正确
Files:
client/src/pages/admin/dashboard.tsx
client/src/components/**
⚙️ CodeRabbit configuration file
client/src/components/**: 这是 React 前端组件目录。审查时请关注: 1. 是否同时兼容暗色和亮色主题(检查 CSS 变量和 data-theme) 2. 响应式布局是否完整(移动端/平板/桌面端) 3. 无障碍访问(aria 标签、键盘导航) 4. 组件是否保持单一职责
Files:
client/src/components/navbar.tsxclient/src/components/footer.tsxclient/src/components/admin-layout.tsx
.gitignore
📄 CodeRabbit inference engine (AGENTS.md)
package-lock.jsonmust never be added to.gitignore
Files:
.gitignore
client/src/globals.css
⚙️ CodeRabbit configuration file
client/src/globals.css: 全局样式和 CSS 变量系统。审查时请关注: 1. [data-theme="light"] 和默认暗色主题的变量是否配对 2. OKLCH 色值的明度/色度是否合理 3. 是否有遗漏的选择器未覆盖亮色模式
Files:
client/src/globals.css
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: one-ea/Monolith
Timestamp: 2026-04-21T14:25:25.791Z
Learning: Use adapter pattern for storage layer: routes should only depend on `IDatabase` / `IObjectStorage` interfaces, with provider switching via `DB_PROVIDER` / `STORAGE_PROVIDER` environment variables
Learnt from: CR
Repo: one-ea/Monolith
Timestamp: 2026-04-21T14:25:25.791Z
Learning: Use `npm run deploy:cloudflare` for deployments; never manually execute `wrangler pages deploy`
Learnt from: CR
Repo: one-ea/Monolith
Timestamp: 2026-04-21T14:25:25.791Z
Learning: Always deploy from `client/` directory to ensure `dist` includes Pages Functions, never from other directories
Learnt from: CR
Repo: one-ea/Monolith
Timestamp: 2026-04-21T14:25:25.791Z
Learning: Use dev branch for feature development; follow dev → commit → push → PR → squash merge to main workflow; never push directly to main branch
Learnt from: CR
Repo: one-ea/Monolith
Timestamp: 2026-04-21T14:25:25.791Z
Learning: Frontend uses Vite 6, React 19, Tailwind CSS v4, shadcn/ui, and wouter for routing
Learnt from: CR
Repo: one-ea/Monolith
Timestamp: 2026-04-21T14:25:25.791Z
Learning: Backend uses Hono Workers with Drizzle ORM for data access
Learnt from: CR
Repo: one-ea/Monolith
Timestamp: 2026-04-21T14:25:25.791Z
Learning: Storage system supports D1 (SQLite), R2 (object storage), with ability to switch to Turso/PostgreSQL/S3
Learnt from: CR
Repo: one-ea/Monolith
Timestamp: 2026-04-21T14:25:25.791Z
Learning: CI must inject ADMIN_PASSWORD and JWT_SECRET via `wrangler secret put` in the Sync Worker secrets step
🪛 LanguageTool
AGENTS.md
[grammar] ~41-~41: Ensure spelling is correct
Context: ....gitignore - CI "Sync Worker secrets" 步骤通过 wrangler secret put 注入 ADMIN_PASSWORD...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🪛 markdownlint-cli2 (0.22.0)
AGENTS.md
[warning] 7-7: Spaces inside emphasis markers
(MD037, no-space-in-emphasis)
🔇 Additional comments (3)
.gitignore (1)
46-49:package-lock.json跟踪策略正确,符合仓库规范。这里明确声明了
package-lock.json必须纳入版本管理,和当前.gitignore规则保持一致。As per coding guidelines
.gitignore:package-lock.jsonmust never be added to.gitignore.client/src/globals.css (2)
410-427: LGTM:横向滚动职责划分更清晰。由 wrapper/独立
pre负责横向滚动,pre保持white-space: pre,并补上 iOS 惯性滚动,整体对移动端代码块更稳。Also applies to: 477-486
578-592: LGTM:横向滚动时的固定元素处理到位。
code-header、code-title-bar和行号都固定在left: 0,并设置了层级和背景,能减少移动端横向滚动时的 UI 丢失感。Also applies to: 653-667, 715-729
| Monolith 是基于 Cloudflare 边缘计算的全栈博客系统。 | ||
| - 前端:Vite 6 + React 19 + Tailwind CSS v4 + shadcn/ui + wouter | ||
| - 后端:Hono Workers + Drizzle ORM | ||
| - 代理层:Pages Functions(反向代理 /api/* /cdn/* /rss.xml) |
There was a problem hiding this comment.
修复 Markdown 语法以避免 lint 告警。
/api/* /cdn/* /rss.xml 这类写法容易触发 MD037,建议对 * 转义或整体包裹代码样式,避免 CI 文档检查噪音。
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)
[warning] 7-7: Spaces inside emphasis markers
(MD037, no-space-in-emphasis)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@AGENTS.md` at line 7, 行内 Markdown 对 `/api/* /cdn/* /rss.xml` 中的星号触发 MD037
报警;在 AGENTS.md 中定位包含文本 "代理层:Pages Functions(反向代理 /api/* /cdn/* /rss.xml)"
的行,然后将每个路径或整个路径序列包裹成代码样式(例如用反引号)或对星号进行转义,以消除 MD037 lint 警告并保持可读性。
| - D1 迁移标记不同步时需手动 `INSERT INTO d1_migrations` | ||
| - Cloudflare CDN 有短暂缓存,验证时用部署唯一 URL | ||
| - 本地 `client/functions/api/` 目录的 tsconfig 独立,不与主项目混用 | ||
| - Hono v4 `verify()` 需要 3 个参数:`verify(token, secret, "HS256")` |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In Hono v4, what is the exact function signature of jwt verify(), and does it require an explicit algorithm argument like "HS256"?
💡 Result:
In Hono v4 (hono/jwt), the jwt verify helper function signature is: verify(token: string, secret: string, alg?: 'HS256'; issuer?: string | RegExp): Promise It does NOT require an explicit algorithm argument like "HS256". Reason: the docs show the alg parameter as optional (alg?: ... ) and note that the default is HS256. In other words, you can call verify(token, secret) and Hono will use the default algorithm.
Citations:
更正 Hono v4 jwt verify() 参数用法。
AGENTS.md 第 47 行声称 verify() 需要 3 个参数,但根据 Hono v4 官方文档,算法参数是可选的。正确签名为 verify(token: string, secret: string, alg?: 'HS256'),默认采用 HS256,可直接调用 verify(token, secret) 而无需传入第三参数。建议更新文档说明,并引用 Hono 当前锁定版本的官方文档链接(https://hono.dev/docs/helpers/jwt)以避免版本更新时的信息偏差。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@AGENTS.md` at line 47, AGENTS.md 中第 47 行关于 Hono v4 的 verify()
用法不准确;更新该行将声明从“verify() 需要 3 个参数:verify(token, secret, "HS256")”改为说明
verify(token: string, secret: string, alg?: 'HS256'),即算法参数为可选且默认 HS256,因此可以直接调用
verify(token, secret),并在同处添加对 Hono 当前锁定版本官方文档链接
https://hono.dev/docs/helpers/jwt 作为参考以防版本变化。
| - GitHub PAT:已配置在 system_config.md | ||
| - 后台密码:`monolith2026` | ||
| - 分支保护:main 需要 1 人审批 + 线性历史 + 状态检查 No newline at end of file |
There was a problem hiding this comment.
文档中出现敏感信息与明文密码,需立即移除。
GitHub PAT 存放位置说明 + 明文后台密码会直接扩大泄露面。请改为“仅通过密钥管理系统注入”,不要在仓库文档中出现任何可用凭据或固定口令。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@AGENTS.md` around lines 61 - 63, Remove the sensitive entries in AGENTS.md:
delete the explicit "GitHub PAT:已配置在 system_config.md" line and the plaintext
backend password "monolith2026", and replace them with a short instruction
stating that credentials must never be stored in repo docs and should be
injected at runtime via the organization's secret management solution (e.g.,
"Use secret manager / environment-injected credentials only"). Ensure any
mention of branch protection stays but does not reference credential locations;
also run a repo-wide search for the strings "GitHub PAT" and "monolith2026" and
remove or rotate any other occurrences.
| <aside | ||
| role="dialog" | ||
| aria-label="导航菜单" | ||
| className="relative flex flex-col w-[260px] max-w-[80vw] h-full bg-background shadow-2xl animate-in slide-in-from-left" | ||
| > | ||
| <SidebarContent /> | ||
| </aside> | ||
| </div> | ||
| )} | ||
|
|
||
| {/* Main Content */} | ||
| <main className="flex-1 flex flex-col min-w-0"> | ||
| {/* Mobile Header */} | ||
| <header className="md:hidden sticky top-0 z-40 flex items-center justify-between px-4 h-14 border-b bg-background/80 backdrop-blur-md"> | ||
| <div className="flex items-center gap-2 font-semibold"> | ||
| <div className="w-6 h-6 rounded bg-foreground text-background flex items-center justify-center text-xs font-bold">M</div> | ||
| <span>Admin</span> | ||
| <main className="md:ml-[240px] min-h-screen overflow-y-auto"> | ||
| <header className="md:hidden sticky top-0 z-40 flex items-center justify-between px-[16px] h-[52px] border-b border-border/40 bg-background/80 backdrop-blur-md shrink-0"> | ||
| <div className="flex items-center gap-[8px] font-semibold text-[14px]"> | ||
| <div className="w-[24px] h-[24px] rounded bg-foreground text-background flex items-center justify-center text-[11px] font-bold">M</div> | ||
| <span>Admin</span> | ||
| </div> | ||
| <div className="flex items-center gap-2"> | ||
| <div className="flex items-center gap-[4px]"> | ||
| <ThemeToggle /> | ||
| <a | ||
| href="/" | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| className="p-[6px] text-muted-foreground/50 hover:text-foreground transition-colors" | ||
| aria-label="查看站点" | ||
| > | ||
| <ExternalLink className="w-[16px] h-[16px]" /> | ||
| </a> | ||
| <button | ||
| onClick={() => setMobileMenuOpen(true)} | ||
| className="p-2 -mr-2 text-muted-foreground hover:text-foreground" | ||
| aria-label="打开后台导航菜单" | ||
| aria-expanded={mobileMenuOpen} | ||
| className="p-[6px] text-muted-foreground/50 hover:text-foreground transition-colors" | ||
| aria-label="打开导航菜单" | ||
| aria-expanded={mobileMenuOpen} | ||
| aria-controls="admin-mobile-navigation" | ||
| > | ||
| <Menu className="w-5 h-5" /> | ||
| <Menu className="w-[18px] h-[18px]" /> | ||
| </button> |
There was a problem hiding this comment.
aria-controls 指向的元素不存在
移动端菜单按钮声明了 aria-controls="admin-mobile-navigation",但 aside / nav 上都没有 id="admin-mobile-navigation",屏幕阅读器无法定位被控元素。请给 mobile 抽屉的 <aside> 或其中的 <nav> 加上对应 id。顺便建议 role="dialog" 配合 aria-modal="true" 使用,以匹配真正的模态语义。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@client/src/components/admin-layout.tsx` around lines 149 - 184, The mobile
menu button uses aria-controls="admin-mobile-navigation" but no element has that
id; update the mobile drawer (the <aside> that renders SidebarContent in
admin-layout.tsx) to include id="admin-mobile-navigation" (or add the id to the
internal <nav> inside SidebarContent) and also add aria-modal="true" alongside
role="dialog" to reflect modal semantics; ensure the id you add matches the
aria-controls on the button and keep the existing setMobileMenuOpen state logic
unchanged.
| const navLinks = useMemo( | ||
| () => [ | ||
| ...fixedStart, | ||
| ...navPages.map((p) => ({ href: `/page/${p.slug}`, label: p.title })), | ||
| ...fixedEnd, | ||
| ], | ||
| [navPages], | ||
| ); |
There was a problem hiding this comment.
slug 未进行 URL 编码
`/page/${p.slug}` 直接拼接 slug,若后端未强校验 slug 字符集(服务端 /api/pages 未对 slug 做格式校验),含中文/空格/特殊字符的 slug 会生成无法正确路由的 href。建议 encodeURIComponent(p.slug),footer 同位置也建议同步处理。
- ...navPages.map((p) => ({ href: `/page/${p.slug}`, label: p.title })),
+ ...navPages.map((p) => ({ href: `/page/${encodeURIComponent(p.slug)}`, label: p.title })),📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const navLinks = useMemo( | |
| () => [ | |
| ...fixedStart, | |
| ...navPages.map((p) => ({ href: `/page/${p.slug}`, label: p.title })), | |
| ...fixedEnd, | |
| ], | |
| [navPages], | |
| ); | |
| const navLinks = useMemo( | |
| () => [ | |
| ...fixedStart, | |
| ...navPages.map((p) => ({ href: `/page/${encodeURIComponent(p.slug)}`, label: p.title })), | |
| ...fixedEnd, | |
| ], | |
| [navPages], | |
| ); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@client/src/components/navbar.tsx` around lines 28 - 35, navLinks builds hrefs
by concatenating p.slug into `/page/${p.slug}` which breaks for
spaces/Chinese/special characters; update the construction inside the useMemo
(and the analogous footer code) to URL-encode the slug using
encodeURIComponent(p.slug) so generated hrefs are valid and routes resolve
correctly, and keep the dependency array unchanged (still depends on navPages).
| /* 代码行布局 */ | ||
| .prose-monolith pre code .code-line { | ||
| display: inline-block; | ||
| min-width: 100%; | ||
| display: flex; | ||
| align-items: baseline; | ||
| padding: 0 4px; | ||
| margin: 0 -4px; | ||
| border-radius: 2px; | ||
| } | ||
|
|
||
| .prose-monolith pre code .line-content { | ||
| flex: 1; | ||
| min-width: 0; | ||
| white-space: pre; | ||
| } |
There was a problem hiding this comment.
避免长行高亮背景在横向滚动后被截断。
line-content 允许收缩到容器宽度后,长代码只是在视觉上溢出,.code-line 自身背景宽度可能仍停留在视口宽度;这会影响后面的 line-highlight / diff-add / diff-del 长行展示。建议让行盒按内容撑开,同时保留短行至少占满容器宽度。
🎨 建议调整
.prose-monolith pre code .code-line {
display: flex;
align-items: baseline;
+ width: max-content;
+ min-width: 100%;
padding: 0 4px;
margin: 0 -4px;
border-radius: 2px;
}
.prose-monolith pre code .line-content {
- flex: 1;
- min-width: 0;
+ flex: 0 0 auto;
+ min-width: max-content;
white-space: pre;
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@client/src/globals.css` around lines 699 - 712, The highlighted backgrounds
are getting clipped when lines scroll horizontally because .code-line is
constrained to the container width while .line-content can shrink; update
.code-line (not .line-content) to allow its box to grow with content and still
fill short lines by setting it to an inline-flex layout with width: max-content
and min-width: 100% (keep existing padding/margins/border-radius), so long lines
expand the background past the viewport while short lines still occupy the full
container width; reference .code-line and .line-content and ensure .line-content
retains white-space: pre and min-width: 0.
| @media (max-width: 768px) { | ||
| :root[data-theme="light"] .prose-monolith .code-block-wrapper { | ||
| border-radius: 0; | ||
| } | ||
| } |
There was a problem hiding this comment.
移动端圆角覆盖应同时适用于暗色主题。
这里把 border-radius: 0 限定在 light theme,默认暗色主题移动端仍保留 8px,会造成双主题响应式表现不一致。建议改成主题无关规则。
🎨 建议调整
`@media` (max-width: 768px) {
- :root[data-theme="light"] .prose-monolith .code-block-wrapper {
+ .prose-monolith .code-block-wrapper {
border-radius: 0;
}
}As per coding guidelines, client/src/globals.css: 审查时请关注:1. [data-theme="light"] 和默认暗色主题的变量是否配对 3. 是否有遗漏的选择器未覆盖亮色模式.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @media (max-width: 768px) { | |
| :root[data-theme="light"] .prose-monolith .code-block-wrapper { | |
| border-radius: 0; | |
| } | |
| } | |
| `@media` (max-width: 768px) { | |
| .prose-monolith .code-block-wrapper { | |
| border-radius: 0; | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@client/src/globals.css` around lines 898 - 902, The mobile-only border-radius
override is currently scoped to :root[data-theme="light"] .prose-monolith
.code-block-wrapper which leaves the dark/default theme unaffected; change the
media-query rule so it applies regardless of theme by moving/removing the
[data-theme="light"] qualifier (i.e., use `@media` (max-width: 768px) {
.prose-monolith .code-block-wrapper { border-radius: 0; } }) or add an
equivalent rule for the dark/default theme to ensure both themes get
border-radius: 0 on mobile.
| <div className="flex items-center justify-between mb-[8px]"> | ||
| <h3 className="text-[10px] font-medium text-muted-foreground/30 uppercase tracking-wider">标签</h3> | ||
| {allTags.length > 8 && ( | ||
| <button onClick={() => setTagExpanded(!tagExpanded)} className="text-[10px] text-cyan-400/60 hover:text-cyan-400 transition-colors"> | ||
| {tagExpanded ? "收起" : `+${allTags.length - 8}`} | ||
| </button> | ||
| )} | ||
| </div> | ||
| <div className={`flex flex-wrap gap-[4px] ${!tagExpanded ? "max-h-[64px] overflow-hidden" : ""}`}> |
There was a problem hiding this comment.
折叠阈值与高度裁剪不一致
展开按钮判定是 allTags.length > 8,但折叠态使用 max-h-[64px](约 2 行)做像素裁剪。当标签名较长、8 个标签已经换成 3 行,或者标签数 ≤ 8 但单个较长导致超过 2 行时,标签会被静默裁掉且没有展开入口。建议统一以行数/数量为准,例如使用 line-clamp 思路或只显示前 N 个再以 +N 展示其余数量。
同时,两栏布局 lg:grid-cols-[1fr_220px] 下右侧栏用 lg:sticky lg:top-[24px],若侧栏高度超过视口会出现无法滚动到底部的问题,建议加 max-h-[calc(100vh-48px)] overflow-y-auto 以兼顾长内容场景。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@client/src/pages/admin/dashboard.tsx` around lines 340 - 348, The collapse
behavior is inconsistent: the UI decides collapse by allTags.length > 8 but
actually clips by CSS max-h-[64px], which can silently hide tags when labels
wrap; change the collapsed rendering in the tags block (where allTags,
tagExpanded and setTagExpanded are used) to explicitly render only the first N
items when !tagExpanded (e.g. render allTags.slice(0, 8) and keep the "+N"
button logic) and remove the pixel height clip (drop max-h-[64px]
overflow-hidden) or replace it with a proper line-clamp approach if you prefer;
additionally, update the right column that uses lg:sticky lg:top-[24px] to
include a height constraint and scrolling (e.g. add max-h-[calc(100vh-48px)]
overflow-y-auto) so the sticky sidebar can scroll when its content exceeds the
viewport.
| "instructions": [ | ||
| ".agents/memory_bank/README.md", | ||
| ".agents/memory_bank/master_preferences.md", | ||
| ".agents/memory_bank/monolith_architecture.md", | ||
| ".agents/memory_bank/monolith_roadmap.md", | ||
| ".agents/memory_bank/monolith_v1_status.md", | ||
| ".agents/memory_bank/monolith_security_hardening.md", | ||
| ".agents/memory_bank/system_config.md", | ||
| ".agents/memory_bank/ui_design_parameters.md", | ||
| ".agents/memory_bank/eslint_security_guide.md", | ||
| ".agents/memory_bank/typecho_dev_guide.md" |
There was a problem hiding this comment.
instructions 指向被忽略目录,干净环境下会失效。
当前配置依赖 .agents/memory_bank/*,但该目录被 .gitignore 忽略,新的开发机或 CI 无法稳定拿到这些文件。建议改为可追踪路径,或提供可公开同步的只读副本目录。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@opencode.json` around lines 23 - 33, The opencode.json "instructions" array
references files in the ignored memory_bank directory (entries under the
memory_bank folder), which will be missing on clean clones/CI; update the
"instructions" array to point to a tracked location or to a public read-only
mirror (or add a checked-in copy) so CI and new dev machines can access them
reliably, and ensure any loader falls back to an alternate source (env var or
URL) if those entries are unavailable.
Summary
/api/pages获取showInNav=true的页面动态注入导航栏,归档和关于始终在最后fixed定位,彻底脱离文档流,内容区独立滚动sticky left-0、顶栏sticky left-0,尺寸压缩释放内容空间