fix(contacts): restrict IM platform entry to non-enterprise editions

This commit is contained in:
JzoNg
2026-07-17 13:50:25 +08:00
parent 5b5d62a2af
commit e35752ddbc
8 changed files with 31 additions and 32 deletions
@@ -5,14 +5,14 @@
已有 `hitl-im-contact-domain-discovery` change 已确认以下领域约束,本 change 直接复用而不重新定义:
- 同一 Organization 一期只启用一个 IM platform。
- EE 由企业管理员管理,CE / SaaS 由 workspace owner 或 workspace admin 管理。
- 该 workspace 入口只面向非企业版;CE / SaaS 由 workspace owner 或 workspace admin 管理enterprise plan 不展示本入口
- 连接状态包含 `Not configured``Configured``Connected``Permission issue``Callback error``Connection error`
- 通讯录同步由管理员手动触发,不做自动同步。
- 同步优先按 platform user ID 匹配已有 binding,再按 Email 匹配 Contact;未命中对象进入 unmatched,不能自动创建 External contact。
本 change 的实现范围严格限定为前端。后端 contract 尚未就绪,因此页面、查询、mutation、权限、provider availability、同步任务和同步详情均由集中、类型安全、确定性的 mock repository 驱动。后续真实 API 接入必须通过独立 change 完成。
当前 web 中的 `web/features/agent-v2/roster/` 是 AI Agent 资产管理,不能作为 Contacts UI 的实现位置。Contacts feature 需要拥有自己的组件、数据访问抽象和路由边界,并由 EE 与 CE / SaaS 的不同管理 shell 挂载。
当前 web 中的 `web/features/agent-v2/roster/` 是 AI Agent 资产管理,不能作为 Contacts UI 的实现位置。Contacts feature 需要拥有自己的组件、数据访问抽象和路由边界,并由非企业版 CE / SaaS workspace 设置页挂载。
设计验收来源:
@@ -26,7 +26,7 @@
**Goals:**
- 在 Contacts 管理区域提供从未绑定、配置、mock 授权、连接异常到已连接的完整 IM platform 管理体验。
- 通过同一套 Contacts feature UI 适配 EE 企业管理面和 CE / SaaS workspace 管理面
- 在非 enterprise plan 的 CE / SaaS workspace 设置页提供 Contacts feature UI,并在 enterprise plan 隐藏入口
- 支持手动启动 mock 通讯录同步、恢复 mock 进行中任务、查看最新摘要和指定 sync run 详情。
- 清晰表达 matched、created binding、updated binding、unmatched、skipped、failed 等结果,帮助管理员理解联系人映射。
- 将数据访问集中在可替换的 typed repository,避免页面组件直接依赖 fixture shape。
@@ -46,7 +46,7 @@
### 1. Contacts feature 独立拥有 UI,并由不同管理 shell 挂载
实现建立 Contacts-owned feature 模块,包含 binding summary、provider setup、connection diagnostics、sync trigger 和 sync details。EE 企业管理面与 CE / SaaS workspace 管理面只负责提供 Organization context、mock permission context 和返回路径。
实现建立 Contacts-owned feature 模块,包含 binding summary、provider setup、connection diagnostics、sync trigger 和 sync details。CE / SaaS workspace 设置页负责使用现有 plan 判断、提供 Organization context、mock permission context 和返回路径enterprise plan 不挂载该入口
AI Agent Roster 与 Human Contacts 的数据、权限和生命周期完全不同,因此不复用 `web/features/agent-v2/roster/`
@@ -170,7 +170,7 @@ repository 暴露读取 integration/provider、保存配置、mock 授权、测
- [Mock shape 与未来 API 漂移] → 以 UI 所需 view model 为稳定边界;真实 adapter 负责映射后端 DTO,而不是让组件依赖后端 shape。
- [Mock 交互被误认为真实功能] → 入口使用 feature gate,并在交付说明中明确没有后端持久化、真实授权和真实同步。
- [Figma 中 provider-specific 差异可能继续调整] → 保留共享 shell 与 provider adapter 边界,将视觉差异限制在 adapter 内。
- [EE 与 CE / SaaS 入口不同导致重复实现] → shell 只注入 contextfeature 内容保持单一实现
- [企业版误展示 mock 入口] → workspace shell 复用现有 `Plan.enterprise` 判断,深链同样回退到允许访问的 tab
- [不可控 polling 造成测试不稳定] → mock 状态迁移使用 fake timers 或显式推进,不使用随机延迟。
- [大型详情 fixture 降低测试性能] → 用小型分页 fixture 验证增量加载,不生成大规模浏览器内数据。
- [Secret 泄露到 fixture 或快照] → fixture 只保存配置标记,repository 丢弃输入文本,测试断言日志与 DOM 中无 secret。
@@ -28,7 +28,7 @@ Contacts 目前缺少用于配置 Organization 级 IM platform、触发通讯录
- 前端需要在 Contacts feature 边界内实现管理界面、相关路由、typed mock repository、dify-ui 组件使用和 `web/i18n/*` 文案。现有 `web/features/agent-v2/roster/` 管理的是可复用 AI Agent 资产,不属于本 change。
- 本 change 不修改后端 API、OpenAPI schema、生成式 client、数据模型、数据库迁移、任务队列、provider adapter 或真实 OAuth / credential 存储逻辑。
- 管理入口需要适配部署形态:EE 企业管理面与 CE / SaaS workspace 管理面复用同一 Contacts feature UI;角色权限、provider availability 和各类状态暂由 mock scenario 提供,仅用于前端行为展示,不构成安全边界。
- 管理入口只在非企业版的 CE / SaaS workspace 管理面展示,并复用现有 workspace plan 判断隐藏 enterprise plan;角色权限、provider availability 和各类状态暂由 mock scenario 提供,仅用于前端行为展示,不构成安全边界。
- 需要为权限、连接状态、表单提交、手动同步、匹配结果和同步详情补充 Vitest / Testing Library 测试,并使用确定性的 mock scenario 完成前端 smoke 验证。
- 后续后端能力就绪时,应通过新的 change 用真实 repository adapter 替换 mock repository,而不改写页面组件的状态语义。
- 设计验收来源:
@@ -43,18 +43,18 @@
- **WHEN** 用户保存、测试、授权、替换或解除绑定
- **THEN** 当前 change 的实现 MUST 只调用 mock repositoryMUST NOT 新增后端 endpoint、OpenAPI schema 或网络请求
### Requirement: 管理入口必须展示部署形态与权限差异
### Requirement: 管理入口必须只在非企业版 workspace 展示
前端 MUST 支持 EE 企业管理面和 CE / SaaS workspace 管理面 Contacts IM platform 入口。EE scenario 由企业管理员管理;CE / SaaS scenario 由 workspace owner 或 workspace admin 管理。该权限结果 MUST 来自 mock context,且 MUST 被明确视为 UI 展示状态而非真实安全边界。
前端 MUST 只在非 enterprise plan 的 CE / SaaS workspace 管理面展示 Contacts IM platform 入口,并 MUST 复用现有 workspace plan 判断。非企业版由 workspace owner 或 workspace admin 管理权限结果 MUST 来自 mock context,且 MUST 被明确视为 UI 展示状态而非真实安全边界。
#### Scenario: EE 企业管理员管理绑定
#### Scenario: 企业版不展示绑定入口
- **WHEN** EE 企业管理员 scenario 从企业 Contacts 管理面进入 IM platform 设置
- **THEN** 前端 MUST 允许其查看和操作当前 Organization 的 mock 绑定
- **WHEN** 当前 workspace plan 为 enterprise
- **THEN** 前端 MUST 不展示 Contacts IM platform 入口,且通过深链请求该 tab 时 MUST 回退到允许访问的设置页
#### Scenario: CE 或 SaaS 管理员管理绑定
#### Scenario: 非企业版管理员管理绑定
- **WHEN** CE / SaaS workspace owner 或 workspace admin scenario 从 workspace Contacts 管理面进入 IM platform 设置
- **WHEN** 非 enterprise plan 的 CE / SaaS workspace owner 或 workspace admin 从 workspace Contacts 管理面进入 IM platform 设置
- **THEN** 前端 MUST 允许其查看和操作当前 Organization 的 mock 绑定
#### Scenario: 无管理权限
@@ -1,6 +1,6 @@
## 1. Frontend Scope and Design Baseline
- [x] 1.1 Identify the EE enterprise-management and CE / SaaS workspace-management mount points for Contacts, define the shared Organization context contract, and verify that no entry is added under `web/features/agent-v2/roster/`.
- [x] 1.1 Identify the CE / SaaS workspace-management mount point and existing enterprise-plan guard for Contacts, define the Organization context contract, and verify that no entry is added under `web/features/agent-v2/roster/`.
- [x] 1.2 Inspect the six referenced Figma nodes with authorized access and record a frontend acceptance matrix for layout, overlay type, fields, statuses, table columns, responsive behavior, and visible copy.
- [x] 1.3 Define the existing product feature gate used to expose the mock-backed Contacts IM platform entry and its rollback behavior.
@@ -14,7 +14,7 @@
## 3. IM Platform Binding UI
- [ ] 3.1 Write failing component tests for the Contacts entry, EE and CE / SaaS permission variants, initial loading/error/empty states, provider availability, and the six connection-status presentations.
- [ ] 3.1 Write failing component tests for the non-enterprise Contacts entry, enterprise-plan exclusion, CE / SaaS permission variants, initial loading/error/empty states, provider availability, and the six connection-status presentations.
- [ ] 3.2 Implement the shared Contacts IM platform management surface, status summary, provider selection, diagnostics, recent-sync summary, and feature-gated mount points with `@langgenius/dify-ui/*` primitives.
- [ ] 3.3 Write failing component tests for credential and mock OAuth flows, required-field errors, pending-state duplicate prevention, mutation failure recovery, provider replacement confirmation, and disconnect behavior.
- [ ] 3.4 Implement the shared binding overlay and typed provider-specific form adapters, including mock authorization recovery, callback copy interaction, replacement/disconnect confirmation, and repository refresh after mutations.
+2 -2
View File
@@ -5,10 +5,10 @@ Organization-scoped IM platform binding and directory-sync UI for the Contacts d
## Mount Points
- CE / SaaS workspace management mounts this feature from `app/components/header/account-setting`.
- The open-source web tree does not contain a separate EE enterprise-management shell. The feature therefore exposes an Organization context boundary with an `enterprise` scope so that the EE shell can mount the same surface without importing workspace UI.
- Enterprise-plan workspaces do not expose this binding entry. The Account Settings shell checks `ProviderContext.plan.type` before mounting the feature.
- `features/agent-v2/roster` is intentionally outside this feature boundary and must not import or mount Contacts IM platform UI.
The entry is guarded by the existing `NEXT_PUBLIC_ENABLE_FEATURE_PREVIEW` product gate. Setting the flag to `false` removes the mock-backed entry without deleting saved frontend code or affecting Contacts data.
The entry requires both a non-enterprise workspace plan and the existing `NEXT_PUBLIC_ENABLE_FEATURE_PREVIEW` product gate. Setting the flag to `false`, or changing the workspace to the enterprise plan, removes the mock-backed entry without deleting saved frontend code or affecting Contacts data.
## Internal Modules
@@ -11,13 +11,20 @@ describe('Contacts IM platform feature flag', () => {
featurePreviewEnabled = false
})
it('uses the existing feature-preview product gate', async () => {
it('uses the existing feature-preview product gate for non-enterprise workspaces', async () => {
const { isContactsImPlatformEnabled } = await import('../feature-flag')
expect(isContactsImPlatformEnabled()).toBe(false)
expect(isContactsImPlatformEnabled(false)).toBe(false)
featurePreviewEnabled = true
expect(isContactsImPlatformEnabled()).toBe(true)
expect(isContactsImPlatformEnabled(false)).toBe(true)
})
it('keeps the entry hidden for enterprise workspaces', async () => {
featurePreviewEnabled = true
const { isContactsImPlatformEnabled } = await import('../feature-flag')
expect(isContactsImPlatformEnabled(true)).toBe(false)
})
})
+2 -11
View File
@@ -1,14 +1,5 @@
export const ContactsImPlatformManagementScope = {
Enterprise: 'enterprise',
Workspace: 'workspace',
} as const
export type ContactsImPlatformManagementScope =
(typeof ContactsImPlatformManagementScope)[keyof typeof ContactsImPlatformManagementScope]
export type ContactsImPlatformOrganizationContext = {
organizationId: string
workspaceId?: string
managementScope: ContactsImPlatformManagementScope
canManage: boolean
organizationId: string
workspaceId: string
}
@@ -1,3 +1,4 @@
import { ENABLE_FEATURE_PREVIEW } from '@/config'
export const isContactsImPlatformEnabled = () => ENABLE_FEATURE_PREVIEW
export const isContactsImPlatformEnabled = (isEnterprisePlan: boolean) =>
ENABLE_FEATURE_PREVIEW && !isEnterprisePlan