Conversation
…L and improve redis feature detection
…nt variable access
…ndling fix: update OAuth redirection logic to use buildRedirectUrl for improved URL management
概览该拉取请求引入了一个用于自动化Docker镜像构建和推送的GitHub Actions工作流,创建了一个集中式的URL重定向构建器,更新了多个OAuth回调路由以使用该构建器,将特性标志从静态布尔值改为运行时评估的访问器,并改进了Redis连接生命周期管理。 变更
预估代码审查工作量🎯 2 (简单) | ⏱️ ~12 分钟 诗歌
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. 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)
lib/features.ts (1)
54-63:⚠️ Potential issue | 🟡 Minor
getFeaturesSummary()缺少cloudflareAI和oauthLinuxdo字段新增的
cloudflareAIgetter(第 23 行)及现有的oauth.linuxdo(第 12 行)均未包含在调试摘要中,导致该函数无法反映完整的功能状态。🐛 建议补全摘要字段
export function getFeaturesSummary() { return { oauthGithub: features.oauth.github, oauthGoogle: features.oauth.google, + oauthLinuxdo: features.oauth.linuxdo, database: features.database, email: features.email, fileUpload: features.fileUpload, redis: features.redis, + cloudflareAI: features.cloudflareAI, } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/features.ts` around lines 54 - 63, getFeaturesSummary currently omits the new cloudflareAI getter and the existing oauth.linuxdo flag, so update getFeaturesSummary to include cloudflareAI: features.cloudflareAI and oauthLinuxdo: features.oauth.linuxdo in the returned object; locate the getFeaturesSummary function and add these two properties so the summary reflects the full feature state (referenced symbols: getFeaturesSummary, features.cloudflareAI, features.oauth.linuxdo).
🧹 Nitpick comments (1)
lib/redis.ts (1)
40-42:retryStrategy无上限重试,在 Redis 永久不可用时持续消耗资源
Math.min(times * 200, 2000)始终返回正数,ioredis 会无限重连。对于普通长驻服务器这通常是期望行为,但若 Redis 实例永久下线(如配置错误),客户端会持续产生重连开销。可视情况添加最大重试次数上限。♻️ 可选优化(两处同改)
- retryStrategy(times) { - return Math.min(times * 200, 2000) - }, + retryStrategy(times) { + if (times > 20) return null // 超过上限后停止重连 + return Math.min(times * 200, 2000) + },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/redis.ts` around lines 40 - 42, The current retryStrategy function always returns a positive delay (Math.min(times * 200, 2000)) causing ioredis to reconnect indefinitely; update the retryStrategy (in lib/redis.ts) to enforce a maximum retry count (e.g., stop after N attempts by returning null or undefined when times exceeds the cap) so the client stops reconnecting on permanent Redis failures; apply the same change to the other identical retryStrategy location if present.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/docker.yml:
- Around line 57-58: GIT_COMMIT_SHORT is being set to the full SHA (github.sha)
same as GIT_COMMIT_HASH; add a new step after the Checkout step that sets an
output short_sha by slicing GITHUB_SHA (e.g., use a step with id build_meta that
echoes short_sha=${GITHUB_SHA::8} to GITHUB_OUTPUT), then change the build-args
so GIT_COMMIT_SHORT uses the step output (steps.build_meta.outputs.short_sha)
while leaving GIT_COMMIT_HASH as github.sha.
- Line 59: The pipeline currently assigns GIT_COMMIT_MESSAGE from
github.event.head_commit.message which can contain newlines and breaks
build-push-action build-args; change the "Prepare build metadata" step to
compute GIT_COMMIT_MESSAGE as only the commit subject (first line) by extracting
the first line of github.event.head_commit.message (e.g. via shell trimming like
piping to head -n1 or sed -n '1p') and export that variable, then use that
GIT_COMMIT_MESSAGE in the build-args list passed to build-push-action so no
embedded newlines are included; update references to
github.event.head_commit.message to use the new GIT_COMMIT_MESSAGE variable
(identify symbols: GIT_COMMIT_MESSAGE, github.event.head_commit.message, Prepare
build metadata, build-args).
In `@lib/features.ts`:
- Around line 10-23: getFeaturesSummary is missing two flags from the features
object; update the getFeaturesSummary function to include oauthLinuxdo and
cloudflareAI in its returned summary object by adding entries like oauthLinuxdo:
features.linuxdo (or features.oauthLinuxdo if that getter exists) and
cloudflareAI: features.cloudflareAI so the summary mirrors the defined getters
(refer to the linuxdo/oauthLinuxdo and cloudflareAI getters to locate the
correct source properties).
In `@lib/url.ts`:
- Around line 4-6: buildRedirectUrl currently throws if
process.env.NEXT_PUBLIC_APP_URL is missing a protocol; update buildRedirectUrl
to defensively construct the base URL: attempt to use
process.env.NEXT_PUBLIC_APP_URL, and if new URL(path, base) throws or the base
lacks a scheme, try prepending a safe default scheme (e.g., "https://") and
reconstruct; if that still fails, fall back to using fallbackUrl and ensure the
function always returns a valid URL instance instead of letting a TypeError
bubble up (refer to buildRedirectUrl and its callers such as the oauth route
handlers).
---
Outside diff comments:
In `@lib/features.ts`:
- Around line 54-63: getFeaturesSummary currently omits the new cloudflareAI
getter and the existing oauth.linuxdo flag, so update getFeaturesSummary to
include cloudflareAI: features.cloudflareAI and oauthLinuxdo:
features.oauth.linuxdo in the returned object; locate the getFeaturesSummary
function and add these two properties so the summary reflects the full feature
state (referenced symbols: getFeaturesSummary, features.cloudflareAI,
features.oauth.linuxdo).
---
Nitpick comments:
In `@lib/redis.ts`:
- Around line 40-42: The current retryStrategy function always returns a
positive delay (Math.min(times * 200, 2000)) causing ioredis to reconnect
indefinitely; update the retryStrategy (in lib/redis.ts) to enforce a maximum
retry count (e.g., stop after N attempts by returning null or undefined when
times exceeds the cap) so the client stops reconnecting on permanent Redis
failures; apply the same change to the other identical retryStrategy location if
present.
| GIT_COMMIT_HASH=${{ github.sha }} | ||
| GIT_COMMIT_SHORT=${{ github.sha }} |
There was a problem hiding this comment.
GIT_COMMIT_SHORT 与 GIT_COMMIT_HASH 值相同(均为完整 40 位 SHA)
Line 58 中 GIT_COMMIT_SHORT=${{ github.sha }} 传递的是完整 SHA,与 GIT_COMMIT_HASH 一致,起不到"短 SHA"的效果。应在构建前通过一个独立 step 截取短 SHA。
🔧 建议修复
在 Checkout 步骤后添加:
- name: Prepare build metadata
id: build_meta
run: |
echo "short_sha=${GITHUB_SHA::8}" >> $GITHUB_OUTPUT然后修改 build-args:
- GIT_COMMIT_SHORT=${{ github.sha }}
+ GIT_COMMIT_SHORT=${{ steps.build_meta.outputs.short_sha }}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/docker.yml around lines 57 - 58, GIT_COMMIT_SHORT is being
set to the full SHA (github.sha) same as GIT_COMMIT_HASH; add a new step after
the Checkout step that sets an output short_sha by slicing GITHUB_SHA (e.g., use
a step with id build_meta that echoes short_sha=${GITHUB_SHA::8} to
GITHUB_OUTPUT), then change the build-args so GIT_COMMIT_SHORT uses the step
output (steps.build_meta.outputs.short_sha) while leaving GIT_COMMIT_HASH as
github.sha.
| build-args: | | ||
| GIT_COMMIT_HASH=${{ github.sha }} | ||
| GIT_COMMIT_SHORT=${{ github.sha }} | ||
| GIT_COMMIT_MESSAGE=${{ github.event.head_commit.message }} |
There was a problem hiding this comment.
多行 commit message 会导致 Docker 构建失败
build-push-action 将 build-args 以换行符分隔解析为 KEY=VALUE 列表。当 commit message 包含换行符(例如 feat: subject\n\nbody text),${{ github.event.head_commit.message }} 展开后换行符会被插入 build-args 字符串中,"body text" 等后续行将被当作独立的(无效)构建参数,导致构建失败或行为不可预期。
建议只取 commit message 的第一行(subject line):
🔧 建议修复
在 Prepare build metadata step 中额外提取第一行:
- name: Prepare build metadata
id: build_meta
run: |
echo "short_sha=${GITHUB_SHA::8}" >> $GITHUB_OUTPUT
+ echo "commit_msg=$(git log -1 --format='%s')" >> $GITHUB_OUTPUT然后修改 build-args:
- GIT_COMMIT_MESSAGE=${{ github.event.head_commit.message }}
+ GIT_COMMIT_MESSAGE=${{ steps.build_meta.outputs.commit_msg }}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/docker.yml at line 59, The pipeline currently assigns
GIT_COMMIT_MESSAGE from github.event.head_commit.message which can contain
newlines and breaks build-push-action build-args; change the "Prepare build
metadata" step to compute GIT_COMMIT_MESSAGE as only the commit subject (first
line) by extracting the first line of github.event.head_commit.message (e.g. via
shell trimming like piping to head -n1 or sed -n '1p') and export that variable,
then use that GIT_COMMIT_MESSAGE in the build-args list passed to
build-push-action so no embedded newlines are included; update references to
github.event.head_commit.message to use the new GIT_COMMIT_MESSAGE variable
(identify symbols: GIT_COMMIT_MESSAGE, github.event.head_commit.message, Prepare
build metadata, build-args).
| get github() { return !!(process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET) }, | ||
| get google() { return !!(process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) }, | ||
| get linuxdo() { return !!(process.env.LINUXDO_CLIENT_ID && process.env.LINUXDO_CLIENT_SECRET) }, | ||
| }, | ||
| // 数据库 | ||
| database: !!process.env.DATABASE_URL, | ||
| get database() { return !!process.env.DATABASE_URL }, | ||
| // 邮件服务(用于找回密码等)- 同步检查环境变量 | ||
| email: isEmailConfiguredSync(), | ||
| get email() { return isEmailConfiguredSync() }, | ||
| // 文件上传 | ||
| fileUpload: !!process.env.BLOB_READ_WRITE_TOKEN, | ||
| get fileUpload() { return !!process.env.BLOB_READ_WRITE_TOKEN }, | ||
| // Redis 缓存 | ||
| redis: !!process.env.KV_REST_API_URL, | ||
| get redis() { return !!(process.env.REDIS_URL || process.env.KV_REST_API_URL) }, | ||
| // Cloudflare AI(用于 AI 审核) | ||
| cloudflareAI: !!(process.env.CLOUDFLARE_ACCOUNT_ID && process.env.CLOUDFLARE_API_TOKEN), | ||
| } as const | ||
| get cloudflareAI() { return !!(process.env.CLOUDFLARE_ACCOUNT_ID && process.env.CLOUDFLARE_API_TOKEN) }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, find the file and read it to understand the full context
fd lib/features.ts --type f | head -5Repository: dext7r/precheck
Length of output: 41
🏁 Script executed:
# Read the complete lib/features.ts file
cat -n lib/features.tsRepository: dext7r/precheck
Length of output: 2369
🏁 Script executed:
# Search for files that import from lib/features
rg "from.*['\"].*lib/features['\"]" --type ts --type tsx -B 1 -A 3Repository: dext7r/precheck
Length of output: 86
🏁 Script executed:
# Search for 'use client' directive in files that import features
rg -l "from.*['\"].*lib/features['\"]" --type ts --type tsx | while read file; do
if grep -q "^['\"]use client['\"]" "$file" 2>/dev/null; then
echo "CLIENT COMPONENT: $file"
grep -n "from.*['\"].*lib/features['\"]" "$file"
fi
doneRepository: dext7r/precheck
Length of output: 86
🏁 Script executed:
# Search for imports of features more broadly
rg "lib/features" --type ts | head -20Repository: dext7r/precheck
Length of output: 1594
🏁 Script executed:
# Also search without type restrictions
rg "import.*features" | grep -v node_modulesRepository: dext7r/precheck
Length of output: 1783
🏁 Script executed:
# Check for 'use client' directive in files importing features
rg -l "import.*features" | while read file; do
if head -1 "$file" | grep -q "use client"; then
echo "CLIENT COMPONENT: $file"
fi
doneRepository: dext7r/precheck
Length of output: 41
🏁 Script executed:
# Get a better picture of all client components and their imports
rg "^['\"]use client['\"]" -l | head -20Repository: dext7r/precheck
Length of output: 630
🏁 Script executed:
# Check if the layout.tsx that imports features is a client or server component
head -5 "app/[locale]/(public)/layout.tsx"Repository: dext7r/precheck
Length of output: 303
🏁 Script executed:
# Check for any 'use client' in layout files
rg "use client" "app/[locale]/(public)/layout.tsx"Repository: dext7r/precheck
Length of output: 41
🏁 Script executed:
# Search more broadly for layout.tsx files importing features
fd "layout.tsx" | xargs grep -l "import.*features" 2>/dev/nullRepository: dext7r/precheck
Length of output: 91
🏁 Script executed:
# Examine the actual usage in app/[locale]/(public)/layout.tsx
rg "features\." "app/[locale]/(public)/layout.tsx" -B 2 -A 2Repository: dext7r/precheck
Length of output: 315
🏁 Script executed:
# Check the full context of how features.database is used in layout.tsx
rg "features\." "app/[locale]/(public)/layout.tsx" -B 5 -A 5Repository: dext7r/precheck
Length of output: 421
🏁 Script executed:
# Check if Header component is a client component
head -5 "app/components/layout/header.tsx"Repository: dext7r/precheck
Length of output: 150
🏁 Script executed:
# See if features object is passed directly anywhere
rg "features" "app/[locale]/(public)/layout.tsx"Repository: dext7r/precheck
Length of output: 196
🏁 Script executed:
# Check all client components that import features directly
rg -l "import.*features" | while read file; do
if head -20 "$file" | grep -q "^['\"]use client"; then
echo "=== CLIENT: $file ==="
rg "features" "$file" -A 2 | head -15
fi
doneRepository: dext7r/precheck
Length of output: 41
🏁 Script executed:
# Find the Header component
fd "header\." --type f | grep -E "(component|layout)" | head -10Repository: dext7r/precheck
Length of output: 147
🏁 Script executed:
# Check the full import in layout.tsx
rg "import.*Header" "app/[locale]/(public)/layout.tsx"Repository: dext7r/precheck
Length of output: 110
🏁 Script executed:
# Find Header in components directory
fd "header" "components/" -type fRepository: dext7r/precheck
Length of output: 229
🏁 Script executed:
# Check if Header is a client component and how it uses authEnabled
rg -l "export.*Header" | head -3 | xargs -I {} sh -c 'echo "=== {} ===" && head -20 "{}"'Repository: dext7r/precheck
Length of output: 1909
🏁 Script executed:
# Check the layout/header.tsx file specifically
head -20 "components/layout/header.tsx"Repository: dext7r/precheck
Length of output: 776
🏁 Script executed:
# Check if layout/header.tsx is a client component
rg "use client" "components/layout/header.tsx"Repository: dext7r/precheck
Length of output: 71
🏁 Script executed:
# Check how Header uses the authEnabled prop
rg -A 10 "authEnabled" "components/layout/header.tsx"Repository: dext7r/precheck
Length of output: 1804
🏁 Script executed:
# Verify the complete picture: is layout.tsx a server or client component?
head -1 "app/[locale]/(public)/layout.tsx"Repository: dext7r/precheck
Length of output: 89
🏁 Script executed:
# Final verification: confirm no client components directly import the features object itself
rg -l "use client" | while read file; do
if grep -q "import.*features" "$file" 2>/dev/null; then
echo "PROBLEM: $file"
fi
doneRepository: dext7r/precheck
Length of output: 41
在 getFeaturesSummary() 中补齐所有功能标志
getFeaturesSummary() 函数(第 54-62 行)缺少两个已定义的功能标志:
oauthLinuxdo(第 12 行定义)cloudflareAI(第 23 行定义)
请将这两个字段添加到返回对象中,以保持摘要函数的完整性。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/features.ts` around lines 10 - 23, getFeaturesSummary is missing two
flags from the features object; update the getFeaturesSummary function to
include oauthLinuxdo and cloudflareAI in its returned summary object by adding
entries like oauthLinuxdo: features.linuxdo (or features.oauthLinuxdo if that
getter exists) and cloudflareAI: features.cloudflareAI so the summary mirrors
the defined getters (refer to the linuxdo/oauthLinuxdo and cloudflareAI getters
to locate the correct source properties).
| export function buildRedirectUrl(path: string, fallbackUrl: string): URL { | ||
| const baseUrl = process.env.NEXT_PUBLIC_APP_URL || fallbackUrl | ||
| return new URL(path, baseUrl) |
There was a problem hiding this comment.
NEXT_PUBLIC_APP_URL 格式无效时会抛出未捕获的 TypeError
若 NEXT_PUBLIC_APP_URL 设置为缺少协议的值(如 localhost:3000 或 0.0.0.0:3000),new URL(path, baseUrl) 会抛出 TypeError。由于 google/route.ts、github/route.ts、linuxdo/route.ts 等文件在 try-catch 块外直接调用此函数(例如 github/route.ts 第 11、18、24 行),最终会返回 500 而非执行重定向。建议在函数内部降级处理。
🛡️ 建议修复
export function buildRedirectUrl(path: string, fallbackUrl: string): URL {
- const baseUrl = process.env.NEXT_PUBLIC_APP_URL || fallbackUrl
- return new URL(path, baseUrl)
+ if (process.env.NEXT_PUBLIC_APP_URL) {
+ try {
+ return new URL(path, process.env.NEXT_PUBLIC_APP_URL)
+ } catch {
+ // NEXT_PUBLIC_APP_URL 格式无效,降级使用 fallbackUrl
+ }
+ }
+ return new URL(path, fallbackUrl)
}📝 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.
| export function buildRedirectUrl(path: string, fallbackUrl: string): URL { | |
| const baseUrl = process.env.NEXT_PUBLIC_APP_URL || fallbackUrl | |
| return new URL(path, baseUrl) | |
| export function buildRedirectUrl(path: string, fallbackUrl: string): URL { | |
| if (process.env.NEXT_PUBLIC_APP_URL) { | |
| try { | |
| return new URL(path, process.env.NEXT_PUBLIC_APP_URL) | |
| } catch { | |
| // NEXT_PUBLIC_APP_URL 格式无效,降级使用 fallbackUrl | |
| } | |
| } | |
| return new URL(path, fallbackUrl) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/url.ts` around lines 4 - 6, buildRedirectUrl currently throws if
process.env.NEXT_PUBLIC_APP_URL is missing a protocol; update buildRedirectUrl
to defensively construct the base URL: attempt to use
process.env.NEXT_PUBLIC_APP_URL, and if new URL(path, base) throws or the base
lacks a scheme, try prepending a safe default scheme (e.g., "https://") and
reconstruct; if that still fails, fall back to using fallbackUrl and ensure the
function always returns a valid URL instance instead of letting a TypeError
bubble up (refer to buildRedirectUrl and its callers such as the oauth route
handlers).
Summary by CodeRabbit
发布说明
Bug Fixes
Chores
Infrastructure