Conversation
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 45 minutes and 47 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (7)
📝 Walkthrough总体概述本 PR 重构前端导航系统为动态化,集成 KaTeX 数学表达式渲染,重新设计管理后台侧边栏布局,优化代码块样式,调整项目配置和依赖清单。涉及 15 个文件的变更,包含 UI 组件、样式、类型定义和配置文件。 变更概览
代码审查工作量估计🎯 3 (Moderate) | ⏱️ ~25 分钟 关键审查点:
可能关联的 PR
建议标签
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 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: 5
🧹 Nitpick comments (5)
client/src/pages/admin/dashboard.tsx (1)
340-348: 折叠阈值与高度限制不一致(可选优化)按钮文案
+${allTags.length - 8}暗示折叠时展示 8 个标签,但实际通过max-h-[64px]控制——当标签字符长度差异较大时,可见标签数可能多于或少于 8 个,按钮里的数字会与肉眼看到的不符。如果在意可以改用基于数量的截断(allTags.slice(0, 8))或把阈值抽成常量并在两处共用。非阻塞,按需调整即可。
🤖 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 button text uses 8 as the threshold but the UI limits height via max-h-[64px], which can show a different number of tags; update the rendering to use a consistent count-based collapse (or a shared constant). Specifically, introduce and use a TAG_COLLAPSE_COUNT (or directly use allTags.slice(0, 8) when !tagExpanded) so the tag list rendering and the button text both rely on the same logic (references: allTags, tagExpanded, setTagExpanded) and remove or keep max-h styling only as a secondary visual clamp.client/src/components/admin-layout.tsx (1)
65-135: 避免在组件内部声明子组件
SidebarFooter与SidebarContent在每次AdminLayout渲染时都会被重新创建为新的函数引用,导致 React 将其视为新的组件类型,整棵子树会被卸载/重建(丢失内部状态、破坏过渡动画、增加无谓 reconciliation 开销)。建议提到模块作用域、改为useMemo返回 JSX,或直接内联。♻️ 建议改为返回 JSX 的常量(最小改动)
- const SidebarFooter = () => ( + const sidebarFooter = ( <div className="border-t border-border/40 p-[12px] space-y-[2px]"> ... </div> - ); + ); @@ - const SidebarContent = () => ( + const sidebarContent = ( <div className="flex flex-col h-full"> ... - <SidebarFooter /> + {sidebarFooter} </div> ); @@ - <SidebarContent /> + {sidebarContent} @@ - <SidebarContent /> + {sidebarContent}注意:若改为常量 JSX,记得把依赖(
location、mobileMenuOpen等闭包变量)的更新仍可正确触发重渲染——因为它们在父组件作用域内,正常工作。🤖 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 as new functions inside AdminLayout on every render causing remounts; move them out of the AdminLayout render path by converting them into stable values: either hoist them to module scope as JSX constants or compute them with useMemo inside AdminLayout so their references are stable. Ensure the hoisted/ memoized JSX still reads up-to-date values (location, setMobileMenuOpen, navGroups, mobileMenuOpen, handleLogout) by passing those as dependencies to useMemo or by keeping only presentational markup hoisted and wiring interactive props (onClick handlers, dynamic classes using isActive calculation) from AdminLayout into the JSX via props/closures. Specifically target SidebarFooter, SidebarContent and the isActive logic that uses location and setMobileMenuOpen.client/src/globals.css (1)
410-426: KaTeX 样式整体良好,建议为.math-error补上亮色主题覆盖。
.katex-display的overflow-x: auto+-webkit-overflow-scrolling: touch很好地处理了长公式在窄屏下的横向滚动;.katex { font-size: 1.05em }相对于正文 15px 约 15.75px,与代码/正文排版比例协调。唯一的小隐患:
.math-error使用oklch(0.65 0.2 25)的橙红色,在暗色背景上可读性 OK,但在亮色主题的--background: oklch(0.975 0.002 250)上对比度偏低(约 3:1 左右)。按照该文件[data-theme="light"]覆写其他颜色的惯例,建议补一条亮色覆盖。As per coding guidelines:
是否有遗漏的选择器未覆盖亮色模式。🎨 建议补充
.prose-monolith .math-error { color: oklch(0.65 0.2 25); font-size: 13px; } + +:root[data-theme="light"] .prose-monolith .math-error { + color: oklch(0.45 0.22 25); +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/src/globals.css` around lines 410 - 426, Add a light-theme override for the .prose-monolith .math-error rule so the error text has sufficient contrast on the light background; locate the existing .prose-monolith .math-error selector and add a corresponding [data-theme="light"] selector (e.g. [data-theme="light"] .prose-monolith .math-error) that sets a darker/more saturated color (or a theme variable like --text-danger) to meet contrast requirements against --background: oklch(0.975 0.002 250).client/package.json (1)
31-31: 移除@types/dompurify,依赖dompurify的内置类型定义。
dompurify@^3.4.0已内置官方 TypeScript 声明文件(dist/purify.cjs.d.ts等),@types/dompurify@^3.0.5现为纯 stub 包,无实际用途。保留两份类型定义可能导致冗余或不一致,建议移除该 devDependency。♻️ 建议修改
"@tailwindcss/vite": "^4", - "@types/dompurify": "^3.0.5", "@types/katex": "^0.16.8",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/package.json` at line 31, Remove the redundant devDependency "@types/dompurify" from package.json because dompurify@^3.4.0 already ships TypeScript declarations; update the dependencies/devDependencies block by deleting the "@types/dompurify": "^3.0.5" entry and run npm/yarn install to ensure only the built-in dompurify types are used (verify no other code imports the stub package name).client/src/lib/markdown.ts (1)
309-336: 考虑迁移至marked-katex-extension,提升数学公式处理的稳健性。当前
renderMath通过字符串替换在marked.parse之前注入 KaTeX HTML,存在以下风险:
- 代码保护不完整:仅覆盖
``` 和` ` 语法,漏掉缩进代码块(4 空格/Tab)和 ~~~ 围栏,可能错误地处理其中的 $ 符号。- 占位符恢复脆弱:依赖 marked 对零字节的保留,这是隐式依赖,未来版本变更可能导致失效。
- 块公式正则无边界:/$$([\s\S]+?)$$/ 未锚定行首行尾,可能跨越段落或列表边界。
marked-katex-extension(v5.1.8,兼容 marked v17.0.6) 将 $ / $$ 作为 marked 的 lexer token,由 marked 词法层统一处理冲突,在 DOMPurify 净化前生成纯净的 KaTeX HTML,无需担心代码块保护或占位符问题。现有的 DOMPurify 配置(含 iframe/video 白名单)与该方案完全兼容。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/src/lib/markdown.ts` around lines 309 - 336, The current renderMath function (and its preserved/ph placeholder logic using preserved and ph, plus the regex replacements for $$...$$ and $...$) is fragile and should be replaced by integrating the marked-katex-extension: install marked-katex-extension, import and register its KaTeX extension with marked (instead of doing regex replacements in renderMath), remove the placeholder handling (preserved, ph) and the manual katex.renderToString calls, and ensure the extension is applied before DOMPurify sanitization so KaTeX HTML is produced by the lexer/parser rather than by brittle string surgery; keep existing DOMPurify settings unchanged.
🤖 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`:
- Around line 59-63: The AGENTS.md "CI 与安全" section contains hard-coded secrets
("monolith2026" and explicit "GitHub PAT" mention); remove any plaintext
credentials from that section (delete the "monolith2026" literal and any PAT
references), rotate the exposed admin password immediately, and replace
doc-stored secrets with instructions to use environment variables or a secrets
manager (e.g., reference using .dev.vars or a proper secret store) in the "CI
与安全" text; then purge the secret from history using git filter-repo (or
equivalent) and update repository guidance to store secrets in ignored files
(e.g., .dev.vars) or secret management, plus ensure .gitignore excludes those
files.
In `@client/src/components/admin-layout.tsx`:
- Around line 149-184: The button that toggles mobile menu (onClick={() =>
setMobileMenuOpen(true)}) uses aria-controls="admin-mobile-navigation" but the
aside dialog (<aside role="dialog"> / SidebarContent) lacks that id and
aria-modal; add id="admin-mobile-navigation" to that aside and include
aria-modal="true" so the button correctly references the dialog and the dialog
exposes modal semantics to assistive tech.
In `@client/src/globals.css`:
- Around line 917-921: The mobile rule currently sets border-radius: 0 only for
:root[data-theme="light"] .prose-monolith .code-block-wrapper, leaving the
default/dark theme with 8px corners; make the behavior consistent by moving or
duplicating the mobile rule so that .prose-monolith .code-block-wrapper gets
border-radius: 0 for both themes (either remove the [data-theme="light"]
qualifier or add a matching selector for the default/dark theme such as
:root:not([data-theme="light"]) .prose-monolith .code-block-wrapper) and add a
short comment explaining the intentional cross-theme mobile styling change.
In `@client/src/lib/markdown.ts`:
- Around line 324-331: The inline math regex used in the md.replace call
(currently /(?<!\\)\$([^$\n]+?)\$/g) is too permissive and mis-parses currency
like " $100 "; update the pattern to enforce no leading/trailing spaces and
prevent a closing $ followed by a digit: replace the regex in the md.replace
invocation with /(?<!\\)\$(?!\s)([^$\n]+?)(?<!\s)\$(?!\d)/g while keeping the
existing katex.renderToString(tex.trim(), { displayMode: false, throwOnError:
false }) and the catch branch that returns `<code
class="math-error">${escapeHtml(tex)}</code>` unchanged.
In `@opencode.json`:
- Around line 4-11: The opencode.json GitHub MCP config uses Authorization:
"Bearer {env:GITHUB_TOKEN}" but the GITHUB_TOKEN env var isn't documented or
provisioned; add documentation and example files: create a .env.example showing
GITHUB_TOKEN, add a .dev.vars (or update existing local env guidance) with
instructions for local development, and update deployment/CI config (e.g.,
wrangler.toml and CI secrets) to pass GITHUB_TOKEN into the runtime so the
"github" -> "headers" -> "Authorization" entry works; reference the "github"
section and the Authorization header in opencode.json when updating docs and
deployment manifests.
---
Nitpick comments:
In `@client/package.json`:
- Line 31: Remove the redundant devDependency "@types/dompurify" from
package.json because dompurify@^3.4.0 already ships TypeScript declarations;
update the dependencies/devDependencies block by deleting the
"@types/dompurify": "^3.0.5" entry and run npm/yarn install to ensure only the
built-in dompurify types are used (verify no other code imports the stub package
name).
In `@client/src/components/admin-layout.tsx`:
- Around line 65-135: SidebarFooter and SidebarContent are declared as new
functions inside AdminLayout on every render causing remounts; move them out of
the AdminLayout render path by converting them into stable values: either hoist
them to module scope as JSX constants or compute them with useMemo inside
AdminLayout so their references are stable. Ensure the hoisted/ memoized JSX
still reads up-to-date values (location, setMobileMenuOpen, navGroups,
mobileMenuOpen, handleLogout) by passing those as dependencies to useMemo or by
keeping only presentational markup hoisted and wiring interactive props (onClick
handlers, dynamic classes using isActive calculation) from AdminLayout into the
JSX via props/closures. Specifically target SidebarFooter, SidebarContent and
the isActive logic that uses location and setMobileMenuOpen.
In `@client/src/globals.css`:
- Around line 410-426: Add a light-theme override for the .prose-monolith
.math-error rule so the error text has sufficient contrast on the light
background; locate the existing .prose-monolith .math-error selector and add a
corresponding [data-theme="light"] selector (e.g. [data-theme="light"]
.prose-monolith .math-error) that sets a darker/more saturated color (or a theme
variable like --text-danger) to meet contrast requirements against --background:
oklch(0.975 0.002 250).
In `@client/src/lib/markdown.ts`:
- Around line 309-336: The current renderMath function (and its preserved/ph
placeholder logic using preserved and ph, plus the regex replacements for
$$...$$ and $...$) is fragile and should be replaced by integrating the
marked-katex-extension: install marked-katex-extension, import and register its
KaTeX extension with marked (instead of doing regex replacements in renderMath),
remove the placeholder handling (preserved, ph) and the manual
katex.renderToString calls, and ensure the extension is applied before DOMPurify
sanitization so KaTeX HTML is produced by the lexer/parser rather than by
brittle string surgery; keep existing DOMPurify settings unchanged.
In `@client/src/pages/admin/dashboard.tsx`:
- Around line 340-348: The collapse behavior is inconsistent: the button text
uses 8 as the threshold but the UI limits height via max-h-[64px], which can
show a different number of tags; update the rendering to use a consistent
count-based collapse (or a shared constant). Specifically, introduce and use a
TAG_COLLAPSE_COUNT (or directly use allTags.slice(0, 8) when !tagExpanded) so
the tag list rendering and the button text both rely on the same logic
(references: allTags, tagExpanded, setTagExpanded) and remove or keep max-h
styling only as a secondary visual clamp.
🪄 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: 3256d097-039c-4bb5-b3e5-ea22aff9e7e8
⛔ Files ignored due to path filters (4)
.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!**/*.pngpackage-lock.jsonis excluded by!**/package-lock.json,!**/package-lock.json
📒 Files selected for processing (16)
.gitignore.playwright-mcp/page-2026-04-19T04-35-40-096Z.yml.playwright-mcp/page-2026-04-19T04-35-49-532Z.yml.wiki-tmpAGENTS.mdclient/package.jsonclient/src/components/admin-layout.tsxclient/src/components/footer.tsxclient/src/components/navbar.tsxclient/src/globals.cssclient/src/lib/api.tsclient/src/lib/markdown.tsclient/src/pages/admin/dashboard.tsxopencode.jsonserver/package.jsonserver/src/index.ts
💤 Files with no reviewable changes (5)
- .playwright-mcp/page-2026-04-19T04-35-49-532Z.yml
- server/package.json
- .playwright-mcp/page-2026-04-19T04-35-40-096Z.yml
- .wiki-tmp
- server/src/index.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
client/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
DOMPurify must sanitize Markdown rendered output with whitelist allowing iframe/video embeds
Files:
client/src/lib/api.tsclient/src/pages/admin/dashboard.tsxclient/src/lib/markdown.tsclient/src/components/navbar.tsxclient/src/components/footer.tsxclient/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 (6)
📓 Common learnings
Learnt from: CR
Repo: one-ea/Monolith
Timestamp: 2026-04-25T02:24:16.009Z
Learning: Use `npm run deploy:cloudflare` for one-command deployment, strictly forbidden to manually run `wrangler pages deploy`
Learnt from: CR
Repo: one-ea/Monolith
Timestamp: 2026-04-25T02:24:16.009Z
Learning: Must deploy `dist` directory from `client/` directory, otherwise Pages Functions will be omitted
Learnt from: CR
Repo: one-ea/Monolith
Timestamp: 2026-04-25T02:24:16.009Z
Learning: CI 'Sync Worker secrets' step must inject ADMIN_PASSWORD / JWT_SECRET via `wrangler secret put`
Learnt from: CR
Repo: one-ea/Monolith
Timestamp: 2026-04-25T02:24:16.009Z
Learning: When D1 migration markers are out of sync, manually `INSERT INTO d1_migrations` to resolve
Learnt from: CR
Repo: one-ea/Monolith
Timestamp: 2026-04-25T02:24:16.009Z
Learning: Use deployment unique URLs when validating Cloudflare CDN behavior due to short-lived caching
Learnt from: CR
Repo: one-ea/Monolith
Timestamp: 2026-04-25T02:24:16.009Z
Learning: Production branch is `main` (auto-deployed by Cloudflare Pages); development happens on `dev` branch; strictly forbidden to push directly to main
Learnt from: CR
Repo: one-ea/Monolith
Timestamp: 2026-04-25T02:24:16.009Z
Learning: Main branch requires 1 approval + linear history + status checks before merging
Learnt from: CR
Repo: one-ea/Monolith
Timestamp: 2026-04-25T02:24:16.009Z
Learning: Preferred workflow: dev development → commit → push → PR → squash merge to main → deploy
📚 Learning: 2026-04-21T14:30:39.825Z
Learnt from: CR
Repo: one-ea/Monolith PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-21T14:30:39.825Z
Learning: Applies to client/src/**/*.{ts,tsx} : Use DOMPurify to sanitize Markdown rendered output with whitelist allowing iframe/video embeds
Applied to files:
client/src/lib/markdown.ts
📚 Learning: 2026-04-21T14:30:39.825Z
Learnt from: CR
Repo: one-ea/Monolith PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-21T14:30:39.825Z
Learning: Applies to client/functions/tsconfig.json : Pages Functions `tsconfig.json` must independently import `cloudflare/workers-types` to avoid DOM type conflicts with the main project
Applied to files:
client/package.json
📚 Learning: 2026-04-21T14:30:39.825Z
Learnt from: CR
Repo: one-ea/Monolith PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-21T14:30:39.825Z
Learning: Frontend uses Vite 6, React 19, Tailwind CSS v4, shadcn/ui, and wouter for routing
Applied to files:
client/package.jsonclient/src/components/navbar.tsxclient/src/components/footer.tsx
📚 Learning: 2026-04-21T14:30:39.825Z
Learnt from: CR
Repo: one-ea/Monolith PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-21T14:30:39.825Z
Learning: Applies to .gitignore : `package-lock.json` must never be added to `.gitignore`
Applied to files:
.gitignore
📚 Learning: 2026-04-21T14:30:39.825Z
Learnt from: CR
Repo: one-ea/Monolith PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-21T14:30:39.825Z
Learning: Applies to client/src/**/*.{css,tsx} : Use OKLCH color space for dual theme system with dark theme (Slate & Cyan) and light theme variants
Applied to files:
client/src/globals.css
🪛 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.1)
AGENTS.md
[warning] 7-7: Spaces inside emphasis markers
(MD037, no-space-in-emphasis)
🔇 Additional comments (11)
client/src/lib/api.ts (1)
115-130: LGTM!复用
fetchJsonWithCache+ 失败回退空数组的写法稳健,类型定义清晰。sortOrder字段虽然定义但前端未参与排序,假定服务端已按该字段排好序,这一约定建议在后端接口处保持一致即可。client/src/components/navbar.tsx (1)
22-35: LGTM!动态导航链接的派生方式(
fixedStart+ 动态页面 +fixedEnd)清晰,useMemo依赖正确。顺带移除基于 index 的delay-${i+1}也合理——Tailwind v4 的 JIT 无法扫描到这种字符串拼接的类名,原本的延迟动画其实并不会生效。Also applies to: 74-89
client/src/components/footer.tsx (1)
18-39: LGTM!
fetchNavPages()内部已对失败做了空数组兜底,这里直接.then(setNavPages)不会引发未捕获的 Promise,简洁可靠。flex-wrap+gap-x/gap-y对窄屏友好,主题 token 使用一致。opencode.json (1)
23-34: 指令文件路径与.gitignore冲突。所有指令文件位于
.agents/memory_bank/下,但.gitignore第 37 行将整个.agents/目录加入忽略列表。这将导致这些指令文件无法被 Git 跟踪,OpenCode MCP 无法读取项目规则。需要调整
.gitignore规则以保留指令文件,或将指令文件移至可跟踪的目录。.gitignore (2)
48-57: 优秀的文档实践!注释部分明确列出了必须跟踪的关键文件(如
package-lock.json、client/functions/、部署脚本等),并说明了原因。这符合编码规范,且能有效避免团队成员误操作。根据编码规范:
package-lock.json必须被跟踪,当前配置正确遵守了该规则。
37-37:.gitignore设置正确,无需修改。
.agents/目录设计用于存储本地 AI 工具的敏感信息(密码、PAT、个人偏好等),因此被正确地加入忽略列表。opencode.json和AGENTS.md中的引用仅作为本地开发配置,而非 Git 跟踪的依赖。此外,package-lock.json已正确地被保护,不在忽略列表中,符合编码指南要求。AGENTS.md (2)
1-10: 项目概览清晰准确。技术栈描述与实际使用的 React 19、Tailwind CSS v4 等版本一致,架构说明简洁明了。
37-42: 部署规则完整覆盖关键要点。文档准确记录了项目的部署铁律,与学习库中的规则完全一致:
- 使用
npm run deploy:cloudflare一键部署- 从
client/目录部署以保留 Pages Functionspackage-lock.json必须跟踪- CI 通过
wrangler secret put注入密钥根据学习库:必须使用一键部署命令,严禁手动运行
wrangler pages deploy。client/package.json (1)
19-19: 依赖调整整体合理。
katex@^0.16.45与@types/katex@^0.16.8对应,当前 npm 上 katex 最新即 0.16.45,类型包最新为 0.16.8,版本匹配。shadcn作为 CLI 工具(client/src/globals.css通过@import "shadcn/tailwind.css"在构建期使用),下放到 devDependencies 符合其用途,生产 bundle 不会被拉入。Also applies to: 32-32, 40-40
client/src/lib/markdown.ts (1)
348-366: KaTeX/MathML 白名单扩展看起来合理。保留了原有
iframe/video/source/figure/figcaption以满足 B 站/YouTube 嵌入与懒加载需求,MathML 标签(math/mrow/mi/mo/mn/msup/...)与 KaTeX 常用属性(mathvariant/displaystyle/columnalign/...)均为渲染输出所需,且未放开危险属性(如href、xlink:href、事件处理器)。符合client/src/**/*.{ts,tsx}的 DOMPurify 白名单放行 iframe/video 的约束。As per coding guidelines:
DOMPurify must sanitize Markdown rendered output with whitelist allowing iframe/video embeds.client/src/globals.css (1)
598-612: 代码块横向滚动重构方案正确,z-index 层级也合理。
.code-header/.code-title-bar加position: sticky; left: 0; z-index: 5,配合.line-number的position: sticky; left: 0; z-index: 10,再把.code-line改为display: flex+.line-content { flex: 1; min-width: 0; white-space: pre },可以让内容撑开 wrapper、横向滚动时行号与顶栏始终可见,逻辑自洽。移动端对.line-number宽度(22px)与.has-line-numbers pre左 padding(8px)同步缩减也匹配得当。Also applies to: 684-687, 719-757
| ## CI 与安全 | ||
| - 仓库:`https://github.com/one-ea/Monolith` | ||
| - GitHub PAT:已配置在 system_config.md | ||
| - 后台密码:`monolith2026` | ||
| - 分支保护:main 需要 1 人审批 + 线性历史 + 状态检查 No newline at end of file |
There was a problem hiding this comment.
🚨 严重安全漏洞:文档中硬编码了管理员密码。
第 62 行直接暴露了后台管理密码 monolith2026。此文件已被 Git 跟踪,密码将永久保留在仓库历史中,即使后续删除也无法彻底清除。
安全风险:
- 任何能访问仓库的人都能看到管理员密码
- 即使仓库是私有的,这也违反了安全最佳实践
- 第 61 行提到的 GitHub PAT 也不应在文档中提及具体位置
必须立即:
- 从文档中删除所有敏感凭据(密码、PAT、token 等)
- 更改后台管理密码
- 使用环境变量或密钥管理系统存储凭据
- 考虑使用
git filter-repo从 Git 历史中清除已提交的密码
🔒 建议的修复方案
## CI 与安全
- 仓库:`https://github.com/one-ea/Monolith`
-- GitHub PAT:已配置在 system_config.md
-- 后台密码:`monolith2026`
+- GitHub PAT:通过环境变量 `GITHUB_TOKEN` 配置
+- 后台密码:通过环境变量 `ADMIN_PASSWORD` 配置(参考 `.env.example`)
- 分支保护:main 需要 1 人审批 + 线性历史 + 状态检查同时,在部署后立即更改管理员密码,并将新密码存储在 .dev.vars(已被 .gitignore 忽略)中。
📝 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.
| ## CI 与安全 | |
| - 仓库:`https://github.com/one-ea/Monolith` | |
| - GitHub PAT:已配置在 system_config.md | |
| - 后台密码:`monolith2026` | |
| - 分支保护:main 需要 1 人审批 + 线性历史 + 状态检查 | |
| ## CI 与安全 | |
| - 仓库:`https://github.com/one-ea/Monolith` | |
| - GitHub PAT:通过环境变量 `GITHUB_TOKEN` 配置 | |
| - 后台密码:通过环境变量 `ADMIN_PASSWORD` 配置(参考 `.env.example`) | |
| - 分支保护:main 需要 1 人审批 + 线性历史 + 状态检查 |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@AGENTS.md` around lines 59 - 63, The AGENTS.md "CI 与安全" section contains
hard-coded secrets ("monolith2026" and explicit "GitHub PAT" mention); remove
any plaintext credentials from that section (delete the "monolith2026" literal
and any PAT references), rotate the exposed admin password immediately, and
replace doc-stored secrets with instructions to use environment variables or a
secrets manager (e.g., reference using .dev.vars or a proper secret store) in
the "CI 与安全" text; then purge the secret from history using git filter-repo (or
equivalent) and update repository guidance to store secrets in ignored files
(e.g., .dev.vars) or secret management, plus ensure .gitignore excludes those
files.
| <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 指向不存在的元素
第 181 行的 aria-controls="admin-mobile-navigation" 引用了一个 id,但抽屉 <aside role="dialog">(第 149-153 行)并没有设置该 id,屏幕阅读器无法通过该属性建立按钮与受控区域的关联。同时建议为 dialog 补充 aria-modal="true" 以明确模态语义。
🛡️ 建议修复
<aside
role="dialog"
+ id="admin-mobile-navigation"
+ aria-modal="true"
aria-label="导航菜单"
className="relative flex flex-col w-[260px] max-w-[80vw] h-full bg-background shadow-2xl animate-in slide-in-from-left"
>📝 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.
| <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> | |
| <aside | |
| role="dialog" | |
| id="admin-mobile-navigation" | |
| aria-modal="true" | |
| 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 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-[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-[6px] text-muted-foreground/50 hover:text-foreground transition-colors" | |
| aria-label="打开导航菜单" | |
| aria-expanded={mobileMenuOpen} | |
| aria-controls="admin-mobile-navigation" | |
| > | |
| <Menu className="w-[18px] h-[18px]" /> | |
| </button> |
🤖 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 button
that toggles mobile menu (onClick={() => setMobileMenuOpen(true)}) uses
aria-controls="admin-mobile-navigation" but the aside dialog (<aside
role="dialog"> / SidebarContent) lacks that id and aria-modal; add
id="admin-mobile-navigation" to that aside and include aria-modal="true" so the
button correctly references the dialog and the dialog exposes modal semantics to
assistive tech.
| @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 只写在 [data-theme="light"] 分支下(line 918),而默认暗色主题下 .prose-monolith .code-block-wrapper 在小屏下仍保持 8px 圆角。两个主题在同一断点下的视觉表现不一致,如果这是有意的平铺移动体验,应两者都设为 0;如果仅希望亮色下去掉圆角也需在注释中说明理由。
As per coding guidelines: [data-theme="light"] 和默认暗色主题的变量是否配对。
🎨 建议方案(两主题一致去圆角)
-@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;
+ }
+}📝 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 917 - 921, The mobile rule currently
sets border-radius: 0 only for :root[data-theme="light"] .prose-monolith
.code-block-wrapper, leaving the default/dark theme with 8px corners; make the
behavior consistent by moving or duplicating the mobile rule so that
.prose-monolith .code-block-wrapper gets border-radius: 0 for both themes
(either remove the [data-theme="light"] qualifier or add a matching selector for
the default/dark theme such as :root:not([data-theme="light"]) .prose-monolith
.code-block-wrapper) and add a short comment explaining the intentional
cross-theme mobile styling change.
| // u884cu5185u516cu5f0fuff1a$...$uff08u907fu514du5339u914du8d27u5e01u7b26u53f7u5982 $100uff09 | ||
| md = md.replace(/(?<!\\)\$([^$\n]+?)\$/g, (_, tex) => { | ||
| try { | ||
| return katex.renderToString(tex.trim(), { displayMode: false, throwOnError: false }); | ||
| } catch { | ||
| return `<code class="math-error">${escapeHtml(tex)}</code>`; | ||
| } | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
What is the standard regex heuristic used by Pandoc or markdown-it-texmath to distinguish inline $...?
💡 Result:
The standard regex heuristic used by Pandoc (with the tex_math_dollars extension enabled) to distinguish inline
Citations:
- 1: https://pandoc.org/demo/example33/8.13-math.html
- 2: https://www.pandoc.org/MANUAL.html
- 3: Wont use tex_math_dollars extension when converting Markdown to LaTeX jgm/pandoc#2379
- 4: Math extension treats currency $ signs as inline math delimiters sbarex/QLMarkdown#195
- 5: Cannot include escaped dollar when using dollars delimiters goessner/markdown-it-texmath#32
🏁 Script executed:
cat -n client/src/lib/markdown.ts | sed -n '305,375p'Repository: one-ea/Monolith
Length of output: 3584
行内公式正则未遵循标准防护规则,注释与实现不符。
第 325 行的正则 /(?<!\\)\$([^$\n]+?)\$/g 只检查开头 $ 前不是反斜杠,但无法避免货币符号。对于 价格 $100 和 $200 的对比,非贪婪匹配会将 $100 和 $ 作为公式分界,捕获 100 和 交给 KaTeX,结果错误且吞掉后续 $200 前的 $。
标准做法(Pandoc、markdown-it-texmath)要求:
- 开
$后紧跟非空白字符 - 闭
$前为非空白字符 - 闭
$后不是数字
建议调整为:/(?<!\\)\$(?!\s)([^$\n]+?)(?<!\s)\$(?!\d)/g
DOMPurify 的白名单配置已正确支持 iframe/video 嵌入和 MathML/KaTeX 标签(包括 span、math、mrow 等必要元素),符合防 XSS 要求。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@client/src/lib/markdown.ts` around lines 324 - 331, The inline math regex
used in the md.replace call (currently /(?<!\\)\$([^$\n]+?)\$/g) is too
permissive and mis-parses currency like " $100 "; update the pattern to enforce
no leading/trailing spaces and prevent a closing $ followed by a digit: replace
the regex in the md.replace invocation with
/(?<!\\)\$(?!\s)([^$\n]+?)(?<!\s)\$(?!\d)/g while keeping the existing
katex.renderToString(tex.trim(), { displayMode: false, throwOnError: false })
and the catch branch that returns `<code
class="math-error">${escapeHtml(tex)}</code>` unchanged.
| "github": { | ||
| "type": "remote", | ||
| "url": "https://api.githubcopilot.com/mcp/", | ||
| "headers": { | ||
| "Authorization": "Bearer {env:GITHUB_TOKEN}" | ||
| }, | ||
| "enabled": true | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 检查 GITHUB_TOKEN 在仓库中的配置情况
echo "=== 检查工作流中的 GITHUB_TOKEN 使用 ==="
rg -n 'GITHUB_TOKEN' .github/workflows/
echo -e "\n=== 检查 wrangler.toml 中的环境变量 ==="
rg -n 'GITHUB_TOKEN' wrangler.toml 2>/dev/null || echo "wrangler.toml 中未找到"
echo -e "\n=== 检查 .env.example 中的示例配置 ==="
rg -n 'GITHUB_TOKEN' .env.example 2>/dev/null || echo ".env.example 中未找到"Repository: one-ea/Monolith
Length of output: 573
🏁 Script executed:
#!/bin/bash
# 检查所有可能的环境变量配置文件
echo "=== 查找 .dev.vars 文件 ==="
find . -name ".dev.vars" -o -name ".dev.vars*" 2>/dev/null | head -20
echo -e "\n=== 查找 opencode.json 并显示内容 ==="
find . -name "opencode.json" 2>/dev/null | head -5
cat opencode.json 2>/dev/null | head -40
echo -e "\n=== 查找所有 .env 相关文件 ==="
find . -name ".env*" -type f 2>/dev/null | grep -v node_modules | head -20
echo -e "\n=== 检查 wrangler.toml 配置 ==="
cat wrangler.toml 2>/dev/null | head -50
echo -e "\n=== 查找 GITHUB_TOKEN 的其他出现位置 ==="
rg 'GITHUB_TOKEN|GITHUB_.*TOKEN' -g '!node_modules' -l 2>/dev/null | head -20Repository: one-ea/Monolith
Length of output: 1288
环境变量 GITHUB_TOKEN 缺少配置文档和部署设置。
opencode.json 中 GitHub MCP 端点使用 {env:GITHUB_TOKEN} 进行认证,但该环境变量未在以下位置配置或文档化:
- 无
.env.example或文档说明所需的环境变量 - 无
.dev.vars文件用于本地开发环境 wrangler.toml未配置此变量- 仅在
.github/workflows/dependabot-auto-merge.yml中被 GitHub Actions 使用
建议:
- 添加
.env.example或 README 文档说明GITHUB_TOKEN的配置 - 确保部署流程(wrangler 配置或 CI/CD)能正确传递此环境变量
- 为本地开发补充
.dev.vars配置指南
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@opencode.json` around lines 4 - 11, The opencode.json GitHub MCP config uses
Authorization: "Bearer {env:GITHUB_TOKEN}" but the GITHUB_TOKEN env var isn't
documented or provisioned; add documentation and example files: create a
.env.example showing GITHUB_TOKEN, add a .dev.vars (or update existing local env
guidance) with instructions for local development, and update deployment/CI
config (e.g., wrangler.toml and CI secrets) to pass GITHUB_TOKEN into the
runtime so the "github" -> "headers" -> "Authorization" entry works; reference
the "github" section and the Authorization header in opencode.json when updating
docs and deployment manifests.
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)
- feat: KaTeX math formula rendering ($...$ inline, $$...$$ block) - fix: protect code blocks from KaTeX parsing - chore: remove orphaned .wiki-tmp submodule - chore: remove unused marked import/dep from server - chore: move @types/dompurify, shadcn to devDependencies - chore: add .playwright-mcp/, .wiki-tmp/ to .gitignore - chore: delete stray test artifacts
Summary
$...$and block$$...$$rendering with code block protection.wiki-tmpsubmodule, unusedmarkedfrom server, stray test artifacts@types/dompurifyandshadcnmoved to devDependencies.playwright-mcp/and.wiki-tmp/