Skip to content

fix: add export exclusion toggle for new providers#420

Merged
ymkiux merged 2 commits intomainfrom
fix/issue-419
Mar 17, 2026
Merged

fix: add export exclusion toggle for new providers#420
ymkiux merged 2 commits intomainfrom
fix/issue-419

Conversation

@awsl233777
Copy link
Collaborator

@awsl233777 awsl233777 commented Mar 17, 2026

Summary

  • add a persisted excludeFromExport flag for providers
  • show the export/backup exclusion toggle only during new provider creation, defaulting to off
  • skip flagged providers in provider export and system backup flows

Testing

Summary by CodeRabbit

  • 新功能
    • 为提供商添加了"从导出和备份中排除"选项。启用此选项后,所选提供商将不会被包含在导出文件或备份包中,保持仅在本地系统中。

@coderabbitai
Copy link

coderabbitai bot commented Mar 17, 2026

📝 Walkthrough

演练

此 PR 为提供商添加了"导出屏蔽"功能。新增 ExcludeFromExport 字段,当其为 true 时,该提供商配置会在导出和备份操作中被过滤掉。修改涉及数据模型层、数据库迁移、服务层过滤逻辑、Web UI 组件和国际化资源。

变更

Cohort / File(s) Summary
数据模型与类型定义
internal/domain/model.go, internal/repository/sqlite/models.go, web/src/lib/transport/types.ts, web/src/pages/providers/types.ts
在 Provider 结构体中添加 ExcludeFromExport 字段;Web 端 ProviderFormData 接口扩展了该字段。
数据库迁移
internal/repository/sqlite/migrations.go
新增 Version 7 迁移,将 providers.exclude_from_export 列默认值设为 0。
数据层映射
internal/repository/sqlite/provider.go
在 SQLite Provider 映射中新增 ExcludeFromExport 字段的转换逻辑,支持 domain 与 sqlite 模型之间的双向转换。
服务层导出过滤
internal/service/admin.go, internal/service/backup.go
ExportProviders 和备份导出流程中新增过滤逻辑,跳过标记为 ExcludeFromExport=true 的提供商。
测试更新
internal/handler/admin_import_export_test.go, tests/e2e/backup_test.go
更新现有测试期望;新增 E2E 测试 TestBackupExport_ExcludedProviderIsOmitted 验证排除功能。
前端 UI 与本地化
web/src/pages/providers/components/custom-config-step.tsx, web/src/pages/providers/components/provider-edit-flow.tsx, web/src/pages/providers/context/provider-form-context.tsx, web/src/locales/en.json, web/src/locales/zh.json
新增导出屏蔽开关 UI 组件;初始化 ProviderFormData 表单状态;在数据流中传递 excludeFromExport 字段;补充英文与中文本地化资源。

序列图

sequenceDiagram
    participant Web as Web UI
    participant Handler as Handler
    participant Service as Admin/Backup Service
    participant DB as Database
    
    Web->>Handler: POST /providers (excludeFromExport: true)
    Handler->>Service: CreateProvider(data)
    Service->>DB: Store provider with excludeFromExport=1
    DB-->>Service: Success
    
    Web->>Handler: GET /export
    Handler->>Service: ExportProviders()
    Service->>DB: Fetch all providers
    DB-->>Service: Provider list
    Service->>Service: Filter: skip where excludeFromExport=true
    Service-->>Handler: Filtered provider list
    Handler-->>Web: Exported data (without excluded providers)
Loading

预计代码审查工作量

🎯 3 (Moderate) | ⏱️ ~20 minutes

可能相关的 PR

  • PR #192:同时修改了 Provider 导入/导出路径和提供商模型结构(domain/sqlite Provider 和管理员导入/导出测试),存在关联。
  • PR #129:此 PR 直接修改了在 PR #129 中实现的 BackupService 和相关导出处理器的备份/导出流程,存在关联。

建议的审查者

  • whhjdi

🐰 新增屏蔽开关妙,
敏感配置不外泄,
过滤导出保安全,
备份备份更无忧,
临时测试不误事呢~

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题清晰准确地总结了主要变更,即为新提供商添加导出排除切换开关。
Linked Issues check ✅ Passed PR 的所有代码变更完全满足 issue #419 的要求:添加了持久化的导出排除标志、UI 切换仅在新建提供商时显示、导出和备份流程会跳过被标记的提供商。
Out of Scope Changes check ✅ Passed 所有变更都严格限定在实现导出排除功能范围内,包括数据模型、迁移、UI 组件、本地化和服务层逻辑,无超出范围的改动。

✏️ 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
  • Commit unit tests in branch fix/issue-419
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

Migrating from UI to YAML configuration.

Use the @coderabbitai configuration command in a PR comment to get a dump of all your UI settings in YAML format. You can then edit this YAML file and upload it to the root of your repository to configure CodeRabbit programmatically.

@awsl233777
Copy link
Collaborator Author

Ran Playwright E2E for the documentation page flow and captured screenshots.

Test run:

  • pnpm -C tests/e2e/playwright test:documentation-page

Screenshots:

Notes:

  • This spec uses mocked admin APIs, so it is a focused UI/E2E verification for the documentation flow rather than a full backend-integrated scenario.
  • The run passed locally on branch fix/issue-419.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/src/pages/providers/components/claude-token-import.tsx (1)

254-303: ⚠️ Potential issue | 🟡 Minor

请在 Token 创建路径也显式传递 excludeFromExport

Line 257 已在 OAuth 路径设置该字段,但 handleCreateproviderData(Line 291 起)仍未包含。两条创建路径行为不一致,建议统一显式赋值,避免后续后端默认值或校验策略变化带来偏差。

建议修改
       const providerData: CreateProviderData = {
         type: 'claude',
         name: finalEmail || 'Claude Account',
+        excludeFromExport: false,
         config: {
           claude: {
             email: finalEmail,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/providers/components/claude-token-import.tsx` around lines 254
- 303, The token-based creation path (handleCreate) builds a CreateProviderData
object named providerData but omits excludeFromExport, causing inconsistent
behavior vs the OAuth path; update the providerData in handleCreate to include
excludeFromExport: false (same as the OAuth branch) so both creation flows
explicitly set excludeFromExport and remain consistent.
🧹 Nitpick comments (2)
web/src/pages/providers/components/codex-token-import.tsx (1)

239-239: 建议补齐 Token 创建路径的 excludeFromExport 显式赋值

OAuth 路径已显式传 false,但 token 路径仍依赖后端默认值。建议两条创建路径都显式赋值,避免后续默认值变化导致行为漂移。

♻️ 建议修改
 const providerData: CreateProviderData = {
   type: 'codex',
   name: finalEmail || 'Codex Account',
+  excludeFromExport: false,
   config: {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/providers/components/codex-token-import.tsx` at line 239, The
token creation branch currently relies on backend defaults for excludeFromExport
while the OAuth branch explicitly sets excludeFromExport: false; update the
token creation path in the Codex token import logic to also set
excludeFromExport: false so both creation flows are explicit and consistent
(locate the token creation object where excludeFromExport is absent and add
excludeFromExport: false to it, matching the OAuth creation object).
web/src/pages/providers/components/codex-provider-view.tsx (1)

379-470: 建议提取统一的更新 payload 构造,减少四处重复。

这四个 handler 都在重复拼装 ...provider + excludeFromExport + config。可抽一个小 helper,降低后续字段扩展时的漏改风险。

可选重构示例
+  const buildProviderUpdateData = (codexPatch: Partial<NonNullable<typeof config>>) => ({
+    ...provider,
+    excludeFromExport: !!provider.excludeFromExport,
+    config: {
+      ...provider.config,
+      codex: {
+        ...config,
+        ...codexPatch,
+      },
+    },
+  });

   const handleToggleCLIProxyAPI = async (checked: boolean) => {
     if (!config) return;
@@
       await updateProvider.mutateAsync({
         id: provider.id,
-        data: {
-          ...provider,
-          excludeFromExport: !!provider.excludeFromExport,
-          config: {
-            ...provider.config,
-            codex: {
-              ...config,
-              useCLIProxyAPI: checked,
-            },
-          },
-        },
+        data: buildProviderUpdateData({ useCLIProxyAPI: checked }),
       });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/providers/components/codex-provider-view.tsx` around lines 379
- 470, Extract a small helper (e.g., buildProviderUpdatePayload) to centralize
the repeated payload construction used by handleToggleCLIProxyAPI,
handleToggleDisableErrorCooldown, handleChangeReasoning, and
handleChangeServiceTier: the helper should accept the provider, updated codex
partial (or other top-level config overrides) and return the object passed to
updateProvider.mutateAsync (including ...provider, excludeFromExport:
!!provider.excludeFromExport, config: { ...provider.config, ...overrides, codex:
{ ...config, ...codexOverrides } }). Replace the inline payload assembly in each
handler with a call to this helper and pass the appropriate codex override
(useCLIProxyAPI, disableErrorCooldown, reasoning, serviceTier) to eliminate
duplication and risk of missed fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/service/backup.go`:
- Around line 122-123: The exported backup includes ExcludeFromExport but
importProviders does not populate it back into domain.Provider; update the
importProviders function to set the ExcludeFromExport field on the constructed
domain.Provider (e.g., assign provider.ExcludeFromExport =
backupProvider.ExcludeFromExport or p.ExcludeFromExport where p/backupProvider
is the source) so the flag is preserved during import.
- Around line 111-113: 当前逻辑仅在遍历 provider 时用 p.ExcludeFromExport 跳过 Provider,但导出
routes 与 model mappings 时并未同步过滤,导致导出的 route/modelMapping 可能引用已被排除的 Provider。修改导出
routes 和 model mappings 的逻辑(例如在导出函数或变量处理处:exportRoutes、exportModelMappings 或遍历
routes/modelMappings 的代码)增加与 p.ExcludeFromExport 等价的过滤条件:构建一组可导出的 provider
IDs(基于 p.ExcludeFromExport)并在导出每个 route 或 modelMapping 前检查其 providerId/related
provider 是否在该集合内,若不在则跳过导出,从而避免悬空引用。

In `@web/src/pages/providers/components/antigravity-token-import.tsx`:
- Around line 174-177: Two provider creation paths construct a
CreateProviderData object inconsistently: the token flow sets excludeFromExport:
false on providerData but the OAuth/new-account flow (the providerData created
around the OAuth flow starting at the second providerData) omits it; update the
OAuth/new-account providerData to explicitly include excludeFromExport with the
same value (false) so both creation paths pass the same field and avoid relying
on defaults.

---

Outside diff comments:
In `@web/src/pages/providers/components/claude-token-import.tsx`:
- Around line 254-303: The token-based creation path (handleCreate) builds a
CreateProviderData object named providerData but omits excludeFromExport,
causing inconsistent behavior vs the OAuth path; update the providerData in
handleCreate to include excludeFromExport: false (same as the OAuth branch) so
both creation flows explicitly set excludeFromExport and remain consistent.

---

Nitpick comments:
In `@web/src/pages/providers/components/codex-provider-view.tsx`:
- Around line 379-470: Extract a small helper (e.g., buildProviderUpdatePayload)
to centralize the repeated payload construction used by handleToggleCLIProxyAPI,
handleToggleDisableErrorCooldown, handleChangeReasoning, and
handleChangeServiceTier: the helper should accept the provider, updated codex
partial (or other top-level config overrides) and return the object passed to
updateProvider.mutateAsync (including ...provider, excludeFromExport:
!!provider.excludeFromExport, config: { ...provider.config, ...overrides, codex:
{ ...config, ...codexOverrides } }). Replace the inline payload assembly in each
handler with a call to this helper and pass the appropriate codex override
(useCLIProxyAPI, disableErrorCooldown, reasoning, serviceTier) to eliminate
duplication and risk of missed fields.

In `@web/src/pages/providers/components/codex-token-import.tsx`:
- Line 239: The token creation branch currently relies on backend defaults for
excludeFromExport while the OAuth branch explicitly sets excludeFromExport:
false; update the token creation path in the Codex token import logic to also
set excludeFromExport: false so both creation flows are explicit and consistent
(locate the token creation object where excludeFromExport is absent and add
excludeFromExport: false to it, matching the OAuth creation object).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e4216f35-10c2-42b0-a8e5-1b2c6e2e0568

📥 Commits

Reviewing files that changed from the base of the PR and between c134b8e and 090cbd3.

📒 Files selected for processing (24)
  • internal/domain/backup.go
  • internal/domain/model.go
  • internal/handler/admin_import_export_test.go
  • internal/repository/sqlite/migrations.go
  • internal/repository/sqlite/models.go
  • internal/repository/sqlite/provider.go
  • internal/service/admin.go
  • internal/service/backup.go
  • tests/e2e/backup_test.go
  • web/src/lib/transport/types.ts
  • web/src/locales/en.json
  • web/src/locales/zh.json
  • web/src/pages/providers/components/antigravity-provider-view.tsx
  • web/src/pages/providers/components/antigravity-token-import.tsx
  • web/src/pages/providers/components/claude-provider-view.tsx
  • web/src/pages/providers/components/claude-token-import.tsx
  • web/src/pages/providers/components/codex-provider-view.tsx
  • web/src/pages/providers/components/codex-token-import.tsx
  • web/src/pages/providers/components/custom-config-step.tsx
  • web/src/pages/providers/components/kiro-provider-view.tsx
  • web/src/pages/providers/components/kiro-token-import.tsx
  • web/src/pages/providers/components/provider-edit-flow.tsx
  • web/src/pages/providers/context/provider-form-context.tsx
  • web/src/pages/providers/types.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: e2e
  • GitHub Check: playwright
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-02-06T09:39:16.667Z
Learnt from: Bowl42
Repo: awsl-project/maxx PR: 187
File: internal/adapter/provider/codex/adapter.go:305-331
Timestamp: 2026-02-06T09:39:16.667Z
Learning: 在 Codex provider 的 `internal/adapter/provider/codex/adapter.go` 中,从 ID Token 提取的 PII 字段(Email, Name, Picture, AccountID, UserID 等)持久化到 provider config 是产品需求,用于前端展示账户信息。这是经过确认的设计决策,并计划在隐私文档中补充数据保留策略。

Applied to files:

  • web/src/pages/providers/components/codex-token-import.tsx
🔇 Additional comments (21)
web/src/lib/transport/types.ts (1)

103-103: 新增字段定义与后端序列化语义一致。

Line 103 使用可选布尔字段可兼容未返回该字段的历史数据,方向正确。

internal/repository/sqlite/migrations.go (2)

122-122: 该处为等价改动,无需额外处理。


211-218: v7 回填迁移实现正确且可重复执行。

WHERE exclude_from_export IS NULL 仅修复历史脏数据,不会影响已规范化记录。

web/src/locales/zh.json (1)

1221-1223: 新增中文文案语义清晰,与功能一致。

web/src/locales/en.json (1)

1221-1223: 新增英文文案准确,键值对齐良好。

internal/domain/backup.go (1)

43-43: 备份领域模型字段扩展定义正确。

tests/e2e/backup_test.go (1)

205-235: 新增 e2e 用例覆盖了“排除导出/备份”核心路径。

internal/service/admin.go (1)

169-176: Provider 导出过滤逻辑实现简洁且符合需求。

web/src/pages/providers/types.ts (1)

237-237: 表单类型已覆盖导出排除字段,方向正确

该字段补充到 ProviderFormData 后,前端创建/编辑流程的数据模型更完整。

web/src/pages/providers/components/kiro-provider-view.tsx (1)

290-291: 保留 excludeFromExport 状态的更新方式合理

在切换 disableErrorCooldown 时携带该字段,可避免无关更新覆盖导出排除配置。

web/src/pages/providers/components/antigravity-provider-view.tsx (1)

308-309: 两个更新入口都已正确透传 excludeFromExport

在切换 CLIProxyAPI 和错误冷却开关时都保留该字段,避免配置更新时误改导出策略。

Also applies to: 333-334

internal/domain/model.go (1)

242-243: 领域模型字段新增正确且必要

Provider 增加 ExcludeFromExport 能直接支撑导出/备份过滤逻辑。

internal/repository/sqlite/models.go (1)

98-98: 数据库模型默认值设置合理

default:0 与“新建默认不排除导出”语义一致,落库行为明确。

web/src/pages/providers/context/provider-form-context.tsx (1)

44-44: 表单初始值设置符合需求

默认 false 与“新建时默认不排除导出/备份”保持一致。

internal/handler/admin_import_export_test.go (1)

126-130: 测试断言已准确覆盖导出排除行为

用例将 ExcludeFromExport=true 与“导出结果为空”关联起来,能有效防止回归。

Also applies to: 155-157

web/src/pages/providers/components/kiro-token-import.tsx (1)

76-80: 该变更方向正确。

在创建 payload 中显式设置 excludeFromExport: false,与默认行为一致,也提升了前后端契约清晰度。

web/src/pages/providers/components/claude-provider-view.tsx (1)

237-247: 这里的字段透传处理是必要的。

在更新 disableErrorCooldown 时同步携带 excludeFromExport,能避免编辑其它配置时意外重置导出排除状态。

web/src/pages/providers/components/provider-edit-flow.tsx (1)

363-389: 更新与克隆路径都补齐了 excludeFromExport,实现完整。

这能确保在保存和克隆时该标志位被稳定携带,不会因为部分路径遗漏而出现状态漂移。

Also applies to: 424-451

internal/repository/sqlite/provider.go (1)

73-91: 映射实现完整且一致。

toModeltoDomain 都已覆盖 ExcludeFromExport,可正确完成布尔值在领域模型与 SQLite 存储之间的往返转换。

Also applies to: 95-109

web/src/pages/providers/components/custom-config-step.tsx (2)

58-84: 创建请求已正确携带排除导出标志。

excludeFromExport 从表单状态进入 CreateProviderData,与后续导出/备份筛选逻辑能够闭环。


228-257: 新建页上的开关实现符合预期。

开关状态与 formData.excludeFromExport 双向绑定,文案与行为清晰,满足“仅在创建阶段配置”的产品意图。

Comment on lines +111 to +113
if p.ExcludeFromExport {
continue
}
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

排除 Provider 后会导出悬空的 Route/ModelMapping 引用。

Line 111-113 过滤了 provider,但后续仍导出全量 routes 与 model mappings;这些记录可能引用已被过滤的 provider,导入时会变成无效/被跳过。建议在导出 route 与 mapping 时同步按可导出 provider 过滤。

🛠 建议修复
@@
 	// 6. Export Routes
 	routes, err := s.routeRepo.List(tenantID)
@@
 	for _, r := range routes {
+		providerName, ok := providerIDToName[r.ProviderID]
+		if !ok {
+			// provider 被排除导出时,跳过其关联路由,避免悬空引用
+			continue
+		}
 		backup.Data.Routes = append(backup.Data.Routes, domain.BackupRoute{
@@
-			ProviderName:    providerIDToName[r.ProviderID],
+			ProviderName:    providerName,
 			Position:        r.Position,
 			RetryConfigName: retryConfigIDToName[r.RetryConfigID],
 		})
 	}
@@
-	for _, m := range mappings {
+mappingLoop:
+	for _, m := range mappings {
 		bm := domain.BackupModelMapping{
@@
 		if m.ProviderID != 0 {
-			bm.ProviderName = providerIDToName[m.ProviderID]
+			name, ok := providerIDToName[m.ProviderID]
+			if !ok {
+				continue mappingLoop
+			}
+			bm.ProviderName = name
 		}
@@
 		if m.RouteID != 0 {
 			// Find the route to get its composite key
 			for _, r := range routes {
 				if r.ID == m.RouteID {
+					providerName, ok := providerIDToName[r.ProviderID]
+					if !ok {
+						continue mappingLoop
+					}
 					bm.RouteName = fmt.Sprintf("%s:%s:%s",
-						providerIDToName[r.ProviderID],
+						providerName,
 						r.ClientType,
 						projectIDToSlug[r.ProjectID])
 					break
 				}
 			}
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/service/backup.go` around lines 111 - 113, 当前逻辑仅在遍历 provider 时用
p.ExcludeFromExport 跳过 Provider,但导出 routes 与 model mappings 时并未同步过滤,导致导出的
route/modelMapping 可能引用已被排除的 Provider。修改导出 routes 和 model mappings
的逻辑(例如在导出函数或变量处理处:exportRoutes、exportModelMappings 或遍历 routes/modelMappings
的代码)增加与 p.ExcludeFromExport 等价的过滤条件:构建一组可导出的 provider IDs(基于
p.ExcludeFromExport)并在导出每个 route 或 modelMapping 前检查其 providerId/related provider
是否在该集合内,若不在则跳过导出,从而避免悬空引用。

Comment on lines 122 to 123
ExcludeFromExport: p.ExcludeFromExport,
})
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

导出包含了 ExcludeFromExport,但导入未回填该字段。

Line 122-123 已把字段写入备份对象;但 importProviders 构造 domain.Provider 时未设置 ExcludeFromExport,会导致该字段在导入时丢失。

🛠 建议修复
 		p := &domain.Provider{
 			TenantID:             tenantID,
 			Name:                 bp.Name,
 			Type:                 bp.Type,
 			Logo:                 bp.Logo,
 			Config:               bp.Config,
 			SupportedClientTypes: bp.SupportedClientTypes,
 			SupportModels:        bp.SupportModels,
+			ExcludeFromExport:    bp.ExcludeFromExport,
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/service/backup.go` around lines 122 - 123, The exported backup
includes ExcludeFromExport but importProviders does not populate it back into
domain.Provider; update the importProviders function to set the
ExcludeFromExport field on the constructed domain.Provider (e.g., assign
provider.ExcludeFromExport = backupProvider.ExcludeFromExport or
p.ExcludeFromExport where p/backupProvider is the source) so the flag is
preserved during import.

Comment on lines +174 to +177
const providerData: CreateProviderData = {
type: 'antigravity',
name: finalEmail || 'Antigravity Account',
excludeFromExport: false,
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

excludeFromExport 在新建流程中处理不一致

Line 177 在 token 流里显式设置了 excludeFromExport: false,但 OAuth 新建流(Line 214 起的 providerData)未传该字段。建议两条创建路径统一显式传递同一值,避免后续默认值变化导致导出/备份行为不一致。

🔧 建议修复(统一两条创建路径)
+      const defaultExcludeFromExport = false;
+
       const providerData: CreateProviderData = {
         type: 'antigravity',
         name: finalEmail || 'Antigravity Account',
-        excludeFromExport: false,
+        excludeFromExport: defaultExcludeFromExport,
         config: {
           antigravity: {
             email: finalEmail,
@@
       const providerData: CreateProviderData = {
         type: 'antigravity',
         name: oauthResult.email || 'Antigravity Account',
+        excludeFromExport: defaultExcludeFromExport,
         config: {
           antigravity: {
             email: oauthResult.email || '',
📝 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
const providerData: CreateProviderData = {
type: 'antigravity',
name: finalEmail || 'Antigravity Account',
excludeFromExport: false,
const defaultExcludeFromExport = false;
const providerData: CreateProviderData = {
type: 'antigravity',
name: finalEmail || 'Antigravity Account',
excludeFromExport: defaultExcludeFromExport,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/providers/components/antigravity-token-import.tsx` around lines
174 - 177, Two provider creation paths construct a CreateProviderData object
inconsistently: the token flow sets excludeFromExport: false on providerData but
the OAuth/new-account flow (the providerData created around the OAuth flow
starting at the second providerData) omits it; update the OAuth/new-account
providerData to explicitly include excludeFromExport with the same value (false)
so both creation paths pass the same field and avoid relying on defaults.

@awsl233777
Copy link
Collaborator Author

Updating the screenshots comment with inline Markdown rendering.

Playwright E2E screenshots

Quickstart (Codex)
Quickstart Codex

Quickstart (Gemini)
Quickstart Gemini

Diagnostics
Diagnostics

Test run:

  • pnpm -C tests/e2e/playwright test:documentation-page

Notes:

  • This spec uses mocked admin APIs, so it verifies the documentation UI flow rather than a full backend-integrated scenario.
  • The run passed locally on branch fix/issue-419.

@awsl233777
Copy link
Collaborator Author

已按 #419 的范围收窄本次改动,去掉了与需求无关或冗余的 excludeFromExport 透传,当前保留的内容主要是:

  • 新增提供商页面增加开关(默认关闭)
  • 开关状态持久化
  • providers export 过滤已开启该开关的 provider
  • backup export 过滤已开启该开关的 provider
  • 对应导出/备份测试

测试结论

  • go test ./internal/service ./internal/handler ./tests/e2e -run 'TestBackupExport|TestAdminHandler_ProvidersExport_WithTrailingSlash' ✅\n- pnpm -C web exec tsc --noEmit ✅\n\n如果还希望继续把范围再压缩,可以继续看 provider-edit-flow.tsx 这部分是否要保留为“仅透传现有值,避免编辑时把持久化状态冲掉”。

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.

♻️ Duplicate comments (1)
internal/service/backup.go (1)

111-113: ⚠️ Potential issue | 🟠 Major

需要同步过滤 Route / ModelMapping,当前会导出悬空引用

Line 111-113 只过滤了 Provider;但 Line 174-184 与 Line 210-243 仍会导出关联数据。由于 providerIDToName 中不存在被排除 Provider 的键,导出内容会出现空 ProviderName,导入时会被跳过或错误降级(例如映射丢失 provider 关联)。请在导出 Route 和 ModelMapping 时按“可导出 Provider 集合”同步过滤。

🛠 建议修复(示例)
@@
 	// 6. Export Routes
 	routes, err := s.routeRepo.List(tenantID)
@@
 	for _, r := range routes {
+		providerName, ok := providerIDToName[r.ProviderID]
+		if !ok || providerName == "" {
+			// provider 被排除导出时,跳过其关联 route,避免悬空引用
+			continue
+		}
 		backup.Data.Routes = append(backup.Data.Routes, domain.BackupRoute{
@@
-			ProviderName:    providerIDToName[r.ProviderID],
+			ProviderName:    providerName,
 			Position:        r.Position,
 			RetryConfigName: retryConfigIDToName[r.RetryConfigID],
 		})
 	}
@@
-	for _, m := range mappings {
+mappingLoop:
+	for _, m := range mappings {
 		bm := domain.BackupModelMapping{
@@
 		if m.ProviderID != 0 {
-			bm.ProviderName = providerIDToName[m.ProviderID]
+			name, ok := providerIDToName[m.ProviderID]
+			if !ok || name == "" {
+				continue
+			}
+			bm.ProviderName = name
 		}
@@
 		if m.RouteID != 0 {
 			// Find the route to get its composite key
 			for _, r := range routes {
 				if r.ID == m.RouteID {
+					providerName, ok := providerIDToName[r.ProviderID]
+					if !ok || providerName == "" {
+						continue mappingLoop
+					}
 					bm.RouteName = fmt.Sprintf("%s:%s:%s",
-						providerIDToName[r.ProviderID],
+						providerName,
 						r.ClientType,
 						projectIDToSlug[r.ProjectID])
 					break
 				}
 			}
+			if bm.RouteName == "" {
+				continue
+			}
 		}
 		backup.Data.ModelMappings = append(backup.Data.ModelMappings, bm)
 	}

Also applies to: 174-184, 210-243

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/service/backup.go` around lines 111 - 113, The export currently
skips Providers with p.ExcludeFromExport but still exports Route and
ModelMapping entries that reference excluded Providers, causing empty
ProviderName in providerIDToName and broken imports; when building
providerIDToName (and the set of exportable providers) only include Providers
where p.ExcludeFromExport is false, then when iterating Routes and ModelMappings
(the loops around the Route export and ModelMapping export) skip any entry whose
ProviderID is not in that exportable-provider set (i.e., check providerIDToName
membership or a dedicated allowedProviderIDs set) so Routes and ModelMappings
referencing excluded Providers are not exported.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@internal/service/backup.go`:
- Around line 111-113: The export currently skips Providers with
p.ExcludeFromExport but still exports Route and ModelMapping entries that
reference excluded Providers, causing empty ProviderName in providerIDToName and
broken imports; when building providerIDToName (and the set of exportable
providers) only include Providers where p.ExcludeFromExport is false, then when
iterating Routes and ModelMappings (the loops around the Route export and
ModelMapping export) skip any entry whose ProviderID is not in that
exportable-provider set (i.e., check providerIDToName membership or a dedicated
allowedProviderIDs set) so Routes and ModelMappings referencing excluded
Providers are not exported.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8960d017-1b66-4b1c-8007-f07465fd0a13

📥 Commits

Reviewing files that changed from the base of the PR and between 090cbd3 and ee663a6.

📒 Files selected for processing (2)
  • internal/repository/sqlite/migrations.go
  • internal/service/backup.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/repository/sqlite/migrations.go
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: playwright
  • GitHub Check: e2e

@ymkiux
Copy link
Contributor

ymkiux commented Mar 17, 2026

@coderabbitai If the current suggestion is not adopted, will it lead to disruptive changes?

@coderabbitai
Copy link

coderabbitai bot commented Mar 17, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

Let me pull the review comments on this PR to identify the specific suggestion being referenced.


🏁 Script executed:

#!/bin/bash
gh pr view 420 --repo awsl-project/maxx --json reviews,comments
gh api repos/awsl-project/maxx/pulls/420/comments --jq '.[] | {id: .id, path: .path, line: .line, body: .body}'
gh api repos/awsl-project/maxx/pulls/420/reviews --jq '.[] | {id: .id, user: .user.login, state: .state, body: .body}'

Length of output: 75676


@ymkiux: Based on the review comments posted, here's the breakdown by severity:


🟠 Disruptive — Should be fixed before merge

internal/service/backup.go — dangling Route/ModelMapping references (the comment flagged at lines 111-113, also applying to 174-184 and 210-243):

This one will cause data loss if not fixed. Here's why:

  • When a backup is exported, excluded providers are correctly skipped — so they never appear in providerIDToName.
  • However, the Routes and ModelMappings that reference those excluded providers are still exported, now with an empty ProviderName (map key miss).
  • On import, those routes/mappings cannot resolve their provider, so they are silently skipped or degraded.
  • The net result: a backup/restore cycle drops routes and model mappings that were attached to excluded providers, with no error or warning. This is a real data integrity issue.

🟡 Correctness issue (not immediately disruptive, but causes silent regression)

internal/service/backup.goimportProviders does not restore ExcludeFromExport:

After a restore, any provider that was previously excluded from export will have its ExcludeFromExport flag silently reset to false. On the next backup cycle, it will be included again — defeating the purpose of the feature.


🟡 Consistency issue (not runtime disruptive)

antigravity-token-import.tsx / claude-token-import.tsx / codex-token-import.tsx — OAuth paths omit excludeFromExport while token paths set it explicitly:

Not immediately disruptive because the backend defaults to 0 / false. However, if that default ever changes, these paths will silently diverge in behavior.


🧹 Nitpick (no runtime impact)

codex-provider-view.tsx — refactoring suggestion for deduplicating payload construction. No functional impact.


Summary: The backup dangling-reference issue is the one to prioritize — skipping it will produce silently broken backup/restore behavior for any provider with associated routes or model mappings.

@ymkiux ymkiux merged commit beb041c into main Mar 17, 2026
4 checks passed
@ymkiux ymkiux deleted the fix/issue-419 branch March 17, 2026 13:21
@awsl233777
Copy link
Collaborator Author

已继续按评论建议补修备份导出逻辑:

  • 当 provider 开启 excludeFromExport 时,关联 route 也会一并跳过导出
  • 关联 model mapping 也会跳过导出,避免备份里残留悬空引用
  • 额外补了单测,覆盖“保留可导出 provider 的 route / mapping,同时过滤掉被排除 provider 的关联引用”

测试结论

  • go test ./internal/service ./internal/handler ./tests/e2e -run 'TestBackupService_Export_SkipsReferencesToExcludedProviders|TestBackupExport|TestAdminHandler_ProvidersExport_WithTrailingSlash' ✅\n\n前一轮评论里关于 ExcludeFromExport 写入 backup 但导入未回填、以及 antigravity 两条创建路径不一致的点,我这边也重新核过当前分支:前者在当前实现里已经不再成立,后者当前两条路径都未显式透传该字段,行为一致。

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.

2 participants