Skip to content

使用Action编译docker 镜像,优化了部分docker部署情况下的代码#3

Open
xinhai-ai wants to merge 6 commits intodext7r:mainfrom
xinhai-ai:main
Open

使用Action编译docker 镜像,优化了部分docker部署情况下的代码#3
xinhai-ai wants to merge 6 commits intodext7r:mainfrom
xinhai-ai:main

Conversation

@xinhai-ai
Copy link

@xinhai-ai xinhai-ai commented Feb 22, 2026

Summary by CodeRabbit

发布说明

  • Bug Fixes

    • 改进了认证流程的可靠性
    • 增强了系统错误处理机制
  • Chores

    • 自动化Docker镜像构建和部署流程
  • Infrastructure

    • 优化了功能配置在运行时的评估方式
    • 改进了连接管理和系统稳定性

@coderabbitai
Copy link

coderabbitai bot commented Feb 22, 2026

概览

该拉取请求引入了一个用于自动化Docker镜像构建和推送的GitHub Actions工作流,创建了一个集中式的URL重定向构建器,更新了多个OAuth回调路由以使用该构建器,将特性标志从静态布尔值改为运行时评估的访问器,并改进了Redis连接生命周期管理。

变更

功能群组 / 文件 摘要
Docker工作流
.github/workflows/docker.yml
添加了自动化Docker镜像构建和推送到GitHub Container Registry的工作流,支持在推送到主分支和版本标签时触发。
URL重定向集中化
lib/url.ts
引入新的 buildRedirectUrl 函数,利用环境变量或回退URL构建标准化重定向URL。
OAuth回调路由更新
app/api/auth/callback/github/route.ts, app/api/auth/callback/google/route.ts, app/api/auth/callback/linuxdo/route.ts, app/api/auth/linuxdo/route.ts, app/api/auth/logout/route.ts
用新的 buildRedirectUrl 助手函数替换硬编码的URL构建,在所有OAuth错误和登录后重定向路径中实现一致的URL处理。
特性标志重构
lib/features.ts
将所有特性标志(OAuth提供商、数据库、电子邮件、文件上传、Redis、Cloudflare AI)从静态布尔值转换为运行时评估的访问器,确保使用当前环境值。
Redis连接管理改进
lib/redis.ts
添加了连接状态检查、更安全的客户端重置逻辑、使用重试策略替代旧版连接选项,以及针对ping失败的错误日志记录。

预估代码审查工作量

🎯 2 (简单) | ⏱️ ~12 分钟

诗歌

🐰✨ 流程已优化,重定向统一,
特性标志动态读取时鲜活,
Redis连接更稳健,
Docker镜像自动化流水,
代码整洁如春日阳光。 🌱

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning 标题仅部分涵盖了主要变更。PR包含多个重要改动:Docker工作流自动化、OAuth重定向URL的统一处理、特性标志的运行时评估以及Redis连接管理优化,而标题只强调了Docker镜像编译这一个方面。 建议更新标题以反映所有关键变更,例如:'Add Docker CI workflow and centralize redirect URL handling with feature flag optimization',或保持当前标题但需确保涵盖了PR的主要目的。
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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() 缺少 cloudflareAIoauthLinuxdo 字段

新增的 cloudflareAI getter(第 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.

Comment on lines +57 to +58
GIT_COMMIT_HASH=${{ github.sha }}
GIT_COMMIT_SHORT=${{ github.sha }}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

GIT_COMMIT_SHORTGIT_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 }}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

多行 commit message 会导致 Docker 构建失败

build-push-actionbuild-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).

Comment on lines +10 to +23
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) },
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, find the file and read it to understand the full context
fd lib/features.ts --type f | head -5

Repository: dext7r/precheck

Length of output: 41


🏁 Script executed:

# Read the complete lib/features.ts file
cat -n lib/features.ts

Repository: 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 3

Repository: 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
done

Repository: dext7r/precheck

Length of output: 86


🏁 Script executed:

# Search for imports of features more broadly
rg "lib/features" --type ts | head -20

Repository: dext7r/precheck

Length of output: 1594


🏁 Script executed:

# Also search without type restrictions
rg "import.*features" | grep -v node_modules

Repository: 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
done

Repository: dext7r/precheck

Length of output: 41


🏁 Script executed:

# Get a better picture of all client components and their imports
rg "^['\"]use client['\"]" -l | head -20

Repository: 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/null

Repository: 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 2

Repository: 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 5

Repository: 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
done

Repository: dext7r/precheck

Length of output: 41


🏁 Script executed:

# Find the Header component
fd "header\." --type f | grep -E "(component|layout)" | head -10

Repository: 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 f

Repository: 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
done

Repository: 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).

Comment on lines +4 to +6
export function buildRedirectUrl(path: string, fallbackUrl: string): URL {
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || fallbackUrl
return new URL(path, baseUrl)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

NEXT_PUBLIC_APP_URL 格式无效时会抛出未捕获的 TypeError

NEXT_PUBLIC_APP_URL 设置为缺少协议的值(如 localhost:30000.0.0.0:3000),new URL(path, baseUrl) 会抛出 TypeError。由于 google/route.tsgithub/route.tslinuxdo/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.

Suggested change
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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant