Skip to content

bug fix#192

Closed
ding113 wants to merge 43 commits intomainfrom
dev
Closed

bug fix#192
ding113 wants to merge 43 commits intomainfrom
dev

Conversation

@ding113
Copy link
Owner

@ding113 ding113 commented Nov 24, 2025

Summary

本次 PR 主要包含多个 bug 修复和功能优化,涵盖供应商测试、流式响应处理、Docker 构建、模型重定向和错误规则等核心模块。

Changes

🐛 Bug 修复

  1. Docker 构建修复 (关键修复)

    • 添加 CI=true 环境变量跳过构建时的数据库连接检查
    • 添加 webpack externals 排除 Node.js 内置模块
    • 解决 Docker 镜像构建失败问题
  2. 供应商 API 测试优化

  3. 模型重定向修复

    • 修复 Gemini 模型重定向无效的问题
    • 修复模型重定向日志记录问题
  4. 供应商管理优化

  5. 错误规则系统

    • 修复错误规则正则匹配问题
    • 增强刷新缓存功能
  6. 日志系统优化

    • 修复 log-time-formatter 的 null 安全问题
    • 添加详细文档注释
    • 删除未使用代码并优化错误处理

✨ 功能优化

  1. 流式响应超时调整

    • 调整流式静默期超时默认值从 10 秒改为 300 秒 (见数据库迁移 0023_cheerful_shocker.sql)
    • 字段:providers.streaming_idle_timeout_ms
    • 原因:提高长时间流式响应的容错性
  2. 使用记录 UI 优化

数据库变更

  • Migration 0023: 修改 providers.streaming_idle_timeout_ms 默认值从 10000ms → 300000ms (5分钟)

Testing

  • ✅ 手动测试供应商 API 连接(流式/非流式)
  • ✅ 验证 Docker 镜像构建成功
  • ✅ 测试模型重定向功能
  • ✅ 验证错误规则匹配逻辑
  • ✅ 确认日志系统稳定性

Related PRs

Breaking Changes

无破坏性变更。数据库迁移会自动执行。

Notes

  • 🔧 关键修复:Docker 构建环境添加 CI=true 跳过 instrumentation.ts 的数据库连接
  • 📊 默认超时时间大幅增加(10s → 300s),建议关注生产环境中长时间无响应的流式请求
  • 🔍 错误规则系统增强了缓存刷新能力,提高规则更新的实时性

Silentely and others added 30 commits November 22, 2025 20:20
新增对流式响应(如 SSE 和 NDJSON)的识别逻辑,在检测到流式响应时返回明确提示信息。
同时优化 JSON 响应解析流程,增加对非法 JSON 格式的捕获与日志记录。
此外,调整 OpenAI Responses API 测试输入格式,确保符合其接口规范。
某些中转服务不支持 max_output_tokens 参数,导致测试失败。根据注释说明,
该参数在当前上下文中并非必需,因此将其移除以确保与所有服务兼容。
新增对 SSE 和 NDJSON 格式流式响应的解析能力,包括:
- 添加 `streamInfo` 字段以记录接收到的数据块数量及格式
- 实现 `parseSSEText` 和 `parseStreamResponse` 函数用于解析流式数据
- 引入 `mergeStreamChunks` 方法将多个响应块合并为完整响应
- 更新 API 测试逻辑以正确处理并展示流式响应结果
- 增强错误处理机制以应对流式响应解析失败的情况
- 调整 API 测试配置中响应内容预览最大长度从 100 到 500 字符
- 在测试结果中新增 streamInfo 字段用于记录流式响应数据
- UI 上增加流式响应信息的展示区域,包括接收到的数据块数量和流格式
- 支持在结果详情和错误信息区域显示流式响应相关统计
应用 Prettier 格式化规则到 providers.ts,修复 Code Quality Check 失败问题。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
根据 PR #185 的代码审查建议,修复以下问题:

**Critical 修复**:
- 🔴 修复 Stream reader 资源泄漏
  - 在错误路径中添加 `await reader.cancel()` 防止内存泄漏
  - 确保在异常情况下正确释放资源

**High 修复**:
- 🟠 添加日志记录
  - 为 chunk 解析失败添加 warn 级别日志
  - 记录跳过的 chunk 数量和错误详情
  - 添加流式响应解析完成的 info 日志

- 🟠 改进 SSE 检测逻辑
  - 使用正则表达式替代字符串匹配
  - 提高检测的健壮性,避免误判

- 🟠 增强类型安全
  - 在类型转换前添加运行时类型守卫
  - 为空数组情况添加默认值处理
  - 确保不会创建无效的响应对象

**改进点**:
- 统一错误处理模式
- 提高代码可维护性
- 增强调试能力

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
fix:修复 Codex API 测试参数兼容性问题与增加支持流式测试
## 主要改进

### 1. 供应商模型测试优化
- **显示详细错误信息**: 将后端日志中的 errorDetail 字段返回给前端,用户无需查看容器日志
- **改进测试结果弹窗**: 在"复制结果"按钮右侧添加"关闭"按钮,提升用户体验
- **完善错误处理**: 优化 API 测试失败时的错误信息展示

### 2. 日志时间格式化
- **新增工具函数**: 创建 log-time-formatter.ts,支持将 Unix 时间戳转换为本地时间
- **格式化输出**: 统一日志时间格式为 YYYY/MM/DD HH:mm
- **时区支持**: 支持环境变量 TZ 和浏览器自动检测时区

### 3. ErrorRuleDetector 初始化优化
- **延迟加载**: 移除构造函数中的自动加载,避免数据库未准备好时重复加载
- **显式初始化**: 在 instrumentation.ts 中,数据库连接成功后显式调用 reload()
- **减少启动日志**: 避免在数据库准备前输出重复的加载日志

## 技术细节

### 修改文件
- src/actions/providers.ts: 返回详细错误信息
- src/app/[locale]/settings/providers/_components/forms/api-test-button.tsx: 添加关闭按钮
- messages/*/settings.json: 添加"关闭"翻译
- src/lib/utils/log-time-formatter.ts: 新增日志时间格式化工具
- src/lib/error-rule-detector.ts: 移除构造函数中的自动加载
- src/instrumentation.ts: 在数据库准备好后显式初始化 ErrorRuleDetector

### 测试
- ✅ TypeScript 类型检查通过
- ✅ 所有修改符合 SOLID、KISS、DRY、YAGNI 原则

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
合并供应商测试和日志系统优化

主要改进:
- 供应商模型测试显示详细错误信息
- 测试结果弹窗添加关闭按钮
- 新增日志时间格式化工具
- 优化 ErrorRuleDetector 初始化时机

详见 commit 07267a5
## 修改内容

### 1. 重构 instrumentation.ts 中的重复代码
- **提取公共函数**: 将 ErrorRuleDetector 初始化逻辑提取为独立的 `initializeErrorRuleDetector()` 函数
- **消除重复**: 生产环境和开发环境共用同一个初始化函数
- **提高可维护性**: 遵循 DRY 原则,未来修改只需更新一处

### 2. 优化 log-time-formatter.ts
- **移除硬编码区域设置**: 将 `zh-CN` 改为 `undefined`,使用运行时默认区域设置,提高通用性
- **统一日志记录**: 使用 `logger.error` 替代 `console.error`,保持日志记录一致性
- **改进错误回退**: 使用 `toISOString()` 替代 `toLocaleString()`,提供明确的 ISO 8601 格式

## 技术改进

- ✅ 遵循 DRY (Don't Repeat Yourself) 原则
- ✅ 提高代码可维护性和健壮性
- ✅ 增强国际化支持
- ✅ 统一错误处理策略
- ✅ TypeScript 类型检查通过

## 相关 PR

- 响应 PR #186 的代码审查建议
- 感谢 @gemini-code-assist 的详细审查

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add postgres and drizzle-orm to serverExternalPackages to prevent bundling Node.js-only modules (net, tls, crypto, stream, perf_hooks)
- Set CI=true in Dockerfile to skip instrumentation database connections during build
- Fixes Module not found errors for Node.js built-in modules during Next.js build
## 修改内容

### 1. 修复 providers.ts 中的 null 检查 (Medium Priority) ✅
**问题**: 缺少对 errorText 的 null/undefined 检查,可能产生误导性错误消息

**解决方案**:
- 添加防御性检查: `errorText ? clipText(errorText, 200) : "No error details available"`
- 移除冗余的 `|| "API 请求失败"` 回退,直接使用 finalErrorDetail

**效果**: 避免空字符串产生误导性错误消息,提供更清晰的错误反馈

### 2. 修复 ErrorRuleDetector 竞态条件 (High Priority) ✅
**问题**: 移除构造函数自动加载后,在启动阶段调用 detect() 会返回 false negatives

**解决方案**:
- 添加 `isInitialized` 状态跟踪
- 实现 `ensureInitialized()` 懒加载保护
- 新增 `detectAsync()` 方法,确保规则已加载
- 同步 `detect()` 方法添加未初始化警告

**效果**:
- 避免启动阶段的竞态条件
- 保持向后兼容性(同步 detect 方法)
- 推荐使用 detectAsync 以确保规则已加载

### 3. 确认 instrumentation.ts 重构 ✅
**状态**: 已在上一次提交中完成,符合审查要求
- 提取了 `initializeErrorRuleDetector()` 公共函数
- 消除了生产和开发环境的代码重复
- 遵循 DRY 原则

### 4. 添加 JSDoc 示例 (Low Priority) ✅
**改进**: 为 log-time-formatter.ts 添加使用示例

**新增内容**:
- `formatLogTime()` 添加系统时区和指定时区的示例
- `formatLogTimes()` 添加批量格式化的示例

**效果**: 提升开发者体验,特别是 timezone 参数的使用

## 技术验证
- ✅ TypeScript 类型检查通过
- ✅ 所有修改符合项目编码规范
- ✅ 解决了所有 High 和 Medium 优先级问题
- ✅ 完成了 Low 优先级改进建议

## 相关 PR
- 响应 PR #186 的 ding113 审查意见
- 感谢 @ding113 的详细审查和建议

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
The Docker build was failing because Next.js webpack was trying to bundle
Node.js built-in modules (net, tls, crypto, stream, perf_hooks) imported
by the postgres package through instrumentation.ts.

While serverExternalPackages was configured, it wasn't sufficient for the
build phase. This fix adds explicit webpack externals configuration to
properly exclude Node.js built-in modules from the server bundle.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
## 修改内容

### 1. 修复 null 安全问题 (🟠 High Priority) ✅
**问题**: Map.get() 返回 `string | undefined`,直接插值可能产生 "undefined" 字符串

**解决方案**:
- 添加验证检查,确保所有必需的时间部分都已成功获取
- 如果任一部分缺失,记录警告并回退到 ISO 8601 格式
- 提供详细的日志信息(timestamp, timezone)便于调试

**代码**:
```typescript
if (!year || !month || !day || !hour || !minute) {
  logger.warn(
    "[log-time-formatter] Failed to get all date parts, falling back to ISO string",
    { timestamp, timezone }
  );
  return new Date(timestamp).toISOString();
}
```

**效果**: 防止输出 "undefined/undefined/undefined undefined:undefined" 的无效日期字符串

### 2. 添加文件级文档说明 (🟡 Medium Priority) ✅
**问题**: 文件未被使用,违反 YAGNI 原则

**解决方案**:
- 添加详细的模块文档,说明计划用途
- 列出具体的集成场景:
  1. Dashboard 日志显示组件
  2. 实时监控页面
  3. 日志导出功能
- 添加集成计划清单
- 预留 issue 链接位置

**效果**:
- 明确工具函数的设计意图和使用场景
- 为后续集成提供清晰的路线图
- 符合代码文档最佳实践

## 技术验证
- ✅ TypeScript 类型检查通过
- ✅ 增强了代码健壮性
- ✅ 提供了清晰的文档和集成计划

## 相关 PR
- 响应 PR #186 的最新审查意见
- 感谢 @ding113@gemini-code-assist 的详细审查

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
## 修改内容

### 1. 删除未使用的 log-time-formatter.ts (🟢 Low) ✅
**问题**: 整个文件未被使用,违反 YAGNI 原则

**解决方案**: 删除文件,遵循 "You Aren't Gonna Need It" 原则

**理由**:
- 文件中包含 TODO 清单,但没有实际集成
- 没有任何导入或使用此文件的代码
- 根据 CLAUDE.md: "不要为假设的未来需求设计"
- 当实际需要时可以在未来 PR 中重新添加

**效果**: 减少技术债务和维护负担

### 2. 优化错误处理避免代码重复 (🟡 Medium) ✅
**问题**: `initializeErrorRuleDetector()` 函数内部捕获错误但不传播,可能掩盖严重问题

**解决方案**:
- 移除函数内部的 try-catch,让关键错误传播
- 在调用处添加 try-catch,实现优雅降级
- 添加清晰的注释说明错误处理策略

**代码**:
```typescript
// 函数内部 - 让关键错误传播
async function initializeErrorRuleDetector(): Promise<void> {
  await initializeDefaultErrorRules();
  logger.info("Default error rules initialized successfully");

  await errorRuleDetector.reload();
  logger.info("Error rule detector cache loaded successfully");
}

// 调用处 - 优雅降级
try {
  await initializeErrorRuleDetector();
} catch (error) {
  logger.error("[Instrumentation] Non-critical: Error rule detector initialization failed", error);
  // 继续启动 - 错误检测不是核心功能的关键依赖
}
```

**效果**:
- ✅ 关键错误可以被正确传播和处理
- ✅ 调用者可以决定是否需要优雅降级
- ✅ 避免掩盖严重的数据库连接问题
- ✅ 保持应用启动的灵活性

## 技术验证
- ✅ TypeScript 类型检查通过
- ✅ 遵循 YAGNI 原则
- ✅ 改进错误处理策略
- ✅ 减少代码重复

## 相关 PR
- 响应 PR #186 的最新审查意见
- 感谢 @ding113 的详细审查和建议

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
fix: 优化供应商测试和日志系统
- 移除 session.requestUrl 的 readonly 属性
- 在 ModelRedirector.apply() 中添加 Gemini URL 路径重写逻辑
- Gemini API 模型名称通过 URL 路径传递,需要修改 pathname 而非 body
问题原因:
- 测试请求未显式设置 stream: false,导致上游可能返回流式响应
- Cloudflare 在处理 SSE 流式响应时可能触发 520 错误
- 请求头不够完整,可能被 Cloudflare Bot 检测拦截

修复内容:
- Anthropic Messages API 测试添加 stream: false 参数
- 添加完整的 HTTP 请求头(Accept, Accept-Language, Accept-Encoding, Connection)
- 添加代理失败降级逻辑(520/502/504 错误时自动尝试直连)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
问题原因:
- 代理降级时,如果直连失败继续使用原始响应
- 但原始响应已通过 clone() 读取过,后续 response.text() 会报错

修复内容:
- 直连失败时直接返回组合错误信息,不再尝试读取原始响应
- 提供更详细的错误信息(代理错误 + 直连错误)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
10 秒超时对部分供应商可能不够

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
1. 添加免责声明提示
   - 测试会消耗真实额度
   - 结果仅供参考,不代表实际调用效果

2. 修复详情对话框按钮样式
   - 关闭按钮改为 outline 样式,与复制按钮保持一致

3. 格式化生产环境日志时间戳
   - 从 Unix 毫秒时间戳改为 ISO 8601 格式 (2025-11-24T10:00:00.000Z)
   - 便于阅读和时区识别

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
部分第三方 Claude 中转站(如 co.yes.vg)使用 Authorization: Bearer 认证
而非官方的 x-api-key 认证,导致 401 Unauthorized 错误

现在同时发送两种认证头:
- x-api-key: 官方 Anthropic API
- Authorization: Bearer: 第三方中转站

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
OpenAI Responses API 的 input 数组中每个元素需要包含 type: "message"
缺少此字段会导致 400 Bad Request 错误

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
timestamp 应该是 pino 配置的顶级选项,而不是 formatters 的子选项
之前的配置导致时间戳仍然显示为 Unix 毫秒时间戳

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
ApiTestButton 组件内已添加了更详细的免责声明提示
删除 provider-form 中的旧版简短提示,避免重复显示

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Remove the `notice` translation key that was replaced by the new
disclaimer component in api-test-button.tsx.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Add disclaimer.title/realRequest/resultReference/confirmConfig keys
- Localize api-test-button.tsx disclaimer component
- Add dark mode support for disclaimer styling
- Add missing apiTest section to zh-TW settings

Supported languages: zh-CN, en, zh-TW

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
改进点:
1. 添加 trace 日志记录原始错误响应文本
2. 尝试多种错误路径提取:
   - errorJson.error?.message (OpenAI 标准)
   - errorJson.error?.error (嵌套 error)
   - errorJson.message (简单 message)
   - errorJson.error_message
   - errorJson.detail
   - errorJson.error (字符串类型)
   - 整个 error 对象序列化 (fallback)
3. 添加解析结果的 trace 日志,包含提取的详情和 JSON keys
4. 添加 JSON 解析失败的 trace 日志

这样可以在 trace 日志级别下看到完整的错误响应,
帮助调试不同供应商的错误响应格式差异
- 支持供应商 groupTag 多标签(如 'cli,chat')与用户单标签(如 'cli')的匹配
- 修复用户分组过滤逻辑:使用标签交集判断而非完全匹配
- 同时修复 Session 复用中的相同问题
- 解决用户设置单标签无法匹配到多标签供应商的问题
@ding113 ding113 added enhancement New feature or request documentation Improvements or additions to documentation labels Nov 24, 2025
Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

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

📋 Code Review Summary

This PR contains multiple bug fixes and optimizations covering provider API testing, streaming response handling, Docker builds, model redirection, and error rule matching. While most changes are solid, there are 2 critical security issues and 1 high-priority bug that must be addressed before merge.

🔍 Issues Found

  • Critical (🔴): 2 issues
  • High (🟠): 1 issue
  • Medium (🟡): 2 issues
  • Low (🟢): 3 issues

🎯 Priority Actions

  1. [CRITICAL] Add SSRF protection validation in API test button - currently allows testing arbitrary internal URLs
  2. [CRITICAL] Add webpack externals configuration to prevent Node.js native module bundling errors during Docker builds
  3. [HIGH] Fix timeout environment variable fallback logic - wrong order causes user-configured values to be ignored
  4. [MEDIUM] Add error handling for stream controller operations to prevent unhandled promise rejections
  5. [MEDIUM] Validate regex patterns before compilation in error rule detector

💡 General Observations

  • The streaming timeout change from 10s to 300s is very aggressive - monitor production carefully for hung connections
  • The CI=true workaround is fragile - proper webpack externals configuration is needed for long-term stability
  • Consider adding rate limiting to API test endpoints to prevent abuse
  • Good use of atomic operations and proper error categorization in response handler

🤖 Automated review by Claude AI - focused on identifying issues for improvement

Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

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

📝 Documentation Review

📊 Issues Summary

  • Critical (🔴): 0 - Must fix before merge
  • High (🟠): 0 - Should fix before merge
  • Medium (🟡): 0 - Consider addressing
  • Low (🟢): 0 - Optional improvements

⚡ Priority Fixes

None - documentation looks good!

📋 Review Coverage

  • Technical accuracy - No issues found

    • ✅ Environment variable range (5000-120000) matches code implementation
    • ✅ Default value (15000) correctly documented
    • ✅ Timeout value consistently referenced across all documentation
  • Completeness - No issues found

    • ✅ Variable documented in README.en.md
    • ✅ Variable documented in README.md
    • ✅ Variable documented in .env.example with detailed comments
    • ✅ Dockerfile CI environment variable properly commented
  • Code examples - Not applicable (no code examples in this PR)

  • Links and references - No issues found

    • ✅ Reference to .env.example is valid
  • Clarity and organization - No issues found

    • ✅ English documentation clear and concise
    • ✅ Chinese documentation clear and idiomatic
    • ✅ Table formatting correct in both languages
    • ✅ Comments in .env.example are detailed and helpful

💡 General Observations

The documentation changes are well-executed:

  1. Bilingual Consistency: Both English and Chinese versions maintain identical information with appropriate localization
  2. Technical Precision: The documented range (5000-120000 ms) and default (15000 ms) match the actual code implementation in src/actions/providers.ts
  3. User-Friendly: The Chinese version includes helpful context ("跨境网络可适当提高" - "can be increased for cross-border networks")
  4. Complete Coverage: The new environment variable is documented in all appropriate locations (.env.example, both README files)
  5. Code Comments: The Dockerfile comment clearly explains the purpose of the CI environment variable

Verified against implementation:

  • Code uses API_TEST_TIMEOUT_LIMITS with MIN: 5000, MAX: 120000, DEFAULT: 15000
  • Out-of-range values fall back to default with warning logs
  • Implementation includes proper validation and error handling

APPROVED - Ready to merge

🤖 Automated docs review by Claude AI

Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

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

🔴 Critical: SSRF vulnerability in API test function

File: src/actions/providers.ts (lines ~1181-1268 in diff)

Why this is a problem: The executeProviderApiTest function directly fetches user-provided URLs without SSRF validation. Although only admins can access this feature, it still allows:

  • Scanning internal networks (10.x.x.x, 192.168.x.x, 172.16-31.x.x)
  • Accessing localhost services (127.0.0.1, ::1)
  • Port scanning via timeout differences
  • Reading cloud metadata endpoints (169.254.169.254)

Suggested fix:

// Add validation BEFORE fetch
import { isInternalUrl } from "@/lib/utils/validation";

// Inside executeProviderApiTest, before the fetch call:
if (isInternalUrl(normalizedProviderUrl)) {
  return {
    ok: false,
    error: "安全限制:不允许访问内部网络地址",
  };
}

// Create helper function in lib/utils/validation.ts:
export function isInternalUrl(url: string): boolean {
  try {
    const parsed = new URL(url);
    const hostname = parsed.hostname;
    
    // Block localhost
    if (["localhost", "127.0.0.1", "::1", "0.0.0.0"].includes(hostname)) {
      return true;
    }
    
    // Block internal IP ranges
    if (/^10\.|^172\.(1[6-9]|2\d|3[01])\.|^192\.168\./.test(hostname)) {
      return true;
    }
    
    // Block link-local addresses (AWS metadata, etc.)
    if (/^169\.254\.|^fe80:/i.test(hostname)) {
      return true;
    }
    
    // Block dangerous ports
    const port = parseInt(parsed.port || "0", 10);
    const dangerousPorts = [22, 23, 3306, 5432, 6379, 27017];
    if (dangerousPorts.includes(port)) {
      return true;
    }
    
    return false;
  } catch {
    return true; // Block invalid URLs
  }
}

Note: The README mentions SSRF protection exists, but I don't see it implemented in this PR's changes to the API test function.

Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

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

🔴 Critical: Missing webpack externals for Docker build

File: next.config.ts (line 31)

Why this is a problem: The Dockerfile adds CI=true to skip database connections during build, but this doesn't prevent webpack from trying to bundle Node.js native modules (ioredis, bull, dns, etc.) into the client bundle. This causes build failures like:

Module not found: Can't resolve 'dns'
Module not found: Can't resolve 'net'

The serverExternalPackages config only prevents these packages from being bundled for server runtime, but doesn't prevent webpack from attempting to resolve them during the client build phase when analyzing module dependencies.

Suggested fix:

const nextConfig: NextConfig = {
  output: "standalone",
  transpilePackages: ["@lobehub/icons"],
  serverExternalPackages: ["bull", "bullmq", "@bull-board/api", "@bull-board/express", "ioredis"],
  outputFileTracingIncludes: {
    "/**": ["./node_modules/undici/**/*", "./node_modules/socks-proxy-agent/**/*"],
  },
  
  // ✅ ADD THIS: Prevent Node.js modules from being bundled in client
  webpack: (config, { isServer }) => {
    if (!isServer) {
      config.resolve.fallback = {
        ...config.resolve.fallback,
        // Node.js native modules should not be in client bundle
        net: false,
        dns: false,
        tls: false,
        fs: false,
        child_process: false,
      };
    }
    return config;
  },
  
  experimental: {
    serverActions: {
      bodySizeLimit: "500mb",
    },
  },
};

export default withNextIntl(nextConfig);

Alternative solution: If you don't want to modify webpack config, ensure that all server-only imports use Next.js server-only package:

import "server-only";
import { redis } from "@/lib/redis";

But the webpack approach is more robust and catches transitive dependencies automatically.

Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

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

🟠 High: Environment variable fallback logic may be incorrect

Files: .env.example, src/lib/constants/provider.constants.ts (inferred)

Why this is a problem: The new API_TEST_TIMEOUT_MS environment variable is documented in .env.example, but based on the code pattern in src/actions/providers.ts line 35-39:

const API_TEST_CONFIG = {
  TIMEOUT_MS: 10000, // Hardcoded 10 seconds
  // ...
} as const;

This constant is used throughout the code, but there's no evidence it reads from the environment variable. If the code does:

const timeout = config.timeout ?? API_TEST_CONFIG.TIMEOUT_MS;

Then user-configured API_TEST_TIMEOUT_MS=30000 will be ignored because the constant is always 10000.

Suggested fix:
Verify the environment variable is actually read. It should be:

// In src/lib/config/env.schema.ts or similar:
export const API_TEST_TIMEOUT_MS = parseInt(
  process.env.API_TEST_TIMEOUT_MS || "15000", 
  10
);

// Then in providers.ts:
import { API_TEST_TIMEOUT_MS } from "@/lib/config/env";

const timeout = config.timeout ?? API_TEST_TIMEOUT_MS;

Verification needed: Please confirm that API_TEST_TIMEOUT_MS environment variable is actually read at runtime, not just documented in .env.example.

Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

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

🟡 Medium: Unhandled rejection in stream controller error handling

File: src/app/v1/_lib/proxy/response-handler.ts (lines 700-714)

Why this is a problem: The idle timeout handler calls streamController.error() to close the client stream, but this can throw a TypeError if the stream is already closed. The current try-catch swallows the error silently:

try {
  if (streamController) {
    streamController.error(new Error("Streaming idle timeout"));
    logger.debug("ResponseHandler: Client stream closed due to idle timeout", {
      taskId,
      providerId: provider.id,
    });
  }
} catch (e) {
  logger.warn("ResponseHandler: Failed to close client stream", {
    taskId,
    providerId: provider.id,
    error: e,  // ❌ No error details logged
  });
}

When the client disconnects before the timeout fires, calling controller.error() throws:

TypeError: Cannot call error on a closed stream

This creates "unhandled promise rejection" warnings and makes debugging difficult.

Suggested fix:

try {
  if (streamController) {
    streamController.error(new Error("Streaming idle timeout"));
    logger.debug("ResponseHandler: Client stream closed due to idle timeout", {
      taskId,
      providerId: provider.id,
    });
  }
} catch (e) {
  const err = e as Error;
  // Only log if it's not an expected "already closed" error
  if (!err.message?.toLowerCase().includes("closed")) {
    logger.warn("ResponseHandler: Failed to close client stream", {
      taskId,
      providerId: provider.id,
      error: err.message,
      errorType: err.name,
    });
  } else {
    logger.trace("ResponseHandler: Stream already closed (expected)", {
      taskId,
      providerId: provider.id,
    });
  }
}

This differentiates between expected "already closed" errors and unexpected errors, reducing log noise while maintaining debuggability.

Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

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

🟡 Medium: Unsafe regex compilation can crash error detector

File: src/lib/error-rule-detector.ts (lines 143-163)

Why this is a problem: The regex pattern is validated with safe-regex AFTER construction, but the RegExp constructor itself can throw on invalid syntax:

case "regex": {
  // ❌ safe-regex check AFTER construction
  if (!safeRegex(rule.pattern)) {
    logger.warn(`ReDoS risk: ${rule.pattern}`);
    skippedRedosCount++;
    break;
  }
  
  const pattern = new RegExp(rule.pattern, "i"); // ⚠️ Can throw here!
  this.regexPatterns.push({ pattern, category: rule.category });
  validRegexCount++;
  break;
}

If an admin enters an invalid regex (e.g., [unclosed, (?!invalid), the new RegExp() call throws a SyntaxError. This bubbles up and prevents the error detector from loading any rules, breaking the entire error categorization system.

Suggested fix (reorder validation):

case "regex": {
  try {
    // STEP 1: Validate syntax by attempting to compile
    let pattern: RegExp;
    try {
      pattern = new RegExp(rule.pattern, "i");
    } catch (syntaxError) {
      logger.error(
        `[ErrorRuleDetector] Invalid regex syntax: ${rule.pattern}`,
        { error: syntaxError }
      );
      break; // Skip this rule, continue with others
    }
    
    // STEP 2: Check ReDoS risk (only if syntax is valid)
    if (!safeRegex(rule.pattern)) {
      logger.warn(
        `[ErrorRuleDetector] ReDoS risk detected: ${rule.pattern}, skipping`
      );
      skippedRedosCount++;
      break;
    }
    
    // STEP 3: Add to cache
    this.regexPatterns.push({
      pattern,
      category: rule.category,
      description: rule.description ?? undefined,
    });
    validRegexCount++;
  } catch (error) {
    // Catch-all for unexpected errors
    logger.error(
      `[ErrorRuleDetector] Unexpected error processing pattern: ${rule.pattern}`,
      { error }
    );
  }
  break;
}

Benefit: Invalid patterns are skipped individually instead of crashing the entire detector, allowing valid rules to continue working.

Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

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

🟢 Low: Hardcoded Chinese strings bypass i18n system

File: src/app/[locale]/settings/providers/_components/forms/api-test-button.tsx

Lines affected: 264, 314-332 (handleCopyResult), 480-481, 574

Why this is a problem: The component uses useTranslations() for most text, but hardcodes Chinese strings in several places, breaking localization for English/other language users:

// Line 264
console.error("测试 API 连通性失败:", error);

// Line 314-332 (handleCopyResult function)
const resultText = [
  `测试结果: ${testResult.success ? "成功" : "失败"}`,
  `消息: ${testResult.message}`,
  // ...
];

// Line 480-481
显示前 {API_TEST_UI_CONFIG.MAX_PREVIEW_LENGTH} 字符,完整内容请复制查看

// Line 574
显示前 200 字符,完整内容请点击"查看详情"

Suggested fix:

  1. Console logs should use English (not user-facing):
// Line 264
console.error("API test failed:", error);
  1. UI strings should use i18n:
// Add to messages/zh-CN/settings.json:
{
  "apiTest": {
    "truncatedPreview": "显示前 {length} 字符,完整内容请复制查看",
    "truncatedBrief": "显示前 {length} 字符,完整内容请点击"查看详情"",
    "copyFormat": {
      "result": "测试结果",
      "success": "成功",
      "failed": "失败",
      "message": "消息",
      "model": "模型",
      "responseTime": "响应时间",
      "usage": "Token 用量",
      "responseContent": "响应内容",
      "errorDetails": "错误详情"
    }
  }
}

// Add to messages/en/settings.json:
{
  "apiTest": {
    "truncatedPreview": "Showing first {length} characters, copy to see full content",
    "truncatedBrief": "Showing first {length} characters, click \"View Details\" for more",
    "copyFormat": {
      "result": "Test Result",
      "success": "Success",
      "failed": "Failed",
      // ...
    }
  }
}

// Then use in component:
{t("apiTest.truncatedPreview", { length: API_TEST_UI_CONFIG.MAX_PREVIEW_LENGTH })}

// For handleCopyResult:
const resultText = [
  `${t("apiTest.copyFormat.result")}: ${testResult.success ? t("apiTest.copyFormat.success") : t("apiTest.copyFormat.failed")}`,
  `${t("apiTest.copyFormat.message")}: ${testResult.message}`,
  // ...
];

This ensures consistent internationalization across the entire component.

Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

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

🟢 Low: Inconsistent preview length constants

File: src/app/[locale]/settings/providers/_components/forms/api-test-button.tsx

Lines: 39-43, 568

Why this is a problem: The component defines MAX_PREVIEW_LENGTH: 500 but hardcodes a different value (200) for the brief preview:

// Line 39-43
const API_TEST_UI_CONFIG = {
  MAX_PREVIEW_LENGTH: 500, // Used in dialog
  // ...
} as const;

// Line 568
{testResult.details.content.slice(
  0,
  Math.min(200, testResult.details.content.length)  // ❌ Magic number
)}
{testResult.details.content.length > 200 && "..."}

// Line 574
显示前 200 字符,完整内容请点击"查看详情"  // ❌ Hardcoded again

This creates confusion:

  • Dialog shows 500 characters
  • Brief preview shows 200 characters
  • Message says "200 characters" but uses Math.min(200, length) which is redundant

Suggested fix:

// Line 39-43: Define both constants
const API_TEST_UI_CONFIG = {
  MAX_PREVIEW_LENGTH: 500,        // For detail dialog
  BRIEF_PREVIEW_LENGTH: 200,      // For inline preview
  TOAST_SUCCESS_DURATION: 3000,
  TOAST_ERROR_DURATION: 5000,
} as const;

// Line 565-577: Use the constant
<pre className="whitespace-pre-wrap break-words max-h-32 overflow-y-auto">
  {testResult.details.content.slice(
    0,
    API_TEST_UI_CONFIG.BRIEF_PREVIEW_LENGTH
  )}
  {testResult.details.content.length > API_TEST_UI_CONFIG.BRIEF_PREVIEW_LENGTH && "..."}
</pre>
{testResult.details.content.length > API_TEST_UI_CONFIG.BRIEF_PREVIEW_LENGTH && (
  <div className="text-muted-foreground italic">
    {t("truncatedBrief", { length: API_TEST_UI_CONFIG.BRIEF_PREVIEW_LENGTH })}
  </div>
)}

This makes the code self-documenting and easier to maintain. If you need to change the preview length, update one constant instead of hunting for magic numbers.

Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

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

🟢 Low: Inconsistent structured logging format

File: src/app/v1/_lib/proxy/model-redirector.ts

Lines: 29, 36-37, 43-45, 62

Why this is a problem: The model redirector uses inconsistent logging formats - sometimes interpolating values into the message string, sometimes using structured context:

// Line 29: Structured (good)
logger.debug("[ModelRedirector] No model found in request, skipping redirect");

// Line 36-37: Interpolated (inconsistent)
logger.debug(
  `[ModelRedirector] No redirect configured for model "${originalModel}" in provider ${provider.id}`
);

// Line 43-45: Interpolated with provider ID (harder to query)
logger.info(
  `[ModelRedirector] Redirecting model: "${originalModel}" → "${redirectedModel}" (provider ${provider.id})`
);

This makes log aggregation and searching more difficult. For example, querying "all redirects for provider 123" requires regex parsing of the message string instead of filtering by providerId field.

Suggested fix (use structured logging consistently):

// Line 29: Keep as-is (good)
logger.debug("[ModelRedirector] No model found in request, skipping redirect");

// Line 36-40: Use structured context
logger.debug(
  "[ModelRedirector] No redirect configured for model",
  {
    model: originalModel,
    providerId: provider.id,
    providerName: provider.name,
  }
);

// Line 43-45: Use structured context
logger.info(
  "[ModelRedirector] Redirecting model",
  {
    providerId: provider.id,
    providerName: provider.name,
    originalModel,
    redirectedModel,
  }
);

// Line 62: Use structured context
logger.debug(
  "[ModelRedirector] Model redirect recorded in provider chain",
  {
    providerId: provider.id,
    originalModel,
    redirectedModel,
  }
);

Benefits:

  • Easier to query logs by provider ID: providerId=123
  • Better integration with log aggregation tools (Datadog, Splunk, etc.)
  • Consistent format across the codebase
  • Machine-readable log fields

This pattern should be applied to all logger calls in the file (and ideally project-wide).

@ding113
Copy link
Owner Author

ding113 commented Nov 24, 2025

🔒 Security Scan Results

No security vulnerabilities detected

This PR has been scanned against OWASP Top 10, CWE Top 25, and common security anti-patterns. No security issues were identified in the code changes.

🛡️ Security Features Verified

✅ SSRF Protection (A05: Broken Access Control)

  • Location: src/actions/providers.ts:validateProviderUrlForConnectivity() (lines 1000-1066)
  • Protection: Comprehensive SSRF mitigation for API connectivity testing
    • ✅ Blocks private/internal IP ranges (10.x.x.x, 172.16-31.x.x, 192.168.x.x, 127.0.0.1, localhost)
    • ✅ Blocks link-local addresses (169.254.x.x, fe80::/10)
    • ✅ Blocks dangerous ports (SSH:22, Telnet:23, MySQL:3306, PostgreSQL:5432, Redis:6379, MongoDB:27017)
    • ✅ Admin-only access control enforced (line 1085-1087)
  • Security Impact: Prevents attackers from using the API test feature to scan internal networks or access internal services

✅ ReDoS Prevention (A03: Sensitive Data Exposure / DoS)

  • Location: src/lib/error-rule-detector.ts (lines 143-151)
  • Protection: Uses safe-regex library to detect catastrophic backtracking patterns before compiling regex
    • ✅ Validates all user-supplied regex patterns before execution
    • ✅ Skips unsafe patterns and logs warnings
    • ✅ Prevents denial-of-service attacks via malicious regex patterns
  • Security Impact: Prevents attackers from submitting regex patterns that could cause exponential CPU time (ReDoS attacks)

✅ Information Disclosure Prevention (A03: Sensitive Data Exposure)

  • Location: src/actions/providers.ts:sanitizeErrorTextForLogging() (lines 1027-1046)
  • Protection: Comprehensive PII and credential sanitization in error logs
    • ✅ Redacts API keys matching patterns: sk-, rk-, pk- (line 1033)
    • ✅ Redacts email addresses (line 1034)
    • ✅ Redacts Bearer tokens (line 1035)
    • ✅ Redacts password/token/secret fields (lines 1036-1039)
    • ✅ Redacts sensitive file paths (.env, .yaml, .json, .conf, .ini) (line 1040)
    • ✅ Truncates long error messages to prevent log injection
  • Security Impact: Prevents credential leakage in application logs and external monitoring systems

✅ Input Validation (A03: Injection / A07: XSS)

  • Protection: Strict input validation and sanitization
    • ✅ Proxy URL validation with protocol allowlist (line 1089)
    • ✅ Timeout value validation with min/max bounds (lines 16-35)
    • ✅ Stream resource limits to prevent DoS (lines 44-48):
      • Max 1000 stream chunks
      • Max 10MB buffer size
      • Max 10000 iterations
  • Security Impact: Prevents injection attacks and denial-of-service via resource exhaustion

✅ SQL Injection Protection (A01: Injection)

  • Location: drizzle/0023_cheerful_shocker.sql
  • Analysis: Migration uses parameterized DDL statement (ALTER TABLE with literal value)
    • ✅ No dynamic SQL generation
    • ✅ No user input in migration
    • ✅ No concatenation vulnerabilities
  • Security Impact: N/A - Static schema change only

✅ Secure Configuration (A06: Security Misconfiguration)

  • Location: deploy/Dockerfile (line 25), src/instrumentation.ts (lines 11-17)
  • Analysis: CI environment detection to skip database connections during build
    • ✅ Prevents build failures with placeholder credentials
    • ✅ Uses environment variable check (CI=true) instead of hardcoded secrets
    • ✅ Graceful degradation for non-critical features
  • Security Impact: Prevents credential leakage in CI/CD pipelines

📋 OWASP Top 10 Coverage

  • A01: Injection - No SQL/NoSQL/Command/LDAP injection vulnerabilities found
  • A02: Broken Authentication - No authentication bypass or credential issues found
  • A03: Sensitive Data Exposure - Comprehensive sanitization implemented
  • A04: XML External Entities - N/A (no XML parsing in this PR)
  • A05: Broken Access Control - SSRF protection and admin-only checks in place
  • A06: Security Misconfiguration - Secure CI configuration implemented
  • A07: Cross-Site Scripting - No XSS vectors found
  • A08: Insecure Deserialization - All JSON parsing has proper error handling
  • A09: Using Components with Known Vulnerabilities - Uses safe-regex for ReDoS protection
  • A10: Insufficient Logging & Monitoring - Comprehensive structured logging with sanitization

🔍 Additional Security Checks

  • Path Traversal: No file operations with user-controlled paths
  • Race Conditions: Redis atomic operations used for rate limiting
  • Cryptographic Issues: No weak crypto or insecure random number generation
  • Privilege Escalation: Admin role checks enforced
  • TOCTOU Bugs: No time-of-check to time-of-use vulnerabilities
  • Arbitrary Code Execution: No eval(), exec(), spawn() with user input
  • Resource Exhaustion: Stream limits prevent DoS attacks

🎯 Security Best Practices Observed

  1. Defense in Depth: Multiple layers of validation (URL format, IP range, port checks)
  2. Fail Secure: Invalid inputs are rejected rather than sanitized
  3. Least Privilege: Admin-only access for sensitive operations
  4. Secure by Default: Safe timeout values with bounded ranges
  5. Input Validation: Allowlist-based validation for critical inputs
  6. Output Encoding: Comprehensive sanitization before logging
  7. Error Handling: No stack traces or sensitive data in user-facing errors

📊 Code Quality Metrics

  • Total Files Scanned: 22 TypeScript/SQL files
  • Security-Sensitive Functions: 6 critical functions analyzed
  • Lines of Security Code: ~400 lines of security-focused code (validation, sanitization, protection)
  • False Positives: 0 (all findings verified)

🤖 Automated security scan by Claude AI - OWASP Top 10 & CWE coverage
🔬 *Scan completed at: *
🎯 Commit SHA: 743605d

@ding113 ding113 added the size/L Large PR (< 1000 lines) label Nov 25, 2025
@ding113
Copy link
Owner Author

ding113 commented Nov 25, 2025

📊 PR Size Analysis

This PR is Large (L) with ~1,335 lines of code changed across 31 files (excluding ~1,570 lines of auto-generated Drizzle snapshot).

Large PRs are harder to review and more likely to introduce bugs.

🔀 Suggested Split:

Based on the changes, this PR could be split into 5 smaller PRs:

  1. PR 1: Docker Build Fix (~35 lines)

    • deploy/Dockerfile
    • next.config.ts
    • src/instrumentation.ts
    • Fix: CI environment variable and webpack externals
  2. PR 2: Provider API Test Improvements (~950 lines)

    • src/actions/providers.ts (major refactor: +839 lines)
    • src/app/[locale]/settings/providers/_components/forms/api-test-button.tsx
    • .env.example, README.md, README.en.md (API_TEST_TIMEOUT_MS)
    • messages/*.json (i18n strings)
    • Feature: Streaming support and improved API testing
  3. PR 3: Model Redirector & Provider Selector Fixes (~80 lines)

  4. PR 4: Error Rule System Improvements (~90 lines)

    • src/actions/error-rules.ts
    • src/lib/error-rule-detector.ts
    • src/repository/error-rules.ts
    • Fix: Regex matching and cache refresh
  5. PR 5: Logging & Minor Fixes (~60 lines)

  6. PR 6: DB Migration (separate)

    • drizzle/0023_cheerful_shocker.sql
    • src/drizzle/schema.ts
    • Change: Streaming timeout default 10s → 300s

Why Split?

  • Easier to review: Each PR focuses on one concern
  • Faster CI feedback: Smaller changes merge faster
  • Easier to revert: Issues can be isolated
  • Better git history: Clear change attribution

Current State

The PR description shows this already combines fixes for 6 different issues (#185, #186, #188, #189, #190 + unlisted fixes), which confirms it should ideally be separate PRs.


🤖 Automated analysis by Claude AI

@ding113
Copy link
Owner Author

ding113 commented Nov 25, 2025

📝 Documentation Review

Documentation looks good

No significant issues found. The documentation changes are:

  • Technically accurate - Values match implementation (DEFAULT: 15000, MIN: 5000, MAX: 120000)
  • Complete - Both Chinese (README.md) and English (README.en.md) versions updated consistently
  • Well-structured - Follows existing table format and style

📋 Review Coverage

File Changes Status
README.md +1 line (environment variable table) ✅ Accurate
README.en.md +1 line (environment variable table) ✅ Accurate
.env.example +4 lines (new config section) ✅ Complete with comments

📊 Issues Summary

  • Critical (🔴): 0
  • High (🟠): 0
  • Medium (🟡): 0
  • Low (🟢): 0

✅ Verification

The documented API_TEST_TIMEOUT_MS values were verified against the implementation:

const API_TEST_TIMEOUT_LIMITS = {
  DEFAULT: 15000,  // ✅ Matches docs
  MIN: 5000,       // ✅ Matches docs  
  MAX: 120000,     // ✅ Matches docs
} as const;

🤖 Automated docs review by Claude AI

@ding113
Copy link
Owner Author

ding113 commented Nov 25, 2025

🔒 Security Scan Results

No security vulnerabilities detected

This PR has been scanned against OWASP Top 10, CWE Top 25, and common security anti-patterns. No security issues were identified in the code changes.

Scanned Categories

  • ✅ Injection attacks (SQL, NoSQL, Command, LDAP, etc.)
  • ✅ Authentication and session management
  • ✅ Sensitive data exposure
  • ✅ Access control and authorization
  • ✅ Security misconfiguration
  • ✅ Cross-site scripting (XSS)
  • ✅ Insecure deserialization
  • ✅ SSRF and path traversal
  • ✅ Cryptographic weaknesses

Key Security Controls Verified

1. SSRF Protection (src/actions/providers.ts:990-1066)

validateProviderUrlForConnectivity() implements comprehensive SSRF defenses:

  • ✅ Blocks localhost and loopback addresses (127.x.x.x, ::1)
  • ✅ Blocks private IP ranges (10.x.x.x, 172.16-31.x.x, 192.168.x.x)
  • ✅ Blocks link-local addresses (169.254.x.x, fe80::/10)
  • ✅ Blocks private IPv6 ranges (fc00::/7, fd00::/8)
  • ✅ Blocks dangerous ports (SSH 22, Telnet 23, DB ports: 3306, 5432, 6379, 27017, 9200)
  • ✅ Only allows HTTP/HTTPS protocols

2. ReDoS Protection (src/actions/error-rules.ts:98-116)

Error rule creation/update validates regex patterns:

  • ✅ Uses safe-regex library to detect catastrophic backtracking
  • ✅ Validates regex syntax before saving
  • ✅ Prevents users from creating DoS-prone patterns

3. Input Validation

  • ✅ API test timeout: Validated range 5000-120000ms with safe defaults
  • ✅ Proper error handling with fallback to defaults
  • ✅ Type-safe parsing with Number.parseInt() and Number.isFinite()

4. Authorization Controls

  • ✅ All sensitive actions require admin role check (session.user.role !== "admin")
  • ✅ API connectivity tests restricted to admins only
  • ✅ Error rule management restricted to admins only

5. Information Disclosure Prevention

  • ✅ Response preview limited to 500 chars (API_TEST_CONFIG.MAX_RESPONSE_PREVIEW_LENGTH)
  • ✅ Error messages sanitized in logs
  • ✅ URL credentials redacted (.replace(/:\/\/[^@]*@/, "://***@"))
  • ✅ API keys never logged in clear text

6. Resource Limits (DoS Prevention)

New stream limits added to prevent resource exhaustion:

  • ✅ MAX_STREAM_CHUNKS: 1000 chunks
  • ✅ MAX_STREAM_BUFFER_SIZE: 10MB
  • ✅ MAX_STREAM_ITERATIONS: 10000 iterations

7. Timeout Configuration

  • ✅ API test timeout configurable via environment (API_TEST_TIMEOUT_MS)
  • ✅ Range validation (5-120 seconds)
  • ✅ Safe default of 15 seconds

Changes Reviewed

Files analyzed: 31 files

  • Configuration files (.env.example, Dockerfile)
  • Database migrations (drizzle/0023_*.sql)
  • Action handlers (src/actions/providers.ts, error-rules.ts)
  • Proxy logic (src/app/v1/_lib/proxy/*.ts)
  • UI components (API test button, provider forms)

Security-relevant changes:

  1. ✅ Added configurable API test timeout (no injection risk)
  2. ✅ Enhanced SSRF protection for provider URL validation
  3. ✅ Added ReDoS protection for user-defined regex patterns
  4. ✅ Improved error message detection (now scans full response body)
  5. ✅ Added stream resource limits to prevent DoS
  6. ✅ Fixed model redirection tracking (no security impact)
  7. ✅ Enhanced logging with proper sanitization

Conclusion

This PR demonstrates strong security practices:

  • Multiple layers of input validation
  • Proper authorization checks
  • SSRF and ReDoS protections
  • Resource limits to prevent DoS
  • Information disclosure prevention
  • No hardcoded secrets or credentials

Recommendation: ✅ APPROVED from security perspective - Safe to merge.


🤖 Automated security scan by Claude AI - OWASP Top 10 & CWE coverage

Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

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

📋 Code Review Summary

This PR includes multiple bug fixes and optimizations across core modules. While the fixes address real issues (Docker build, streaming response handling, model redirection), several critical problems remain that could cause production failures or security issues.

🔍 Issues Found

  • Critical (🔴): 2 issues
  • High (🟠): 4 issues
  • Medium (🟡): 3 issues
  • Low (🟢): 0 issues

🎯 Priority Actions

  1. Add environment variable validation for API_TEST_TIMEOUT_MS - currently documented but not validated, risking crashes
  2. Fix timeout inconsistency - hardcoded 10s vs documented 15s default creates confusion
  3. Review database migration impact - 30x timeout increase affects ALL existing providers immediately
  4. Replace CI detection with explicit build-phase check - current CI=true check is fragile
  5. Improve error handling in error-rule-detector - overly broad catch blocks hide real database errors

💡 General Observations

Positive patterns:

  • Good use of structured logging throughout
  • Comprehensive timeout handling for streaming responses
  • ReDoS protection in regex compilation

Recurring issues:

  • Missing environment variable validation layer
  • Hardcoded constants that should be configurable
  • Error handling that suppresses too many error types
  • Documentation-code mismatches (timeout values, model names)

🤖 Automated review by Claude AI - focused on identifying issues for improvement

@ding113
Copy link
Owner Author

ding113 commented Nov 25, 2025

Detailed Code Review Issues for PR #192

🔴 Critical Issues

Issue 1: Missing Environment Variable Validation

File: .env.example (line 22), src/actions/providers.ts (line 35)
Problem: The new API_TEST_TIMEOUT_MS environment variable is documented but has no validation logic. Invalid values will cause undefined behavior.

Code Location:

# .env.example:22
API_TEST_TIMEOUT_MS=15000
// src/actions/providers.ts:35
const API_TEST_CONFIG = {
  TIMEOUT_MS: 10000, // ❌ Hardcoded, ignores env var

Suggested Fix:

// Add to env.schema.ts
API_TEST_TIMEOUT_MS: z.coerce
  .number()
  .min(5000)
  .max(120000)
  .default(15000),

// Use in providers.ts
import { env } from '@/lib/config/env';
const API_TEST_CONFIG = {
  TIMEOUT_MS: env.API_TEST_TIMEOUT_MS,

Issue 2: Inconsistent Timeout Values Create Confusion

File: src/actions/providers.ts (line 35)
Problem: Documentation says 15000ms default, code hardcodes 10000ms. Users will be confused why their env var doesn't work.

Suggested Fix: Use environment variable consistently (see Issue 1 fix).


🟠 High Priority Issues

Issue 3: CI Environment Check Too Broad

File: src/instrumentation.ts (line 12)
Problem: Checking process.env.CI === "true" is fragile. Many CI systems don't set this. The real need is skipping during next build, not detecting CI.

Current Code:

if (process.env.CI === "true") {
  logger.warn("[Instrumentation] CI environment detected...");
  return;
}

Suggested Fix:

if (process.env.NEXT_PHASE === 'phase-production-build' || 
    process.env.SKIP_DB_INIT === 'true') {
  logger.warn('[Instrumentation] Build phase detected: skipping DB init');
  return;
}

Update Dockerfile: ENV SKIP_DB_INIT=true


Issue 4: Missing Regex Compilation Error Handling

File: src/lib/error-rule-detector.ts (line 153)
Problem: new RegExp(rule.pattern, 'i') can throw. The try-catch wraps the entire switch case, making error messages misleading.

Current Code:

case 'regex': {
  try {
    if (!safeRegex(rule.pattern)) { /* ... */ }
    const pattern = new RegExp(rule.pattern, 'i'); // Can throw
    // ...
  } catch (error) {
    logger.error(`Invalid regex pattern: ${rule.pattern}`, error);
  }

Suggested Fix: Separate try-catch blocks for ReDoS check vs compilation.


Issue 5: Overly Broad Error Suppression

File: src/lib/error-rule-detector.ts (lines 95-109)
Problem: Catches ALL database errors and checks string message for "does not exist". This hides connection failures, permissions, etc. String matching is locale-dependent.

Suggested Fix:

try {
  rules = await getActiveErrorRules();
} catch (dbError) {
  const error = dbError as { code?: string };
  if (error.code === '42P01') { // PostgreSQL: undefined_table
    logger.warn('[ErrorRuleDetector] Table not created yet');
    return;
  }
  throw dbError; // Re-throw all other errors
}

Issue 6: Database Migration Lacks Backward Compatibility Warning

File: drizzle/0023_cheerful_shocker.sql (line 1)
Problem: Changing default from 10000ms to 300000ms (30x increase) affects ALL existing providers immediately. No warning in PR description.

Impact: Existing production providers configured for 10s timeout suddenly have 5min timeout.

Suggested Fix: Add migration comment and PR warning:

COMMENT ON COLUMN providers.streaming_idle_timeout_ms IS 
  'Default changed from 10s to 5min in migration 0023 (yyyy-mm-dd)';

🟡 Medium Priority Issues

Issue 7: Hardcoded Test Models Don't Exist

File: src/app/[locale]/settings/providers/_components/forms/api-test-button.tsx (line 56)
Problem: "gpt-5.1-codex" and "gemini-3-pro-preview" are fictional models. API tests will always fail.

Suggested Fix:

const apiFormatDefaultModel: Record<ApiFormat, string> = {
  'anthropic-messages': 'claude-sonnet-4-5-20250929',
  'openai-chat': 'gpt-4o',
  'openai-responses': 'gpt-4o',
  gemini: 'gemini-2.0-flash-exp',
};

Issue 8: Potential Race Condition in Model Redirector

File: src/app/v1/_lib/proxy/model-redirector.ts (line 65)
Problem: Accesses session.getProviderChain() without checking if it's empty. If chain is empty, redirect info is silently lost.

Suggested Fix: Add length check and warning log.


Issue 9: Webpack Externals Comment Misleading

File: next.config.ts (comment removed in diff)
Problem: PR removes webpack externals configuration but adds comment about "Node.js built-in modules". The actual issue was that Next.js 15 no longer supports webpack.externals in favor of serverExternalPackages.

Recommendation: Keep serverExternalPackages (good), but update comments to reflect Next.js 15 migration, not "avoiding bundling Node modules".


Summary Statistics

  • Total Issues: 9
  • Files Affected: 8
  • Critical Issues Requiring Immediate Fix: 2
  • High Priority Issues: 4
  • Medium Priority Issues: 3

Testing Recommendations

  1. Test with invalid API_TEST_TIMEOUT_MS values (non-numeric, out of range)
  2. Verify Docker build works without CI=true env var
  3. Test existing providers after migration 0023 to confirm timeout behavior
  4. Test API connectivity with actual OpenAI/Gemini model names
  5. Test error rule reload when database is unavailable

- fix(error-rule-detector): 修复 isLoading 在 early return 时未清除导致的死锁问题
- fix(error-rules): 修复 updateErrorRuleAction 中 matchType 默认值逻辑,确保 ReDoS 检查覆盖所有场景
- refactor(model-redirector): 统一日志格式为结构化日志,提升可观测性
- fix(i18n): 修复 api-test-button 组件中的硬编码中文字符串
- feat(error-rules): 添加 getErrorRuleById 函数支持按 ID 查询

Issues verified and ignored (not real problems):
- SSRF: validateProviderUrlForConnectivity() 已有完整保护
- Webpack externals: 配置已正确设置
- 环境变量读取: resolveApiTestTimeoutMs() 正确读取环境变量
@ding113 ding113 removed the size/L Large PR (< 1000 lines) label Nov 25, 2025
@ding113
Copy link
Owner Author

ding113 commented Nov 25, 2025

📝 Documentation Review

📊 Issues Summary

  • Critical (🔴): 1 - Must fix before merge
  • High (🟠): 0
  • Medium (🟡): 0
  • Low (🟢): 0

⚡ Critical Issue Found

🔴 Inconsistent Discount Percentage

Location: README.en.md:33 vs README.md:33

Problem: The Chinese README.md states "20% 优惠折扣" (20% discount) while the English README.en.md still states "10% discount". This inconsistency exists because commit 8a18194 ("Update discount offer from 10% to 20%") only updated README.md but missed README.en.md.

Current (Wrong in README.en.md):

you can enjoy a <b>10% discount</b>

Should Be:

you can enjoy a <b>20% discount</b>

Impact: Users reading the English documentation will see incorrect promotional information. This could cause confusion, trust issues, or complaints when the actual discount differs from documented.


📋 Review Coverage

  • Technical accuracy - 1 issue (discount percentage mismatch)
  • Completeness - No issues
  • Code examples - No issues (none in documentation changes)
  • Links and references - No issues
  • Clarity and organization - No issues

💡 General Observations

The documentation changes in this PR are minimal and well-structured:

  • README.md/README.en.md: Added new API_TEST_TIMEOUT_MS environment variable documentation with consistent descriptions
  • .env.example: Added corresponding configuration with clear Chinese comments

The new environment variable API_TEST_TIMEOUT_MS is properly documented with:

  • Default value (15000ms)
  • Valid range (5000-120000ms)
  • Use case description (cross-border network tuning)

However, the pre-existing discount inconsistency (10% vs 20%) should be addressed as part of this PR to maintain documentation consistency.


🤖 Automated docs review by Claude AI

@ding113
Copy link
Owner Author

ding113 commented Nov 25, 2025

📊 PR Size Analysis

This PR is XL with 3,281 lines changed across 31 files (~1,464 lines excluding generated snapshot file).

Large PRs are harder to review and more likely to introduce bugs.

🔀 Suggested Split:

Based on the changes, this PR could be split into:

  1. PR 1: Docker Build Fix - Critical infrastructure fix

    • deploy/Dockerfile
    • src/instrumentation.ts
    • next.config.ts
  2. PR 2: Provider API Test Improvements (fix:修复 Codex API 测试参数兼容性问题与增加支持流式测试 #185, Codex 供应商模型测试失败,供应商后台报错:{"detail":"Input must be a list"} #189)

    • src/app/[locale]/settings/providers/_components/forms/api-test-button.tsx
    • src/app/[locale]/settings/providers/_components/forms/provider-form.tsx
    • src/actions/providers.ts
    • src/lib/constants/provider.constants.ts
    • .env.example, README.md, README.en.md (API_TEST_TIMEOUT_MS)
  3. PR 3: Model Redirection & Proxy Fixes

    • src/app/v1/_lib/proxy/model-redirector.ts
    • src/app/v1/_lib/proxy/provider-selector.ts
    • src/app/v1/_lib/proxy/message-service.ts
    • src/app/v1/_lib/proxy/session.ts
  4. PR 4: Error Rules & Handler Improvements

    • src/actions/error-rules.ts
    • src/lib/error-rule-detector.ts
    • src/repository/error-rules.ts
    • src/app/v1/_lib/proxy/error-handler.ts
    • src/app/v1/_lib/proxy/errors.ts
  5. PR 5: Response Handler & Streaming Timeout

    • src/app/v1/_lib/proxy/response-handler.ts
    • src/app/v1/_lib/codex/chat-completions-handler.ts
    • src/drizzle/schema.ts
    • drizzle/0023_cheerful_shocker.sql (+ snapshot)
  6. PR 6: UI & Logging Improvements (使用记录一栏,建议状态码按照颜色差异较大的显示出来,这样比较好区分出来。 #188)

    • src/app/[locale]/dashboard/logs/_components/error-details-dialog.tsx
    • src/lib/logger.ts
    • src/repository/message.ts
    • messages/en/settings.json, messages/zh-CN/settings.json, messages/zh-TW/settings.json

Why Split?

  • ✅ Easier to review - Each PR focuses on one concern
  • ✅ Faster CI feedback - Smaller changes = quicker builds
  • ✅ Easier to revert if needed - Issues can be isolated
  • ✅ Better git history - Clear commit messages per feature/fix
  • ✅ Parallel review - Multiple reviewers can work simultaneously

Note:

If this is a consolidated release PR and splitting is not practical, consider adding detailed commit messages for each logical change to help reviewers navigate the changes.


🤖 Automated analysis by Claude AI

@ding113
Copy link
Owner Author

ding113 commented Nov 25, 2025

🔒 Security Scan Results

No security vulnerabilities detected

This PR has been scanned against OWASP Top 10, CWE Top 25, and common security anti-patterns. No security issues were identified in the code changes.

Scanned Categories

  • Injection attacks (SQL, NoSQL, Command, LDAP) - All database operations use Drizzle ORM with parameterized queries. No raw SQL string concatenation detected.

  • Authentication and session management - Admin role checks properly enforced in error-rules.ts actions. Session validation occurs before privileged operations.

  • Sensitive data exposure - Sensitive data is properly sanitized in logs using sanitizeErrorTextForLogging() function which redacts API keys, emails, passwords, and tokens.

  • Access control and authorization - Provider group tag matching logic (为供应商设置多个标签的场合,单标签用户无法匹配到供应商 #190 fix) properly implements tag intersection checks. Admin-only actions are protected.

  • Security misconfiguration - The CI=true environment variable in Dockerfile is appropriately used to skip DB connections during build time only.

  • Cross-site scripting (XSS) - React components properly use translation keys and do not use dangerouslySetInnerHTML with user input.

  • Insecure deserialization - JSON parsing in streaming response handling includes proper error handling and DoS protections (MAX_STREAM_CHUNKS, MAX_STREAM_BUFFER_SIZE, MAX_STREAM_ITERATIONS).

  • SSRF and path traversal - Provider URL validation in validateProviderUrlForConnectivity() properly blocks:

    • Internal network addresses (localhost, 127.x.x.x, 10.x.x.x, 172.16-31.x.x, 192.168.x.x, 169.254.x.x)
    • IPv6 local addresses (::1, fe80:, fc00:, fd00:)
    • Dangerous ports (22, 23, 25, 3306, 5432, 6379, 27017, 9200)
  • Cryptographic weaknesses - No new cryptographic operations introduced.

  • ReDoS (Regular Expression Denial of Service) - The safe-regex library is used to validate user-provided regex patterns before compilation in error rule creation.

Security Positive Observations

  1. DoS Protection in Stream Parsing: The new streaming response parsing includes resource limits:

    • MAX_STREAM_CHUNKS: 1000 - Limits number of chunks processed
    • MAX_STREAM_BUFFER_SIZE: 10MB - Limits memory consumption
    • MAX_STREAM_ITERATIONS: 10000 - Prevents infinite loops
  2. Input Validation: API test timeout configuration properly validates range (5000-120000ms).

  3. Error Information Sanitization: The sanitizeErrorTextForLogging() function properly redacts sensitive patterns from error messages before logging.

  4. Configurable Timeout: The new API_TEST_TIMEOUT_MS environment variable allows regional tuning without hardcoding values.

OWASP Top 10 Coverage

  • A01: Injection - Clean (Drizzle ORM parameterized queries)
  • A02: Broken Authentication - Clean (admin checks in place)
  • A03: Sensitive Data Exposure - Clean (data sanitization implemented)
  • A04: XML External Entities - N/A (no XML processing)
  • A05: Broken Access Control - Clean (role-based checks)
  • A06: Security Misconfiguration - Clean
  • A07: XSS - Clean (React escaping)
  • A08: Insecure Deserialization - Clean (bounds checking)
  • A09: Known Vulnerabilities - N/A (no dependency changes)
  • A10: Logging & Monitoring - Clean (structured logging)

🛡️ Security Posture

Assessment: Strong

This PR demonstrates good security practices:

  • Proper input validation and sanitization
  • Defense-in-depth with multiple validation layers
  • Resource limits to prevent DoS attacks
  • Sensitive data redaction in logs
  • Admin authorization checks on sensitive actions

🤖 Automated security scan by Claude AI - OWASP Top 10 & CWE coverage

Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

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

📋 Code Review Summary

This PR introduces several significant features and improvements including error rule detection enhancements, model redirection improvements for Gemini, streaming idle timeout adjustments, and UI/UX improvements for API testing and logs.

🔍 Issues Found

  • Critical (🔴): 0 issues
  • High (🟠): 1 issue
  • Medium (🟡): 2 issues
  • Low (🟢): 1 issue

🎯 Priority Actions

  1. 🟠 Memory Safety in parseStreamResponse (src/actions/providers.ts): The stream parsing function accumulates all chunks without any size limits. A malicious or malfunctioning upstream server could return an extremely large stream, causing memory exhaustion. Add a MAX_CHUNKS constant (e.g., 10000) and break the loop if exceeded.

  2. 🟡 Race Condition in ErrorRuleDetector.reload() (src/lib/error-rule-detector.ts): The isLoading flag is set at the beginning but on early return at line 109, it wasn't being reset (though the diff shows this was fixed). However, the ensureInitialized() method should use proper async locking instead of just checking isLoading - concurrent calls during initialization could lead to multiple loads.

  3. 🟡 Default Timeout Increase (src/lib/constants/provider.constants.ts): Increasing STREAMING_IDLE_TIMEOUT_MS default from 10s to 300s (5 minutes) is a significant change that could hide legitimate issues with stuck streams. Consider if this change aligns with the expected behavior for all use cases.

  4. 🟢 Hardcoded String in Log Message (src/actions/providers.ts): Log messages like "流式响应 chunk 解析失败" use Chinese strings which may cause encoding issues in some log aggregation systems. Consider using English or locale-independent identifiers.

💡 General Observations

  • The refactoring of error detection to use the full response body instead of parsed message is a good improvement for flexibility.
  • The Gemini model redirection URL path handling is well-implemented with proper regex escaping.
  • The i18n improvements for API test button are thorough and well-structured.

🤖 Automated review by Claude AI - focused on identifying issues for improvement

Copy link
Owner Author

@ding113 ding113 left a comment

Choose a reason for hiding this comment

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

📋 Code Review Summary

This PR introduces several significant features and improvements including error rule detection enhancements, model redirection improvements for Gemini, streaming idle timeout adjustments, and UI/UX improvements for API testing and logs.

🔍 Issues Found

  • Critical (🔴): 0 issues
  • High (🟠): 1 issue
  • Medium (🟡): 2 issues
  • Low (🟢): 1 issue

🎯 Priority Actions

  1. 🟠 Memory Safety in parseStreamResponse (src/actions/providers.ts): The stream parsing function accumulates all chunks without any size limits. A malicious or malfunctioning upstream server could return an extremely large stream, causing memory exhaustion. Add a MAX_CHUNKS constant (e.g., 10000) and break the loop if exceeded.

  2. 🟡 Race Condition in ErrorRuleDetector.reload() (src/lib/error-rule-detector.ts): The isLoading flag is set at the beginning but on early return at line 109, it wasn't being reset (though the diff shows this was fixed). However, the ensureInitialized() method should use proper async locking instead of just checking isLoading - concurrent calls during initialization could lead to multiple loads.

  3. 🟡 Default Timeout Increase (src/lib/constants/provider.constants.ts): Increasing STREAMING_IDLE_TIMEOUT_MS default from 10s to 300s (5 minutes) is a significant change that could hide legitimate issues with stuck streams. Consider if this change aligns with the expected behavior for all use cases.

  4. 🟢 Hardcoded String in Log Message (src/actions/providers.ts): Log messages like "流式响应 chunk 解析失败" use Chinese strings which may cause encoding issues in some log aggregation systems. Consider using English or locale-independent identifiers.

💡 General Observations

  • The refactoring of error detection to use the full response body instead of parsed message is a good improvement for flexibility.
  • The Gemini model redirection URL path handling is well-implemented with proper regex escaping.
  • The i18n improvements for API test button are thorough and well-structured.

🤖 Automated review by Claude AI - focused on identifying issues for improvement

@ding113
Copy link
Owner Author

ding113 commented Nov 25, 2025

📍 Detailed Inline Recommendations

1. 🟠 High: Memory Safety in parseStreamResponse

File: src/actions/providers.ts (new parseStreamResponse function)

The stream parsing function accumulates all chunks into an array without any size limits:

const chunks: ProviderApiResponse[] = [];
// ...
while (true) {
  // chunks.push(parsed) - no limit on array size
}

Suggested fix - Add a maximum chunk limit:

const MAX_CHUNKS = 10000; // Reasonable limit for API test

try {
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;

    // Add memory protection
    if (chunks.length >= MAX_CHUNKS) {
      logger.warn("Stream parsing aborted: too many chunks", { 
        maxChunks: MAX_CHUNKS 
      });
      break;
    }
    
    buffer += decoder.decode(value, { stream: true });
    // ... rest of logic
  }
}

2. 🟡 Medium: Potential Race Condition in ensureInitialized()

File: src/lib/error-rule-detector.ts (lines ~72-80)

The ensureInitialized() method checks isInitialized and isLoading flags but multiple concurrent calls could still trigger multiple loads:

private async ensureInitialized(): Promise<void> {
  if (this.isInitialized || this.isLoading) {
    return;
  }
  await this.reload(); // Multiple calls before isLoading is set could reach here
}

Suggested fix - Use a promise-based lock pattern:

private initPromise: Promise<void> | null = null;

private async ensureInitialized(): Promise<void> {
  if (this.isInitialized) return;
  
  if (!this.initPromise) {
    this.initPromise = this.reload().finally(() => {
      this.initPromise = null;
    });
  }
  await this.initPromise;
}

3. 🟡 Medium: Significant Default Timeout Change

File: src/lib/constants/provider.constants.ts

Increasing STREAMING_IDLE_TIMEOUT_MS from 10s to 300s (5 minutes) is a 30x increase:

-  // 流式静默期超时默认 10 秒(防止中途卡住)
-  STREAMING_IDLE_TIMEOUT_MS: 10000,
+  // 流式静默期超时默认 300 秒(5 分钟,防止中途卡住)
+  STREAMING_IDLE_TIMEOUT_MS: 300000,

Recommendation: Consider documenting the rationale for this change in commit message or PR description. A 5-minute timeout may hide legitimate stuck stream issues in production.


4. 🟢 Low: Chinese Log Messages

File: src/actions/providers.ts (multiple locations in parseStreamResponse)

Several log messages use Chinese strings which may cause encoding issues:

  • "流式响应 chunk 解析失败"
  • "响应体为空"
  • "未能从流式响应中解析出有效数据"

Suggested fix: Use English or locale-independent identifiers for log messages to ensure compatibility with all log aggregation systems.


🤖 Automated review by Claude AI

@ding113 ding113 closed this Nov 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation enhancement New feature or request size/XL Extra Large PR (> 1000 lines)

Projects

None yet

2 participants

Comments