feat: add configurable homepage SEO with admin settings - #6530
Conversation
WalkthroughAdds configurable SEO settings, robots.txt and sitemap.xml endpoints, frontend metadata and JSON-LD handling, homepage SEO behavior, administration controls, and localized SEO interface text. ChangesSEO configuration and delivery
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Admin
participant SettingsAPI
participant StatusAPI
participant Browser
participant SEOEngine
Admin->>SettingsAPI: Save SEO options
SettingsAPI->>StatusAPI: Persist and expose SEO fields
Browser->>StatusAPI: Load cached or refreshed status
StatusAPI-->>Browser: System and SEO metadata
Browser->>SEOEngine: Apply document SEO
SEOEngine-->>Browser: Update title, meta tags, links, and JSON-LD
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/src/features/system-settings/general/system-info-section.tsx (1)
140-151: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick winMap SEO dot-path fields to backend option keys before saving.
SEO form fields are registered as
seo.title,seo.titleSuffix,seo.description,seo.keywords,seo.siteUrl,seo.ogImage, andseo.robotsIndex.useSettingsFormflattens these to the same keys, and this section passes them through unchanged, but the backend option contract reads/writesSEO.Title,SEO.TitleSuffix,SEO.Description,SEO.Keywords,SEO.SiteURL,SEO.OGImage, andSEO.RobotsIndex. Save the correct option key for each SEO field so these settings are written intocommon.SEOTitleand the matching global SEO values.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/features/system-settings/general/system-info-section.tsx` around lines 140 - 151, Update the onSubmit handler to map each flattened SEO field key to its backend option key before calling updateOption.mutateAsync: map seo.title, seo.titleSuffix, seo.description, seo.keywords, seo.siteUrl, seo.ogImage, and seo.robotsIndex to the corresponding SEO.Title, SEO.TitleSuffix, SEO.Description, SEO.Keywords, SEO.SiteURL, SEO.OGImage, and SEO.RobotsIndex keys; leave non-SEO keys and existing value normalization unchanged.
🧹 Nitpick comments (2)
web/src/features/system-settings/general/system-info-section.tsx (1)
65-65: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd URL validation for
seo.siteUrl/seo.ogImage, matchingLogo.
Logoenforcesz.string().url().optional().or(z.literal('')), butseo.siteUrlandseo.ogImageare plain optional strings despite being URL-typed fields consumed bycontroller/seo.gofor the sitemap<loc>base and Open Graph image. Invalid values would silently propagate into public robots/sitemap output.Also applies to: 440-458, 462-478
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/features/system-settings/general/system-info-section.tsx` at line 65, Update the validation schema containing siteUrl and the related seo.ogImage field to use the same URL-or-empty-string optional validation as Logo: z.string().url().optional().or(z.literal('')). Preserve the existing optional behavior and apply the validation consistently to both SEO URL fields.web/src/features/home/hooks/use-home-seo.ts (1)
16-23: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract a shared "read cached SEO status" helper instead of duplicating the localStorage read+parse logic three times. All three sites implement the identical pattern: read
localStorage.getItem('status'),JSON.parseit, and silently swallow errors — a single helper (e.g.readCachedStatus()inweb/src/lib/seo/) would remove the duplication and prevent the copies from drifting (e.g. inconsistent key names or error handling) as this code evolves.
web/src/features/home/hooks/use-home-seo.ts#L16-L23: replace this mount-time read with a call to the shared helper.web/src/features/home/hooks/use-home-seo.ts#L61-L66: replace this cleanup-time read with the same shared helper.web/src/routes/__root.tsx#L57-L64: replace this read with the same shared helper.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/features/home/hooks/use-home-seo.ts` around lines 16 - 23, Extract the duplicated localStorage status read and JSON parsing into a shared readCachedStatus helper, preserving silent error handling and the existing null/default behavior. Replace both reads in useHomeSeo and the read in the root route with this helper; update web/src/features/home/hooks/use-home-seo.ts lines 16-23 and 61-66, and web/src/routes/__root.tsx lines 57-64.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@controller/seo.go`:
- Around line 15-24: Protect all reads of shared SEO globals in seoSiteBase and
RobotsTxt with common.OptionMapRWMutex.RLock/RUnlock, matching the locking
convention used by updateOptionMap and GetStatus. Cover SEOSiteURL,
SEORobotsIndex, and the additional SEO reads at the referenced locations while
preserving their existing behavior.
- Line 85: Escape the configured site URL before inserting it into the `<loc>`
element in the sitemap generation flow around `b.WriteString`. Use
XML-appropriate escaping, such as `xml.EscapeText`, for `site` while preserving
the existing path concatenation and output structure.
In `@web/src/i18n/locales/fr.json`:
- Around line 4126-4132: Add translations for all missing SEO field labels and
hint strings defined in en.json to fr.json, ja.json, ru.json, vi.json,
zh-TW.json, and zh.json, including the listed indexing, canonical URL, keywords,
title, suffix, description, and social-preview text. Keep the locale JSON valid
and use native translations so these entries no longer fall back to English.
In `@web/src/i18n/locales/zh-TW.json`:
- Line 321: Update the “Allow Search Engine Indexing” translation in the locale
data to use the existing Traditional Chinese terminology, changing “搜索” to “搜尋”
while preserving the rest of the translation.
In `@web/src/lib/seo/apply.ts`:
- Around line 72-78: Refactor the language selection in the SEO application
logic to remove the nested ternary, extracting it into a clearly named camelCase
helper. Preserve the existing behavior for Chinese regional variants, mapping
TW/HK to zh-TW, other zh languages to zh-CN, and non-Chinese languages to their
original value.
In `@web/src/routes/__root.tsx`:
- Line 45: Update the SEO effect in the root route component to obtain the
active pathname through TanStack Router’s useRouterState with a
location.pathname selector, then use that pathname in the effect and dependency
array. Remove the direct window.location.pathname read and ensure
applySeoFromStatus reruns only when the active route pathname changes.
---
Outside diff comments:
In `@web/src/features/system-settings/general/system-info-section.tsx`:
- Around line 140-151: Update the onSubmit handler to map each flattened SEO
field key to its backend option key before calling updateOption.mutateAsync: map
seo.title, seo.titleSuffix, seo.description, seo.keywords, seo.siteUrl,
seo.ogImage, and seo.robotsIndex to the corresponding SEO.Title,
SEO.TitleSuffix, SEO.Description, SEO.Keywords, SEO.SiteURL, SEO.OGImage, and
SEO.RobotsIndex keys; leave non-SEO keys and existing value normalization
unchanged.
---
Nitpick comments:
In `@web/src/features/home/hooks/use-home-seo.ts`:
- Around line 16-23: Extract the duplicated localStorage status read and JSON
parsing into a shared readCachedStatus helper, preserving silent error handling
and the existing null/default behavior. Replace both reads in useHomeSeo and the
read in the root route with this helper; update
web/src/features/home/hooks/use-home-seo.ts lines 16-23 and 61-66, and
web/src/routes/__root.tsx lines 57-64.
In `@web/src/features/system-settings/general/system-info-section.tsx`:
- Line 65: Update the validation schema containing siteUrl and the related
seo.ogImage field to use the same URL-or-empty-string optional validation as
Logo: z.string().url().optional().or(z.literal('')). Preserve the existing
optional behavior and apply the validation consistently to both SEO URL fields.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: f74ea30c-0965-48bf-a153-21dcf0f7e133
📒 Files selected for processing (28)
common/seo.gocontroller/misc.gocontroller/seo.gomodel/option.gorouter/main.goweb/index.htmlweb/src/features/home/hooks/index.tsweb/src/features/home/hooks/use-home-seo.tsweb/src/features/home/index.tsxweb/src/features/system-settings/general/system-info-section.tsxweb/src/features/system-settings/hooks/use-update-option.tsweb/src/features/system-settings/site/index.tsxweb/src/features/system-settings/site/section-registry.tsxweb/src/features/system-settings/types.tsweb/src/i18n/locales/en.jsonweb/src/i18n/locales/fr.jsonweb/src/i18n/locales/ja.jsonweb/src/i18n/locales/ru.jsonweb/src/i18n/locales/vi.jsonweb/src/i18n/locales/zh-TW.jsonweb/src/i18n/locales/zh.jsonweb/src/lib/seo/apply.tsweb/src/lib/seo/defaults.tsweb/src/lib/seo/dom.tsweb/src/lib/seo/index.tsweb/src/lib/seo/types.tsweb/src/main.tsxweb/src/routes/__root.tsx
| "SEO Description": "Description SEO", | ||
| "SEO Keywords": "Mots-clés SEO", | ||
| "SEO OG Image URL": "URL image OG SEO", | ||
| "SEO Settings": "Paramètres SEO", | ||
| "SEO Site URL": "URL du site SEO", | ||
| "SEO Title (full)": "Titre SEO (complet)", | ||
| "SEO Title Suffix": "Suffixe du titre SEO", |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check which locale files are missing the new SEO keys present in en.json.
for f in web/src/i18n/locales/*.json; do
echo "=== $f ==="
for key in "Allow Search Engine Indexing" "Canonical base URL for Open Graph and sitemap" "Comma-separated keywords for search engines" "Used for meta description and social previews" "When disabled, robots.txt will block all crawlers"; do
grep -q "\"$key\"" "$f" || echo "MISSING: $key"
done
doneRepository: QuantumNous/new-api
Length of output: 1681
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the locale files and extract JSON key names safely with Python.
for f in web/src/i18n/locales/*.json; do
echo "=== $f ==="
python3 - <<'PY' "$f"
import json, sys
from pathlib import Path
p = Path(sys.argv[1])
keys = json.loads(p.read_text(encoding="utf-8")).keys()
seo_keys = [k for k in keys if "SEO" in k or k in {
"Allow Search Engine Indexing",
"Appended after system name when full title is empty: {Name} - {Suffix}",
"Canonical base URL for Open Graph and sitemap",
"Comma-separated keywords for search engines",
"e.g. AI大模型API网关|OpenAI兼容|统一分发平台",
"Full browser tab title. Supports long-tail keywords. Overrides suffix mode when set.",
"Image shown in social media link previews",
"Leave empty to use System Name + suffix",
"Used for meta description and social previews",
"When disabled, robots.txt will block all crawlers",
}]
# Sort deterministically without assuming locale string collation
seo_keys.sort(key=lambda s: s.encode("utf-8"))
missing = [
"Allow Search Engine Indexing",
"Appended after system name when full title is empty: {Name} - {Suffix}",
"Canonical base URL for Open Graph and sitemap",
"Comma-separated keywords for search engines",
"e.g. AI大模型API网关|OpenAI兼容|统一分发平台",
"Full browser tab title. Supports long-tail keywords. Overrides suffix mode when set.",
"Image shown in social media link previews",
"Leave empty to use System Name + suffix",
"Used for meta description and social previews",
"When disabled, robots.txt will block all crawlers",
]
print("FOUND:", "\n".join(seo_keys) if seo_keys else "")
print("MISSING:", "\n".join([k for k in missing if k not in keys]) or "None")
print("TOTAL_FOUND:", len(seo_keys))
print("TOTAL_MISSING:", len([k for k in missing if k not in keys]))
PY
doneRepository: QuantumNous/new-api
Length of output: 4164
🏁 Script executed:
#!/bin/bash
set -euo pipefail
target=web/src/i18n/locales/en.json
echo "Locales:"
git ls-files web/src/i18n/locales | sort
echo
echo "en.json line count/size:"
wc -l "$target"
echo
echo "Search target keys in en.json with context:"
while IFS= read -r line; do
key=$(printf '%s' "$line" | sed -n 's/^[[:space:]]*"\([^"]*\)"[[:space:]]*:.*/\1/p')
if test -n "$key"; then
echo "--- $key"
fi
done < <(grep -n -A1 -B1 -E 'SEO|Search Engine|Open Graph|sitemap|robots|meta description|keywords|canonical|Appended after system name|Overrides suffix mode|social previews|AI大模型API网关|System Name \+ suffix' "$target" | sed -n '1,220p')Repository: QuantumNous/new-api
Length of output: 578
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Locales without reports:"
git ls-files web/src/i18n/locales | grep -v '^web/src/i18n/locales/_reports/' | sort
echo
echo "Raw key matches across locale JSON files:"
python3 - <<'PY'
from pathlib import Path
target = Path("web/src/i18n/locales/en.json")
all_keys = set()
missing = []
for p in sorted(Path("web/src/i18n/locales").glob("*.json")):
if p.name in {"_reports", "_reports.json"}:
continue
if p.stem == "_sync-report":
continue
try:
import json
data = json.loads(p.read_text(encoding="utf-8"))
except Exception as e:
print(f"ERROR {p}: {e}")
data = {}
keys = set(data.get("") or data.keys())
print(f"=== {p} ({len(keys)} keys) ===")
for key in sorted(keys):
print(repr(key))
target_keys = set(json.loads(target.read_text(encoding="utf-8")).keys())
candidates = [
"SEO Description",
"SEO Keywords",
"SEO OG Image URL",
"SEO Settings",
"SEO Site URL",
"SEO Title (.full)",
"SEO Title Suffix",
"Allow Search Engine Indexing",
"Appended after system name when full title is empty: {Name} - {Suffix}",
"Canonical base URL for Open Graph and sitemap",
"Comma-separated keywords for search engines",
"e.g. AI大模型API网关|OpenAI兼容|统一分发平台",
"Full browser tab title. Supports long-tail keywords. Overrides suffix mode when set.",
"Image shown in social media link previews",
"Leave empty to use System Name + suffix",
"Used for meta description and social previews",
"When disabled, robots.txt will block all crawlers",
]
for name, p in (("en", target), *[ (str(p), p) for p in sorted(Path("web/src/i18n/locales").glob("*.json")) if p.name not in {"_reports", "_sync-report"} and "_reports" not in str(p) ]):
try:
import json
keys = set(json.loads(p.read_text(encoding="utf-8")).keys())
except Exception as e:
print(f"{name}: error={e}")
continue
print(f"{name}: matches={sorted([k for k in candidates if k in keys])}")
PY
echo
echo "Context for likely new SEO keys in English locale:"
grep -n -C2 -E 'SEO Description|SEO Keywords|SEO OG Image URL|SEO Settings|SEO Site URL|SEO Title \(full\)|SEO Title Suffix|Allow Search Engine Indexing|Appended after system name when full title is empty|Canonical base URL for Open Graph|Comma-separated keywords for search engines|e\.g\. AI 大模型API网关|Full browser tab title|Image shown in social media|Leave empty to use System Name \+ suffix|Used for meta description and social previews|robots\.txt will block all crawlers' web/src/i18n/locales/en.json | sed -n '1,240p'Repository: QuantumNous/new-api
Length of output: 6122
Add the missing SEO translation keys across the locales.
en.json defines the new SEO field labels/hints, but fr.json, ja.json, ru.json, vi.json, zh-TW.json, and zh.json are missing most or all of those keys, including:
- "Allow Search Engine Indexing"
- "Appended after system name when full title is empty: {Name} - {Suffix}"
- "Canonical base URL for Open Graph and sitemap"
- "Comma-separated keywords for search engines"
- "e.g. AI大模型API网关|OpenAI兼容|统一分发平台"
- "Full browser tab title. Supports long-tail keywords. Overrides suffix mode when set."
- "Image shown in social media link previews"
- "Leave empty to use System Name + suffix"
- "Used for meta description and social previews"
- "When disabled, robots.txt will block all crawlers"
Add translated values so these SEO UI text and hints do not fall back to the raw English labels.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/src/i18n/locales/fr.json` around lines 4126 - 4132, Add translations for
all missing SEO field labels and hint strings defined in en.json to fr.json,
ja.json, ru.json, vi.json, zh-TW.json, and zh.json, including the listed
indexing, canonical URL, keywords, title, suffix, description, and
social-preview text. Keep the locale JSON valid and use native translations so
these entries no longer fall back to English.
| "Allow requests to private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)": "允許請求私有 IP 範圍 (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)", | ||
| "Allow Retry": "允許重試", | ||
| "Allow safety_identifier passthrough": "允許透傳 safety_identifier", | ||
| "Allow Search Engine Indexing": "允許搜索引擎索引", |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Use the locale’s existing Traditional Chinese terminology.
This value uses “搜索”, while the locale consistently uses “搜尋” for search-related UI. Change it to 允許搜尋引擎索引.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/src/i18n/locales/zh-TW.json` at line 321, Update the “Allow Search Engine
Indexing” translation in the locale data to use the existing Traditional Chinese
terminology, changing “搜索” to “搜尋” while preserving the rest of the translation.
| if (lang) { | ||
| document.documentElement.lang = lang.startsWith('zh') | ||
| ? lang.toLowerCase().includes('tw') || lang.toLowerCase().includes('hk') | ||
| ? 'zh-TW' | ||
| : 'zh-CN' | ||
| : lang | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Nested ternary violates coding guideline — extract to a named helper.
The two-level nested ternary for document.documentElement.lang is exactly the pattern the repo guideline prohibits.
As per coding guidelines, "禁止两层及以上嵌套三元表达式;复杂逻辑应拆分为小函数,命名应有意义并遵循驼峰命名。"
♻️ Proposed refactor
+function normalizeHtmlLang(lang: string): string {
+ if (!lang.startsWith('zh')) return lang
+ const lower = lang.toLowerCase()
+ return lower.includes('tw') || lower.includes('hk') ? 'zh-TW' : 'zh-CN'
+}
+
if (lang) {
- document.documentElement.lang = lang.startsWith('zh')
- ? lang.toLowerCase().includes('tw') || lang.toLowerCase().includes('hk')
- ? 'zh-TW'
- : 'zh-CN'
- : lang
+ document.documentElement.lang = normalizeHtmlLang(lang)
}📝 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.
| if (lang) { | |
| document.documentElement.lang = lang.startsWith('zh') | |
| ? lang.toLowerCase().includes('tw') || lang.toLowerCase().includes('hk') | |
| ? 'zh-TW' | |
| : 'zh-CN' | |
| : lang | |
| } | |
| function normalizeHtmlLang(lang: string): string { | |
| if (!lang.startsWith('zh')) return lang | |
| const lower = lang.toLowerCase() | |
| return lower.includes('tw') || lower.includes('hk') ? 'zh-TW' : 'zh-CN' | |
| } | |
| if (lang) { | |
| document.documentElement.lang = normalizeHtmlLang(lang) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/src/lib/seo/apply.ts` around lines 72 - 78, Refactor the language
selection in the SEO application logic to remove the nested ternary, extracting
it into a clearly named camelCase helper. Preserve the existing behavior for
Chinese regional variants, mapping TW/HK to zh-TW, other zh languages to zh-CN,
and non-Chinese languages to their original value.
Source: Coding guidelines
| } from '@/lib/auth-session' | ||
| import { subscribeAuthSessionEvents } from '@/lib/auth-session-sync' | ||
| import { resolveLegacyRoute } from '@/lib/legacy-route' | ||
| import { applySeoFromStatus } from '@/lib/seo' |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n '"`@tanstack/react-router`"' web/package.json
rg -n 'useRouterState' web/src -g '*.tsx' -g '*.ts'Repository: QuantumNous/new-api
Length of output: 798
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- package lock snippets ---\n'
for f in web/package-lock.json web/bun.lockb web/bun.lock web/pnpm-lock.yaml web/yarn.lock package-lock.json bun.lockb bun.lock pnpm-lock.yaml yarn.lock; do
if [ -f "$f" ]; then
echo "FILE $f"
rg -n '"`@tanstack/react-router`"|`@tanstack/react-router`' "$f" | head -80 || true
fi
done
printf '\n--- root route outline and relevant source ---\n'
ast-grep outline web/src/routes/__root.tsx 2>/dev/null || true
wc -l web/src/routes/__root.tsx
cat -n web/src/routes/__root.tsx | sed -n '1,130p'
printf '\n--- package manager files ---\n'
git ls-files | rg '(^web/package\.json$|^web/bun\.lock$|^web/bun\.lockb$|^package-lock\.json$|^bun\.lock$)' | sed 's#^`#-` #'Repository: QuantumNous/new-api
Length of output: 248
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- package lock snippets ---'
for f in web/package-lock.json web/bun.lockb web/bun.lock web/pnpm-lock.yaml web/yarn.lock package-lock.json bun.lockb bun.lock pnpm-lock.yaml yarn.lock; do
if [ -f "$f" ]; then
echo "FILE $f"
rg -n '"`@tanstack/react-router`"|`@tanstack/react-router`' "$f" | head -120 || true
fi
done
printf '%s\n' ''
printf '%s\n' '--- root route outline and relevant source ---'
ast-grep outline web/src/routes/__root.tsx 2>/dev/null || true
wc -l web/src/routes/__root.tsx
cat -n web/src/routes/__root.tsx | sed -n '1,160p'
printf '%s\n' ''
printf '%s\n' '--- package manager files ---'
git ls-files | rg '(^web/package\.json$|^web/bun\.lock$|^web/bun\.lockb$|^package-lock\.json$|^bun\.lock$)' || true
printf '%s\n' ''
printf '%s\n' '--- search for applySeoFromStatus and status-related SEO usage ---'
rg -n 'applySeoFromStatus|status.*SEO|noindex|robots|title' web/src/routes/__root.tsx web/src -g '*.tsx' -g '*.ts' | head -200Repository: QuantumNous/new-api
Length of output: 24578
🌐 Web query:
@tanstack/react-router 1.170.18 useRouterState docs
💡 Result:
The useRouterState hook in TanStack Router allows you to subscribe to and access the current internal state of the router [1][2]. Usage and Best Practices: Because useRouterState exposes the entire router state, it can incur a performance cost [2]. The official documentation advises that for common tasks like accessing the current location or active route matches, you should prefer more specific, performant, and ergonomic hooks such as useLocation and useMatches [1][3]. Options: The hook accepts an optional object to configure its behavior: - select: A selector function that receives the RouterState and returns a specific slice of state. Using this is recommended to ensure your component only re-renders when the selected piece of state changes [1][4]. - structuralSharing: A boolean that configures whether structural sharing is enabled for the selected value [1][3]. This defaults to the router's global configuration [4]. Example: import { useRouterState } from '@tanstack/react-router'; function LoadingIndicator { // Use select to subscribe only to the relevant slice of state const isLoading = useRouterState({ select: (s) => s.isLoading }); return isLoading?
Citations:
- 1: https://tanstack.com/router/latest/docs/framework/react/api/router/useRouterStateHook
- 2: https://github.com/TanStack/router/blob/main/packages/react-router/skills/react-router/SKILL.md
- 3: https://tanstack.com/router/v1/docs/api/router/useRouterStateHook
- 4: https://github.com/TanStack/router/blob/24cc08f8/packages/react-router/src/useRouterState.tsx
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- web package.json router entries ---'
cat -n web/package.json | sed -n '1,120p'
printf '%s\n' ''
printf '%s\n' '--- router hook examples in repo ---'
for f in web/src/components/page-transition.tsx web/src/components/navigation-progress.tsx web/src/components/layout/components/public-header.tsx; do
if [ -f "$f" ]; then
echo "FILE $f"
cat -n "$f" | sed -n '1,130p'
fi
doneRepository: QuantumNous/new-api
Length of output: 15880
Reapply SEO when the active route changes, not on every render.
The SEO effect has no dependency array and still reads window.location.pathname outside React state, so it only runs on incidental re-renders and can miss metadata changes for navigation. Subscribe to TanStack Router’s active URL via useRouterState({ select: (s) => s.location.pathname }) and use that value with the effect dependency.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/src/routes/__root.tsx` at line 45, Update the SEO effect in the root
route component to obtain the active pathname through TanStack Router’s
useRouterState with a location.pathname selector, then use that pathname in the
effect and dependency array. Remove the direct window.location.pathname read and
ensure applySeoFromStatus reruns only when the active route pathname changes.
3518df7 to
d65612e
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@web/src/lib/seo/cached-status.ts`:
- Around line 5-8: Update readCachedStatus to validate the JSON.parse result
before returning it: accept only non-null objects that are not arrays, and
return null for primitives or arrays. Remove reliance on the unchecked
Record<string, unknown> cast while preserving the existing localStorage read and
error-handling behavior.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: fbf6c5a8-dddc-444f-9c0b-02f11729557d
📒 Files selected for processing (29)
common/seo.gocontroller/misc.gocontroller/seo.gomodel/option.gorouter/main.goweb/index.htmlweb/src/features/home/hooks/index.tsweb/src/features/home/hooks/use-home-seo.tsweb/src/features/home/index.tsxweb/src/features/system-settings/general/system-info-section.tsxweb/src/features/system-settings/hooks/use-update-option.tsweb/src/features/system-settings/site/index.tsxweb/src/features/system-settings/site/section-registry.tsxweb/src/features/system-settings/types.tsweb/src/i18n/locales/en.jsonweb/src/i18n/locales/fr.jsonweb/src/i18n/locales/ja.jsonweb/src/i18n/locales/ru.jsonweb/src/i18n/locales/vi.jsonweb/src/i18n/locales/zh-TW.jsonweb/src/i18n/locales/zh.jsonweb/src/lib/seo/apply.tsweb/src/lib/seo/cached-status.tsweb/src/lib/seo/defaults.tsweb/src/lib/seo/dom.tsweb/src/lib/seo/index.tsweb/src/lib/seo/types.tsweb/src/main.tsxweb/src/routes/__root.tsx
🚧 Files skipped from review as they are similar to previous changes (23)
- web/src/lib/seo/index.ts
- web/src/lib/seo/types.ts
- web/src/routes/__root.tsx
- web/src/features/home/index.tsx
- web/src/i18n/locales/vi.json
- web/src/main.tsx
- router/main.go
- web/src/lib/seo/defaults.ts
- web/src/lib/seo/dom.ts
- web/src/i18n/locales/ru.json
- web/src/features/system-settings/site/index.tsx
- web/src/i18n/locales/zh-TW.json
- controller/misc.go
- model/option.go
- common/seo.go
- web/src/i18n/locales/ja.json
- controller/seo.go
- web/src/features/system-settings/general/system-info-section.tsx
- web/src/features/system-settings/hooks/use-update-option.ts
- web/src/lib/seo/apply.ts
- web/index.html
- web/src/features/home/hooks/use-home-seo.ts
- web/src/i18n/locales/en.json
Add a complete SEO management system for the homepage and public pages: Backend: - Add SEO option variables (Title, TitleSuffix, Description, Keywords, SiteURL, OGImage, RobotsIndex) with admin persistence - Add /robots.txt handler with configurable crawl rules and sitemap link - Add /sitemap.xml handler with lastmod, changefreq, and priority metadata - Expose SEO fields in /api/status for frontend consumption Frontend: - Add lib/seo module for document meta tag management (title, description, keywords, OG, Twitter Card, canonical, JSON-LD) - Add useHomeSeo hook for long-tail SEO titles and structured data on homepage - Add SEO Settings admin section with all fields + RobotsIndex toggle - Apply SEO from status on boot and route navigation - Add static fallback meta tags in index.html for crawlers without JS Features: - Long-tail title support (full title override or suffix mode) - Per-route robots control (public pages indexed, console pages noindex) - JSON-LD structured data (WebSite + Organization) on homepage - Admin toggle to block all crawlers via robots.txt - Immediate status cache refresh on SEO setting changes i18n: translations added for en, zh, zh-TW, ja, fr, ru, vi Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
d65612e to
bd8850e
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@web/src/i18n/locales/en.json`:
- Line 1467: The English locale’s SEO placeholder currently uses Chinese-only
text; replace the value for the key “e.g. AI大模型API网关|OpenAI兼容|统一分发平台” with an
English or language-neutral SEO example while leaving the locale key and
surrounding translations unchanged.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 38c1536f-ef57-4076-a867-c998911807f4
📒 Files selected for processing (29)
common/seo.gocontroller/misc.gocontroller/seo.gomodel/option.gorouter/main.goweb/index.htmlweb/src/features/home/hooks/index.tsweb/src/features/home/hooks/use-home-seo.tsweb/src/features/home/index.tsxweb/src/features/system-settings/general/system-info-section.tsxweb/src/features/system-settings/hooks/use-update-option.tsweb/src/features/system-settings/site/index.tsxweb/src/features/system-settings/site/section-registry.tsxweb/src/features/system-settings/types.tsweb/src/i18n/locales/en.jsonweb/src/i18n/locales/fr.jsonweb/src/i18n/locales/ja.jsonweb/src/i18n/locales/ru.jsonweb/src/i18n/locales/vi.jsonweb/src/i18n/locales/zh-TW.jsonweb/src/i18n/locales/zh.jsonweb/src/lib/seo/apply.tsweb/src/lib/seo/cached-status.tsweb/src/lib/seo/defaults.tsweb/src/lib/seo/dom.tsweb/src/lib/seo/index.tsweb/src/lib/seo/types.tsweb/src/main.tsxweb/src/routes/__root.tsx
🚧 Files skipped from review as they are similar to previous changes (27)
- web/src/features/system-settings/site/section-registry.tsx
- web/src/features/home/hooks/index.ts
- web/index.html
- web/src/routes/__root.tsx
- web/src/lib/seo/cached-status.ts
- web/src/i18n/locales/ja.json
- web/src/lib/seo/types.ts
- web/src/lib/seo/index.ts
- web/src/i18n/locales/fr.json
- router/main.go
- web/src/features/system-settings/site/index.tsx
- web/src/features/system-settings/hooks/use-update-option.ts
- web/src/features/system-settings/types.ts
- web/src/features/system-settings/general/system-info-section.tsx
- common/seo.go
- web/src/i18n/locales/vi.json
- web/src/lib/seo/defaults.ts
- web/src/main.tsx
- web/src/features/home/hooks/use-home-seo.ts
- controller/seo.go
- controller/misc.go
- web/src/features/home/index.tsx
- web/src/i18n/locales/zh-TW.json
- web/src/i18n/locales/zh.json
- web/src/lib/seo/dom.ts
- web/src/lib/seo/apply.ts
- model/option.go
| "Duration Unit": "Duration Unit", | ||
| "Duration Value": "Duration Value", | ||
| "Dynamic Pricing": "Dynamic Pricing", | ||
| "e.g. AI大模型API网关|OpenAI兼容|统一分发平台": "e.g. AI大模型API网关|OpenAI兼容|统一分发平台", |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Use an English example in the English locale.
This placeholder is displayed in the English SEO settings UI but contains only Chinese text, which is confusing for English-speaking administrators. Use an English or language-neutral SEO example instead.
Proposed fix
- "e.g. AI大模型API网关|OpenAI兼容|统一分发平台": "e.g. AI大模型API网关|OpenAI兼容|统一分发平台",
+ "e.g. AI API gateway|OpenAI-compatible|unified distribution platform": "e.g. AI API gateway|OpenAI-compatible|unified distribution platform",🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@web/src/i18n/locales/en.json` at line 1467, The English locale’s SEO
placeholder currently uses Chinese-only text; replace the value for the key
“e.g. AI大模型API网关|OpenAI兼容|统一分发平台” with an English or language-neutral SEO
example while leaving the locale key and surrounding translations unchanged.
📝 变更描述 / Description
为首页和公开页面添加完整的 SEO 管理功能,支持管理员通过后台配置所有 SEO 相关参数。
后端:
/robots.txt动态生成,支持管理员开关爬虫索引/sitemap.xml动态生成,含 lastmod / changefreq / priority/api/status暴露 SEO 字段供前端消费前端:
lib/seo模块:document meta tag 管理(title, description, OG, Twitter Card, canonical, JSON-LD)useHomeSeohook:首页长尾标题 + Schema.org 结构化数据index.html添加静态 fallback meta tags(不执行 JS 的爬虫也能抓取)特性:
🚀 变更类型 / Type of change
✅ 提交前检查项 / Checklist
📸 运行证明 / Proof of Work
此 PR 由 AI 辅助生成代码。
🤖 Generated with Claude Code
Summary by CodeRabbit
/robots.txtand/sitemap.xmlendpoints based on configuration.