Conversation
- 新增 client/src/lib/seo-analyzer.ts: SEO 评分算法(318 行) - 新增 client/src/pages/admin/seo.tsx: SEO 仪表盘页面(343 行) - client/src/components/admin-layout.tsx: 加 SEO 入口导航 - client/src/app.tsx: 注册 /admin/seo lazy route - client/src/pages/admin/dashboard.tsx: 文章列表独立滚动容器 - 新增 client/functions/sitemap.xml.ts: 代理 /sitemap.xml 到 Workers - 新增 client/functions/robots.txt.ts: 代理 /robots.txt 到 Workers - server/src/index.ts: Bindings 加 SITE_ORIGIN,sitemap/robots 优先读取 - server/wrangler.toml: 配置 SITE_ORIGIN=https://monolith-client.pages.dev
|
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 47 minutes and 6 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 selected for processing (4)
📝 WalkthroughWalkthrough本PR实现完整的SEO分析功能,包括SEO检查引擎、管理后台页面、Pages Functions代理层,以及后端环境变量支持。提供按维度分类的评分系统、故障检查、关键词提取和内容质量评估。 Changes
Sequence DiagramsequenceDiagram
participant User as 用户
participant Page as AdminSeo页面
participant API as 后端API
participant PF as Pages Functions
participant Engine as SEO引擎
User->>Page: 访问/admin/seo
activate Page
Page->>API: fetchAdminPosts()
activate API
API-->>Page: 所有文章数据
deactivate API
Page->>PF: GET /sitemap.xml
activate PF
PF->>API: 代理请求
API-->>PF: XML响应
PF-->>Page: 流式响应
deactivate PF
Page->>PF: GET /robots.txt
activate PF
PF->>API: 代理请求
API-->>PF: 文本响应
PF-->>Page: 流式响应
deactivate PF
Page->>Engine: buildOverview(文章数据)
activate Engine
Engine->>Engine: 遍历计算analyzePost()
Engine->>Engine: 聚合维度评分、关键词、分布
Engine-->>Page: SeoOverview报告
deactivate Engine
Page->>Page: 过滤/搜索/排序
Page-->>User: 渲染评分表、检查项、关键词云
deactivate Page
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes 关注点:SEO分析引擎的13项检查规则是否准确(尤其是标题/摘要长度、中英文关键词双语提取、图片alt检测);seo.tsx的过滤/排序/搜索逻辑一致性;双主题/响应式支持(暗色评分环配色、表格滚动布局);Pages Functions代理的流式处理和错误恢复。 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 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: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
server/src/index.ts (1)
336-336:⚠️ Potential issue | 🟡 MinorRSS feed 未同步使用
SITE_ORIGIN,绝对 URL 仍可能错位
/rss.xml中siteUrl仍然是new URL(c.req.url).origin,没有像 sitemap/robots 一样优先读c.env.SITE_ORIGIN。如果 RSS 端点在 Pages 域下也通过 Functions 代理(或将来添加),生成的<link>/<guid>会指向 Workers 自身域名,与 sitemap 的输出不一致。建议同步切换:
- const siteUrl = new URL(c.req.url).origin; + const siteUrl = c.env.SITE_ORIGIN || new URL(c.req.url).origin;另外:本 PR 在
client/functions/加了 sitemap/robots 代理,但rss.xml没有;若希望 RSS 也走 Pages 域名访问(避免 SPA fallback 截取),还需补一个client/functions/rss.xml.ts。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/src/index.ts` at line 336, 在生成 RSS 的地方不要直接用 new URL(c.req.url).origin — 改为优先使用环境变量 SITE_ORIGIN(即当 c.env.SITE_ORIGIN 存在时用它),否则回退到 new URL(c.req.url).origin;在 server/src/index.ts 中更新 siteUrl 的赋值(变量名 siteUrl)以先读 c.env.SITE_ORIGIN。若还希望 RSS 通过 Pages 域名暴露以避免 SPA fallback,另外新增一个代理函数 client/functions/rss.xml.ts(与 sitemap/robots 相同的代理逻辑)。
🧹 Nitpick comments (8)
client/src/lib/seo-analyzer.ts (2)
127-127: slug 规范校验逻辑在多处重复,建议抽取共享函数相同的判定
/^[a-z0-9-]+$/.test(slug) && !slug.includes("--") && !slug.startsWith("-") && !slug.endsWith("-")同时出现在client/src/pages/admin/dashboard.tsx:281,且server/src/index.ts中没有对应的服务端校验。建议在
client/src/lib/seo-analyzer.ts顶部导出一个isValidSlug(slug: string): boolean,dashboard 复用之;服务端创建/更新文章接口也建议加上同样的校验,避免脏数据进入数据库。♻️ 抽取共享函数
+/** 校验 slug 是否为合法的 URL 友好格式(小写字母+数字+连字符,不允许首尾或连续连字符) */ +export function isValidSlug(slug: string): boolean { + return /^[a-z0-9-]+$/.test(slug) + && !slug.includes("--") + && !slug.startsWith("-") + && !slug.endsWith("-"); +} + export function analyzePost(p: AnalyzeInput): PostSeoReport { const checks: SeoCheckResult[] = []; ... - const slugOk = /^[a-z0-9-]+$/.test(p.slug) && !p.slug.includes("--") && !p.slug.startsWith("-") && !p.slug.endsWith("-"); + const slugOk = isValidSlug(p.slug);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/src/lib/seo-analyzer.ts` at line 127, Duplicate slug validation logic is repeated and missing server-side checks; extract the predicate into a shared function isValidSlug(slug: string): boolean at the top of client/src/lib/seo-analyzer.ts (implement the existing logic: /^[a-z0-9-]+$/.test(slug) && !slug.includes("--") && !slug.startsWith("-") && !slug.endsWith("-")), export it, then replace the inline checks in client/src/pages/admin/dashboard.tsx (where p.slug is validated) to call isValidSlug, and add the same validation call in the server article create/update handlers in server/src/index.ts to reject invalid slugs before persisting.
264-270: 当publishedPosts === 0时totalScore会是 0,不够友好新站/全部草稿场景下,
publishedReports为空,所有dimensionScores落到0,totalScore也变成0,仪表盘显示"红色 0%"会误导用户认为站点 SEO 极差。建议在没有已发布文章时直接返回中性状态(如
null/--),由 UI 层判定显示空态。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/src/lib/seo-analyzer.ts` around lines 264 - 270, When there are no published posts the current calculation produces 0 which is misleading; update the logic around dimensionScores/totalScore to short-circuit when there are no published posts (check the existing publishedPosts or publishedReports counter) and return a neutral value (e.g. null or undefined) instead of 0 so the UI can render an empty state. Locate the aggregation using dimAcc and the computed dimensionScores and adjust the code that computes totalScore to first test publishedPosts/publishedReports === 0 and handle that case by returning the neutral sentinel rather than computing/rounding a 0 score.server/src/index.ts (1)
373-373: 对SITE_ORIGIN做最小规范化,避免运维误配如果运维误填了带尾斜杠的值(例如
"https://monolith-client.pages.dev/"),/sitemap.xml会输出<loc>https://monolith-client.pages.dev//posts/foo</loc>、/robots.txt会出现Sitemap: https://.../sitemap.xml之前多一个斜杠。建议在使用前做一次trim+ 去尾斜杠:🛡️ 防御性规范化
- const siteUrl = c.env.SITE_ORIGIN || new URL(c.req.url).origin; + const siteUrl = (c.env.SITE_ORIGIN?.trim().replace(/\/+$/, "")) || new URL(c.req.url).origin;可考虑抽成 helper 在两处复用。
Also applies to: 428-428
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/src/index.ts` at line 373, The SITE_ORIGIN value may include trailing whitespace or a trailing slash causing double-slashes in outputs; normalize it before use by trimming whitespace and removing any trailing slash, then fall back to new URL(c.req.url).origin if empty. Update the code that builds siteUrl (const siteUrl = c.env.SITE_ORIGIN || new URL(c.req.url).origin) to call a small helper like normalizeSiteOrigin(value) that returns the trimmed value without a trailing slash (and returns null/undefined for empty strings) and use that helper in both places where SITE_ORIGIN is read (the siteUrl assignment and the robots/sitemap generation code) so all consumers get the canonical origin.server/wrangler.toml (1)
9-10: 自定义域名切换时记得同步更新 SITE_ORIGIN当前硬编码为 Pages 预览域名
https://monolith-client.pages.dev。一旦绑定自定义域,需要同步修改此值,否则 sitemap 中的<loc>与 robots 中的Sitemap:仍会指向 Pages 默认域。考虑在注释中加一句提醒,或后续改为按部署环境注入。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@server/wrangler.toml` around lines 9 - 10, The SITE_ORIGIN value is hardcoded to "https://monolith-client.pages.dev" which will break sitemap/robots when switching to a custom domain; update SITE_ORIGIN whenever you bind a custom domain or change the implementation to read an environment/deployment variable (e.g., process.env.SITE_ORIGIN or equivalent runtime injection) instead of the literal, and add a short inline comment by the SITE_ORIGIN declaration reminding maintainers to sync this value with any custom domain changes.client/src/pages/admin/dashboard.tsx (1)
179-179:max-h-[calc(100vh-260px)]依赖魔法值,需关注上方区域高度变化
260px是当前“顶栏 + 数据概览 + 搜索框”累计高度的硬编码估算。后续若新增/删除概览卡或调整间距,列表区会出现被截断或底部留白。可选改进方向(非必须):
- 将外层
<div className="mx-auto …">改为flex flex-col min-h-screen,把列表容器的lg:max-h改为lg:flex-1 lg:min-h-0,让浏览器自动算高;- 或者使用 CSS Grid 的
grid-template-rows: auto auto auto 1fr让列表区天然占满剩余空间。当前实现功能正常,仅是维护性建议。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/src/pages/admin/dashboard.tsx` at line 179, The hardcoded lg:max-h-[calc(100vh-260px)] on the div with className "flex flex-col min-h-0 lg:max-h-[calc(100vh-260px)]" relies on a magic 260px value and should be made flexible: update the surrounding layout (the outer wrapper element currently using "mx-auto …") to use a full-height flex container (e.g., "flex flex-col min-h-screen") and change this inner list container to use flexible growth instead of a calc max-height (e.g., "flex flex-col min-h-0 lg:flex-1 lg:min-h-0") so the list naturally fills remaining space; alternatively replace the layout with a CSS Grid using grid-template-rows: auto auto auto 1fr to let the list area occupy the leftover space.client/functions/robots.txt.ts (1)
8-8: 需导出ApiEnv,然后在所有 PagesFunction 中使用统一的类型声明
_shared.ts中的ApiEnv接口未被导出,建议先在_shared.ts第 1 行改为export interface ApiEnv,然后在robots.txt.ts、sitemap.xml.ts、rss.xml.ts、api/[[path]].ts、cdn/[[path]].ts中统一改为PagesFunction<ApiEnv>。虽然
getBackendUrl已正确处理undefined的情况(通过可选链?.和 null 检查),但类型声明中将API_BASE: string(必需)与实际的API_BASE?: string(可选)不一致会误导类型检查。统一使用导出的ApiEnv可提高类型安全性。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/functions/robots.txt.ts` at line 8, 在 _shared.ts 中将 interface ApiEnv 改为导出(export interface ApiEnv),然后把 robots.txt.ts 中的 PagesFunction<{ API_BASE: string }> 以及 sitemap.xml.ts、rss.xml.ts、api/[[path]].ts、cdn/[[path]].ts 中的类似声明统一替换为 PagesFunction<ApiEnv>;保持 getBackendUrl 对 API_BASE 可选性的处理不变(因为实际是 API_BASE?: string),以确保类型声明与实际行为一致并提升类型安全。client/src/pages/admin/seo.tsx (2)
67-70:document.title未在卸载时还原。切换到其他 admin 子路由时,标题会保留为「SEO 优化 | Monolith」直到下个页面再次覆写;如果某个目标页没设置 title,浏览器标签会一直定格在此。建议在
useEffect中返回清理函数还原原始标题,或改用统一的useDocumentTitle工具。useEffect(() => { + const prev = document.title; document.title = "SEO 优化 | Monolith"; loadAll(); + return () => { + document.title = prev; + }; }, []);As per coding guidelines: 页面级组件审查时关注「SEO 相关(页面标题、meta 标签)」。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/src/pages/admin/seo.tsx` around lines 67 - 70, The effect that sets document.title inside useEffect currently changes the page title without restoring it; modify the useEffect in the component to capture the original title (const prev = document.title) before setting document.title = "SEO 优化 | Monolith" and return a cleanup function that restores document.title = prev, or replace the manual logic by using the shared useDocumentTitle hook to set and auto-restore the title; reference the existing useEffect and loadAll calls when making the change.
58-58: 正则解析 sitemap 不解码 XML 实体,建议改用DOMParser。
/<loc>([^<]+)<\/loc>/g取到的字符串若包含&/'等实体不会被还原,进入 UI 后展示和点击跳转都会失真(虽然当前后端生成的 14 条 URL 暂未触发,但带 query 参数或多语言路径的链接很容易踩到)。- const urls = Array.from(smRes.matchAll(/<loc>([^<]+)<\/loc>/g)).map((m) => m[1]); + const doc = new DOMParser().parseFromString(smRes, "application/xml"); + const urls = Array.from(doc.getElementsByTagName("loc")).map((n) => n.textContent ?? "").filter(Boolean);顺带还能在
doc.querySelector("parsererror")上识别后端返回非法 XML 的情况。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@client/src/pages/admin/seo.tsx` at line 58, The sitemap parsing using smRes.matchAll(/<loc>([^<]+)<\/loc>/g) doesn't decode XML entities; replace this with DOMParser: parse smRes into a document, check for parsing errors via doc.querySelector("parsererror") and handle/report them, then extract URLs by selecting all <loc> elements (e.g., doc.querySelectorAll("loc")) and reading each element's textContent to get decoded URLs before building the urls array; update the code that currently assigns urls from smRes.matchAll to use this DOMParser-based approach and add error handling for invalid XML.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@client/functions/robots.txt.ts`:
- Around line 8-22: The onRequest handler currently forwards any HTTP method and
all request headers to the backend, which is unnecessary and unsafe for static
endpoints; update onRequest (and related logic using getBackendUrl,
buildTargetUrl, createPlainApiBaseErrorResponse) to only allow GET and HEAD
(return 405 for others), and send a minimal, explicit header set (e.g., Accept
and User-Agent) instead of context.request.headers so cookies/Authorization are
not leaked; also wrap the fetch(target, ...) in try/catch and add a timed fetch
(or AbortController) so failures return a controlled 502/plain-text response
rather than bubbling an exception.
In `@client/src/lib/seo-analyzer.ts`:
- Around line 280-285: globalChecks currently hardcodes all items as passing
which is misleading when features like rss_enabled or a missing API_BASE can
make /rss.xml, /sitemap.xml or /robots.txt return 404/500; replace the static
pass values by performing runtime probes in the AdminSeo UI: call
fetch('/rss.xml', { method: 'HEAD' }) and fetch('/sitemap.xml'|'/robots.txt', {
method: 'HEAD' }) (handle network errors and non-2xx statuses) and update the
globalChecks entries (the array named globalChecks) with correct
status/score/detail based on response, falling back to "not configured" or
"error" if probe fails; alternatively if you prefer not to probe, rename the
array/labels from "checks" to "feature list" to avoid implying verification
(update references to globalChecks and labels in AdminSeo and any UI
components).
In `@client/src/pages/admin/seo.tsx`:
- Around line 86-100: The filteredReports useMemo currently builds slugSet by
returning p.published for any tab other than "drafts", which causes the "all"
tab to show only published posts; update the logic in the slugSet construction
(inside filteredReports) to explicitly handle tab values so "all" includes both
published and draft posts (e.g., if tab === "drafts" return !p.published; else
if tab === "published" return p.published; else /* "all" */ return true),
ensuring slugSet contains slugs for the intended set of posts and downstream
filters/search still operate on that full set.
- Around line 49-65: loadAll 目前只有 try/finally,缺少错误处理与 IO 隔离:为 loadAll 补上 catch
分支并维护一个 error 状态(例如 setError),在
fetchAdminPosts、fetch("/sitemap.xml")、fetch("/robots.txt") 三个请求间做隔离(用单独的
try/catch 或 Promise.allSettled)以防单个请求失败导致整个页面无提示;在各自失败时填入合理的回退值(空
sitemapPreview/robotsPreview 或带错误信息的对象)并调用
setSitemapPreview/setRobotsPreview/setPosts 或 setError 对应更新,最后在 UI 的头部或表格区域根据
error 渲染用户可见的错误提示;保留 finally 中对 setRefreshing(false)/setLoading(false)
的调用以保证状态被清理。
---
Outside diff comments:
In `@server/src/index.ts`:
- Line 336: 在生成 RSS 的地方不要直接用 new URL(c.req.url).origin — 改为优先使用环境变量
SITE_ORIGIN(即当 c.env.SITE_ORIGIN 存在时用它),否则回退到 new URL(c.req.url).origin;在
server/src/index.ts 中更新 siteUrl 的赋值(变量名 siteUrl)以先读 c.env.SITE_ORIGIN。若还希望 RSS
通过 Pages 域名暴露以避免 SPA fallback,另外新增一个代理函数 client/functions/rss.xml.ts(与
sitemap/robots 相同的代理逻辑)。
---
Nitpick comments:
In `@client/functions/robots.txt.ts`:
- Line 8: 在 _shared.ts 中将 interface ApiEnv 改为导出(export interface ApiEnv),然后把
robots.txt.ts 中的 PagesFunction<{ API_BASE: string }> 以及
sitemap.xml.ts、rss.xml.ts、api/[[path]].ts、cdn/[[path]].ts 中的类似声明统一替换为
PagesFunction<ApiEnv>;保持 getBackendUrl 对 API_BASE 可选性的处理不变(因为实际是 API_BASE?:
string),以确保类型声明与实际行为一致并提升类型安全。
In `@client/src/lib/seo-analyzer.ts`:
- Line 127: Duplicate slug validation logic is repeated and missing server-side
checks; extract the predicate into a shared function isValidSlug(slug: string):
boolean at the top of client/src/lib/seo-analyzer.ts (implement the existing
logic: /^[a-z0-9-]+$/.test(slug) && !slug.includes("--") &&
!slug.startsWith("-") && !slug.endsWith("-")), export it, then replace the
inline checks in client/src/pages/admin/dashboard.tsx (where p.slug is
validated) to call isValidSlug, and add the same validation call in the server
article create/update handlers in server/src/index.ts to reject invalid slugs
before persisting.
- Around line 264-270: When there are no published posts the current calculation
produces 0 which is misleading; update the logic around
dimensionScores/totalScore to short-circuit when there are no published posts
(check the existing publishedPosts or publishedReports counter) and return a
neutral value (e.g. null or undefined) instead of 0 so the UI can render an
empty state. Locate the aggregation using dimAcc and the computed
dimensionScores and adjust the code that computes totalScore to first test
publishedPosts/publishedReports === 0 and handle that case by returning the
neutral sentinel rather than computing/rounding a 0 score.
In `@client/src/pages/admin/dashboard.tsx`:
- Line 179: The hardcoded lg:max-h-[calc(100vh-260px)] on the div with className
"flex flex-col min-h-0 lg:max-h-[calc(100vh-260px)]" relies on a magic 260px
value and should be made flexible: update the surrounding layout (the outer
wrapper element currently using "mx-auto …") to use a full-height flex container
(e.g., "flex flex-col min-h-screen") and change this inner list container to use
flexible growth instead of a calc max-height (e.g., "flex flex-col min-h-0
lg:flex-1 lg:min-h-0") so the list naturally fills remaining space;
alternatively replace the layout with a CSS Grid using grid-template-rows: auto
auto auto 1fr to let the list area occupy the leftover space.
In `@client/src/pages/admin/seo.tsx`:
- Around line 67-70: The effect that sets document.title inside useEffect
currently changes the page title without restoring it; modify the useEffect in
the component to capture the original title (const prev = document.title) before
setting document.title = "SEO 优化 | Monolith" and return a cleanup function that
restores document.title = prev, or replace the manual logic by using the shared
useDocumentTitle hook to set and auto-restore the title; reference the existing
useEffect and loadAll calls when making the change.
- Line 58: The sitemap parsing using smRes.matchAll(/<loc>([^<]+)<\/loc>/g)
doesn't decode XML entities; replace this with DOMParser: parse smRes into a
document, check for parsing errors via doc.querySelector("parsererror") and
handle/report them, then extract URLs by selecting all <loc> elements (e.g.,
doc.querySelectorAll("loc")) and reading each element's textContent to get
decoded URLs before building the urls array; update the code that currently
assigns urls from smRes.matchAll to use this DOMParser-based approach and add
error handling for invalid XML.
In `@server/src/index.ts`:
- Line 373: The SITE_ORIGIN value may include trailing whitespace or a trailing
slash causing double-slashes in outputs; normalize it before use by trimming
whitespace and removing any trailing slash, then fall back to new
URL(c.req.url).origin if empty. Update the code that builds siteUrl (const
siteUrl = c.env.SITE_ORIGIN || new URL(c.req.url).origin) to call a small helper
like normalizeSiteOrigin(value) that returns the trimmed value without a
trailing slash (and returns null/undefined for empty strings) and use that
helper in both places where SITE_ORIGIN is read (the siteUrl assignment and the
robots/sitemap generation code) so all consumers get the canonical origin.
In `@server/wrangler.toml`:
- Around line 9-10: The SITE_ORIGIN value is hardcoded to
"https://monolith-client.pages.dev" which will break sitemap/robots when
switching to a custom domain; update SITE_ORIGIN whenever you bind a custom
domain or change the implementation to read an environment/deployment variable
(e.g., process.env.SITE_ORIGIN or equivalent runtime injection) instead of the
literal, and add a short inline comment by the SITE_ORIGIN declaration reminding
maintainers to sync this value with any custom domain changes.
🪄 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: b7ce672f-1a80-441f-a800-821fed697590
📒 Files selected for processing (9)
client/functions/robots.txt.tsclient/functions/sitemap.xml.tsclient/src/app.tsxclient/src/components/admin-layout.tsxclient/src/lib/seo-analyzer.tsclient/src/pages/admin/dashboard.tsxclient/src/pages/admin/seo.tsxserver/src/index.tsserver/wrangler.toml
📜 Review details
⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (4)
client/functions/**
⚙️ CodeRabbit configuration file
client/functions/**: Cloudflare Pages Functions(API 反向代理层)。审查时请关注: 1. 代理目标 URL 是否正确构建 2. 请求头的传递和清理 3. 错误响应处理
Files:
client/functions/sitemap.xml.tsclient/functions/robots.txt.ts
client/src/components/**
⚙️ CodeRabbit configuration file
client/src/components/**: 这是 React 前端组件目录。审查时请关注: 1. 是否同时兼容暗色和亮色主题(检查 CSS 变量和 data-theme) 2. 响应式布局是否完整(移动端/平板/桌面端) 3. 无障碍访问(aria 标签、键盘导航) 4. 组件是否保持单一职责
Files:
client/src/components/admin-layout.tsx
server/src/index.ts
⚙️ CodeRabbit configuration file
server/src/index.ts: Hono Workers API 路由总入口。审查时请关注: 1. JWT 认证中间件是否正确保护管理接口 2. CORS 配置是否安全 3. 请求参数验证
Files:
server/src/index.ts
client/src/pages/**
⚙️ CodeRabbit configuration file
client/src/pages/**: 页面级组件。审查时请关注: 1. 数据加载和错误处理是否完善 2. SEO 相关(页面标题、meta 标签) 3. 导航和路由是否正确
Files:
client/src/pages/admin/seo.tsxclient/src/pages/admin/dashboard.tsx
🔇 Additional comments (3)
client/functions/sitemap.xml.ts (1)
1-23: 与robots.txt.ts完全重复,建议抽取共享代理工厂两个文件代码几乎逐字相同,仅注释中文件名差异。已在
_shared.ts中沉淀辅助函数,建议进一步把整个onRequest抽成工厂:♻️ DRY 重构示例
在
client/functions/_shared.ts添加:export function createProxyHandler(): PagesFunction<ApiEnv> { return async (context) => { if (context.request.method !== "GET" && context.request.method !== "HEAD") { return new Response("Method Not Allowed", { status: 405, headers: { Allow: "GET, HEAD" } }); } const backend = getBackendUrl(context.env); if (!backend) return createPlainApiBaseErrorResponse(); const target = buildTargetUrl(backend, context.request); try { const res = await fetch(target, { method: context.request.method }); return new Response(res.body, { status: res.status, headers: res.headers }); } catch { return new Response("Upstream unavailable", { status: 502, headers: { "Content-Type": "text/plain; charset=utf-8" } }); } }; }然后两个文件简化为:
import { createProxyHandler } from "./_shared"; export const onRequest = createProxyHandler();至于 method/headers 透传与类型一致性问题,已在
robots.txt.ts评论中说明,此处同样适用。client/src/app.tsx (1)
25-25: 路由与懒加载注册得当
AdminSeo的lazy导入与现有 admin 页面写法一致;<Route path="/admin/seo">放在<Route path="/admin">之前避免了被通配匹配吞掉。LGTM。Also applies to: 169-169
client/src/components/admin-layout.tsx (1)
11-11: SEO 入口接入正确新菜单项复用了现有
navGroups渲染管线,自动继承双主题样式(text-foreground/bg-muted等语义变量)和移动端抽屉行为;Sparkles图标在 lucide-react 标准库内,无需额外配置。LGTM。Also applies to: 55-55
* loadAll 用 Promise.allSettled 隔离 4 个请求(含新增 RSS HEAD 探活), 每路单独错误回退,新增 error state 与红色错误 banner 提示用户 * globalChecks 改为基于真实 fetch 信号的状态判定: - sitemap:urlCount 决定 pass/warn/fail,detail 显示真实 URL 数 - robots:检测是否包含 Sitemap 指令 - rss:HEAD 探活 - 缺信号时显示"未检测"warn,杜绝硬编码 pass 名实不符 * filteredReports:'all' tab 真正显示全部文章(含草稿), 仅 warn/poor 限定已发布范围,与按钮文案保持一致 * Pages Functions(sitemap.xml.ts/robots.txt.ts)收紧: - 仅允许 GET/HEAD,其他 method 直接 405 - 不透传客户端 headers,避免 Cookie/Authorization 被透传到后端
变更摘要
新增管理后台 SEO 仪表盘,并修复 sitemap/robots 在 Pages 域下被 SPA fallback 截胡 + 后端 `` 误用 Workers 自身域名两个问题。
主要改动
前端
Pages Functions
Workers
测试结果
预览部署 https://ebf37c58.monolith-client.pages.dev 已验证:
typecheck(client functions + server)零错误。
风险评估