Files
ComfyUI_frontend/src/platform/remoteConfig/remoteConfig.ts
Hunter d6c582c399 feat(billing): gate consolidated billing behind consolidated_billing_enabled flag (#13359)
## Summary

Shields personal-workspace billing code paths behind the new
`consolidated_billing_enabled` feature flag so they fall back to the
**legacy** billing flow while the flag is `false`. Team workspaces are
unaffected and continue to use the workspace-scoped billing flow.

## Changes

- Add `consolidatedBillingEnabled` to `useFeatureFlags` (reads the
`consolidated_billing_enabled` server flag / remote config, defaults to
`false`) and to the `RemoteConfig` type.
- New `useBillingRouting` composable — a single source of truth for
whether the active workspace uses the workspace vs. legacy billing flow:
  - team workspaces disabled → legacy
  - personal workspace + consolidated billing off/missing → legacy
  - personal workspace + consolidated billing on → workspace
  - team workspace → workspace
  - workspace not loaded yet → legacy
- Route `useBillingContext` and the affected UI sites
(`SubscriptionPanel`, `useSubscriptionDialog`, `UsageLogsTable`,
`TopUpCreditsDialogContentLegacy`) through `useBillingRouting` instead
of keying on `teamWorkspacesEnabled` directly.
- Update the storybook `useFeatureFlags` mock to stay in sync.

## Testing

- `pnpm test:unit` for `useBillingRouting`, `useBillingContext`,
`useSubscriptionDialog`, and `UsageLogsTable` (new + updated coverage
for the routing matrix). Remaining quality gates (`typecheck`, `lint`)
are being verified in CI.

## Related

Requires the backend PR that adds the `consolidated_billing_enabled`
flag to `/api/features`.

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-07-02 18:34:39 +00:00

67 lines
2.0 KiB
TypeScript

import { useStorage } from '@vueuse/core'
import type { ServerFeatureFlag } from '@/composables/useFeatureFlags'
/**
* Remote configuration service
*
* Fetches configuration from the server at runtime, enabling:
* - Feature flags without rebuilding
* - Server-side feature discovery
* - Version compatibility management
* - Avoiding vendor lock-in for native apps
*
* This module is tree-shaken in OSS builds.
*/
import { computed, ref } from 'vue'
import type { RemoteConfig } from './types'
/**
* Load state for remote configuration.
* - 'unloaded': No config loaded yet
* - 'anonymous': Config loaded without auth (bootstrap)
* - 'authenticated': Config loaded with auth (user-specific flags available)
* - 'error': Failed to load config
*/
type RemoteConfigState = 'unloaded' | 'anonymous' | 'authenticated' | 'error'
/**
* Current load state of remote configuration
*/
export const remoteConfigState = ref<RemoteConfigState>('unloaded')
/**
* Whether the authenticated config has been loaded.
* Use this to gate access to user-specific feature flags like teamWorkspacesEnabled.
*/
export const isAuthenticatedConfigLoaded = computed(
() => remoteConfigState.value === 'authenticated'
)
/**
* Reactive remote configuration
* Updated whenever config is loaded from the server
*/
export const remoteConfig = ref<RemoteConfig>({})
export function configValueOrDefault<K extends keyof RemoteConfig>(
remoteConfig: RemoteConfig,
key: K,
defaultValue: NonNullable<RemoteConfig[K]>
): NonNullable<RemoteConfig[K]> {
const configValue = remoteConfig[key]
return configValue || defaultValue
}
export const cachedTeamWorkspacesEnabled = useStorage<boolean | undefined>(
'team_workspaces_enabled' satisfies `${ServerFeatureFlag.TEAM_WORKSPACES_ENABLED}`,
undefined
)
export const cachedConsolidatedBillingEnabled = useStorage<boolean | undefined>(
'consolidated_billing_enabled' satisfies `${ServerFeatureFlag.CONSOLIDATED_BILLING_ENABLED}`,
undefined
)