feat(channels): add two-tier channel visibility policy for resellers - #933
Merged
Conversation
Lets the platform and individual resellers hide channel types from the create-channel picker and settings accordion, without affecting channels already connected. - Tenant.hiddenChannels (new column) holds a reseller's own hidden-channel list; tenantService.resolveVisibleChannels unions it with the platform's policy so root-tenant hides always take precedence. - New /admin/platform-channels and /manage/platform-channels settings pages let the platform and resellers each manage their own list via platform-channels feature (schema, settings UI, action). - resolveChannelCreatable and the new channels-accordion component apply the policy to the create picker and the workspace settings channel rows. - platform-credential-owner.ts centralizes the existing owner-resolution logic (resolvePlatformOwnerId, resolveOwnerForWorkspace) used by both the credential lookup and the new visibility checks, replacing duplicated inline resolution across channel connect/reconnect/select-account actions. - packages/utils/src/channel.ts introduces a single ChannelCapability registry (creatable/manageable/requiresCredential/order) as the source of truth for the create picker and settings accordion, replacing separately hardcoded ChannelType[] literals that had already drifted from each other. Covered by new/updated tests for visibility guards, add-channel-button gating, settings-channels accordion filtering, and credential-owner resolution consistency across connect/reconnect/select-account flows.
realcodesiman
force-pushed
the
feat/channel-visibility-policy
branch
from
August 10, 2026 14:13
6b62e0c to
74f1bea
Compare
Document the two-tier channel-visibility feature (previously undocumented) and correct two review findings from the feature commit. - docs/tenancy.md: new "Channel-visibility policy" section — UI-gate-not-auth, platform/reseller tier union, resolveVisibleChannels rules, grandfathering, and the tenant-aware owner resolution (host wins over workspaceId) - AGENTS.md: invariant #18 covering the three silent pitfalls (actions don't re-check, must pass tenant-aware owner, must preserve grandfathering); synced to .devin and copilot mirrors via sync:agent-instructions - create/page.tsx: correct overclaiming comment — the page gates rendering, not the connect actions, so hiding is a UI hint not access control - platform-channels-settings.tsx: guard toggleChannel against undefined useWatch value before RHF hydrates defaultValues
Drop two debug console.log calls left in the workspace general settings page's not-found guards.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Tenant.hiddenChannelsandtenantService.resolveVisibleChannels, unioning the platform's hidden-channel policy with a reseller's own, root-tenant hides always taking precedence.apps/builder/src/lib/platform-credential-owner.ts.packages/utils/src/channel.ts'sChannelCapabilityregistry as the single source of truth for which channels are creatable/manageable/require-a-credential, replacing hardcodedChannelType[]literals in the create picker and settings accordion that had drifted from each other.Changes
Tenant.hiddenChannels(jsonb, nullable) —packages/database/drizzle/20260810041827_add_tenant_hidden_channels/tenantService.resolveVisibleChannels(packages/business/src/enterprise/tenant/service.ts),resolveChannelCreatable(apps/builder/src/lib/workspace/resolve-channel-creatable.ts)/admin/platform-channels,/manage/platform-channels, backed byapps/builder/src/features/platform-channels/(schema, settings UI, server action)channels-accordion.tsxandadd-channel-button.tsxapply the visibility policy to the settings rows and create-channel entry pointpackages/utils/src/channel.ts(ChannelCapability)apps/builder/src/lib/platform-credential-owner.ts, consumed by channel connect/reconnect/select-account actions across Messenger, Instagram, WhatsApp, Zalo, TikTok, Google Sheets, Facebook Ads/Lead AdsTest plan
pnpm lintpnpm --filter builder check-typespnpm --filter @chatbotx.io/business check-typesplatform-credential-owner,channels-create-visibility-guard,channels-create-platform-owner,settings-channels-visibility,settings-channels-add-button-gating,webchats-create-visibility-guard,add-channel-button,channel-connect-credential-consistency,tenant-channel-visibility(business package)Note:
apps/builder/__tests__/channel-route-guards.test.tshas 2 pre-existing failures onmain(unrelatedtargetWorkspace.ownerIdmock gap in dashboard tests, verified viagit stashagainstmainbefore this change) — not introduced by this PR.Follow-up: docs + review fixes (65e00e5)
Post-review refinements on top of the feature commit:
resolveVisibleChannelsrules, grandfathering, and tenant-aware owner resolution (host wins overworkspaceId)..devin+ copilot mirrors.toggleChannelagainst anundefineduseWatchvalue before RHF hydratesdefaultValues.