diff --git a/global.d.ts b/global.d.ts index 5493cbb18..059e47732 100644 --- a/global.d.ts +++ b/global.d.ts @@ -4,12 +4,19 @@ declare const __SENTRY_DSN__: string declare const __ALGOLIA_APP_ID__: string declare const __ALGOLIA_API_KEY__: string declare const __USE_PROD_CONFIG__: boolean -declare const __MIXPANEL_TOKEN__: string -type BuildFeatureFlags = { - REQUIRE_SUBSCRIPTION: boolean +interface Window { + __CONFIG__: { + mixpanel_token?: string + subscription_required?: boolean + server_health_alert?: { + message: string + tooltip?: string + severity?: 'info' | 'warning' | 'error' + badge?: string + } + } } -declare const __BUILD_FLAGS__: BuildFeatureFlags interface Navigator { /** diff --git a/src/components/actionbar/ComfyRunButton/index.ts b/src/components/actionbar/ComfyRunButton/index.ts index 4d15d44ee..1ee49963e 100644 --- a/src/components/actionbar/ComfyRunButton/index.ts +++ b/src/components/actionbar/ComfyRunButton/index.ts @@ -2,6 +2,6 @@ import { defineAsyncComponent } from 'vue' import { isCloud } from '@/platform/distribution/types' -export default isCloud && __BUILD_FLAGS__.REQUIRE_SUBSCRIPTION +export default isCloud && window.__CONFIG__?.subscription_required ? defineAsyncComponent(() => import('./CloudRunButtonWrapper.vue')) : defineAsyncComponent(() => import('./ComfyQueueButton.vue')) diff --git a/src/components/graph/GraphCanvas.vue b/src/components/graph/GraphCanvas.vue index ea052d34a..90d2b30b9 100644 --- a/src/components/graph/GraphCanvas.vue +++ b/src/components/graph/GraphCanvas.vue @@ -12,7 +12,7 @@ v-if="isNativeWindow() && workflowTabsPosition !== 'Topbar'" class="app-drag fixed top-0 left-0 z-10 h-[var(--comfy-topbar-height)] w-full" /> -
+
diff --git a/src/components/topbar/TopbarBadge.vue b/src/components/topbar/TopbarBadge.vue index 5519e28fd..28504fd4a 100644 --- a/src/components/topbar/TopbarBadge.vue +++ b/src/components/topbar/TopbarBadge.vue @@ -1,39 +1,83 @@ diff --git a/src/components/topbar/TopbarBadges.vue b/src/components/topbar/TopbarBadges.vue index 824b50865..08be1a1bd 100644 --- a/src/components/topbar/TopbarBadges.vue +++ b/src/components/topbar/TopbarBadges.vue @@ -1,18 +1,18 @@ diff --git a/src/extensions/core/cloudBadge.ts b/src/extensions/core/cloudBadge.ts deleted file mode 100644 index 6a1074a7d..000000000 --- a/src/extensions/core/cloudBadge.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { t } from '@/i18n' -import { useExtensionService } from '@/services/extensionService' - -useExtensionService().registerExtension({ - name: 'Comfy.CloudBadge', - topbarBadges: [ - { - label: t('g.beta'), - text: 'Comfy Cloud' - } - ] -}) diff --git a/src/extensions/core/cloudBadges.ts b/src/extensions/core/cloudBadges.ts new file mode 100644 index 000000000..45f658717 --- /dev/null +++ b/src/extensions/core/cloudBadges.ts @@ -0,0 +1,36 @@ +import { computed } from 'vue' + +import { remoteConfig } from '@/platform/remoteConfig/remoteConfig' +import { t } from '@/i18n' +import { useExtensionService } from '@/services/extensionService' +import type { TopbarBadge } from '@/types/comfy' + +const badges = computed(() => { + const result: TopbarBadge[] = [] + + // Add server health alert first (if present) + const alert = remoteConfig.value.server_health_alert + if (alert) { + result.push({ + text: alert.message, + label: alert.badge, + variant: alert.severity ?? 'error', + tooltip: alert.tooltip + }) + } + + // Always add cloud badge last (furthest right) + result.push({ + label: t('g.beta'), + text: 'Comfy Cloud' + }) + + return result +}) + +useExtensionService().registerExtension({ + name: 'Comfy.Cloud.Badges', + get topbarBadges() { + return badges.value + } +}) diff --git a/src/extensions/core/cloudRemoteConfig.ts b/src/extensions/core/cloudRemoteConfig.ts new file mode 100644 index 000000000..a40ac401b --- /dev/null +++ b/src/extensions/core/cloudRemoteConfig.ts @@ -0,0 +1,15 @@ +import { loadRemoteConfig } from '@/platform/remoteConfig/remoteConfig' +import { useExtensionService } from '@/services/extensionService' + +/** + * Cloud-only extension that polls for remote config updates + * Initial config load happens in main.ts before any other imports + */ +useExtensionService().registerExtension({ + name: 'Comfy.Cloud.RemoteConfig', + + setup: async () => { + // Poll for config updates every 30 seconds + setInterval(() => void loadRemoteConfig(), 30000) + } +}) diff --git a/src/extensions/core/cloudSubscription.ts b/src/extensions/core/cloudSubscription.ts index c98fc5a83..a483a6d46 100644 --- a/src/extensions/core/cloudSubscription.ts +++ b/src/extensions/core/cloudSubscription.ts @@ -4,8 +4,11 @@ import { useCurrentUser } from '@/composables/auth/useCurrentUser' import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription' import { useExtensionService } from '@/services/extensionService' +/** + * Cloud-only extension that enforces active subscription requirement + */ useExtensionService().registerExtension({ - name: 'Comfy.CloudSubscription', + name: 'Comfy.Cloud.Subscription', setup: async () => { const { isLoggedIn } = useCurrentUser() @@ -13,7 +16,6 @@ useExtensionService().registerExtension({ const checkSubscriptionStatus = () => { if (!isLoggedIn.value) return - void requireActiveSubscription() } diff --git a/src/extensions/core/index.ts b/src/extensions/core/index.ts index 31d73abee..40ec459b5 100644 --- a/src/extensions/core/index.ts +++ b/src/extensions/core/index.ts @@ -24,10 +24,12 @@ import './uploadImage' import './webcamCapture' import './widgetInputs' +// Cloud-only extensions - tree-shaken in OSS builds if (isCloud) { - import('./cloudBadge') + await import('./cloudRemoteConfig') + await import('./cloudBadges') - if (__BUILD_FLAGS__.REQUIRE_SUBSCRIPTION) { - import('./cloudSubscription') + if (window.__CONFIG__?.subscription_required) { + await import('./cloudSubscription') } } diff --git a/src/main.ts b/src/main.ts index c8d37becc..74f92b475 100644 --- a/src/main.ts +++ b/src/main.ts @@ -21,6 +21,19 @@ import App from './App.vue' import './assets/css/style.css' import { i18n } from './i18n' +/** + * CRITICAL: Load remote config FIRST for cloud builds to ensure + * window.__CONFIG__is available for all modules during initialization + */ +import { isCloud } from '@/platform/distribution/types' + +if (isCloud) { + const { loadRemoteConfig } = await import( + '@/platform/remoteConfig/remoteConfig' + ) + await loadRemoteConfig() +} + const ComfyUIPreset = definePreset(Aura, { semantic: { // @ts-expect-error fixme ts strict error diff --git a/src/platform/cloud/subscription/composables/useSubscription.ts b/src/platform/cloud/subscription/composables/useSubscription.ts index b67c8086b..0b605e5e7 100644 --- a/src/platform/cloud/subscription/composables/useSubscription.ts +++ b/src/platform/cloud/subscription/composables/useSubscription.ts @@ -27,7 +27,7 @@ interface CloudSubscriptionStatusResponse { const subscriptionStatus = ref(null) const isActiveSubscription = computed(() => { - if (!isCloud || !__BUILD_FLAGS__.REQUIRE_SUBSCRIPTION) return true + if (!isCloud || !window.__CONFIG__?.subscription_required) return true return subscriptionStatus.value?.is_active ?? false }) diff --git a/src/platform/remoteConfig/remoteConfig.ts b/src/platform/remoteConfig/remoteConfig.ts new file mode 100644 index 000000000..a20eb7ebd --- /dev/null +++ b/src/platform/remoteConfig/remoteConfig.ts @@ -0,0 +1,45 @@ +/** + * 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. + * Used for initial config load in main.ts and polling in the extension. + */ + +import { ref } from 'vue' + +import type { RemoteConfig } from './types' + +/** + * Reactive remote configuration + * Updated whenever config is loaded from the server + */ +export const remoteConfig = ref({}) + +/** + * Loads remote configuration from the backend /api/features endpoint + * and updates the reactive remoteConfig ref + */ +export async function loadRemoteConfig(): Promise { + try { + const response = await fetch('/api/features', { cache: 'no-store' }) + if (response.ok) { + const config = await response.json() + window.__CONFIG__ = config + remoteConfig.value = config + } else { + console.warn('Failed to load remote config:', response.statusText) + window.__CONFIG__ = {} + remoteConfig.value = {} + } + } catch (error) { + console.error('Failed to fetch remote config:', error) + window.__CONFIG__ = {} + remoteConfig.value = {} + } +} diff --git a/src/platform/remoteConfig/types.ts b/src/platform/remoteConfig/types.ts new file mode 100644 index 000000000..939c6bbf1 --- /dev/null +++ b/src/platform/remoteConfig/types.ts @@ -0,0 +1,19 @@ +/** + * Server health alert configuration from the backend + */ +type ServerHealthAlert = { + message: string + tooltip?: string + severity?: 'info' | 'warning' | 'error' + badge?: string +} + +/** + * Remote configuration type + * Configuration fetched from the server at runtime + */ +export type RemoteConfig = { + mixpanel_token?: string + subscription_required?: boolean + server_health_alert?: ServerHealthAlert +} diff --git a/src/platform/settings/composables/useSettingUI.ts b/src/platform/settings/composables/useSettingUI.ts index 416df810b..a49cccf19 100644 --- a/src/platform/settings/composables/useSettingUI.ts +++ b/src/platform/settings/composables/useSettingUI.ts @@ -81,7 +81,7 @@ export function useSettingUI( } const subscriptionPanel: SettingPanelItem | null = - !isCloud || !__BUILD_FLAGS__.REQUIRE_SUBSCRIPTION + !isCloud || !window.__CONFIG__?.subscription_required ? null : { node: { @@ -149,7 +149,9 @@ export function useSettingUI( keybindingPanel, extensionPanel, ...(isElectron() ? [serverConfigPanel] : []), - ...(isCloud && __BUILD_FLAGS__.REQUIRE_SUBSCRIPTION && subscriptionPanel + ...(isCloud && + window.__CONFIG__?.subscription_required && + subscriptionPanel ? [subscriptionPanel] : []) ].filter((panel) => panel.component) @@ -185,12 +187,12 @@ export function useSettingUI( userPanel.node, ...(isLoggedIn.value && isCloud && - __BUILD_FLAGS__.REQUIRE_SUBSCRIPTION && + window.__CONFIG__?.subscription_required && subscriptionPanel ? [subscriptionPanel.node] : []), ...(isLoggedIn.value && - !(isCloud && __BUILD_FLAGS__.REQUIRE_SUBSCRIPTION) + !(isCloud && window.__CONFIG__?.subscription_required) ? [creditsPanel.node] : []) ].map(translateCategory) diff --git a/src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts b/src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts index 05866cfd0..c89a04098 100644 --- a/src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts +++ b/src/platform/telemetry/providers/cloud/MixpanelTelemetryProvider.ts @@ -7,6 +7,8 @@ import { useWorkflowTemplatesStore } from '@/platform/workflow/templates/reposit import type { AuthMetadata, ExecutionContext, + ExecutionErrorMetadata, + ExecutionSuccessMetadata, RunButtonProperties, SurveyResponses, TelemetryEventName, @@ -40,7 +42,7 @@ export class MixpanelTelemetryProvider implements TelemetryProvider { private isInitialized = false constructor() { - const token = __MIXPANEL_TOKEN__ + const token = window.__CONFIG__?.mixpanel_token if (token) { try { @@ -74,7 +76,7 @@ export class MixpanelTelemetryProvider implements TelemetryProvider { this.isEnabled = false } } else { - console.warn('Mixpanel token not provided') + console.warn('Mixpanel token not provided in runtime config') this.isEnabled = false } } @@ -178,7 +180,15 @@ export class MixpanelTelemetryProvider implements TelemetryProvider { trackWorkflowExecution(): void { const context = this.getExecutionContext() - this.trackEvent(TelemetryEvents.WORKFLOW_EXECUTION_STARTED, context) + this.trackEvent(TelemetryEvents.EXECUTION_START, context) + } + + trackExecutionError(metadata: ExecutionErrorMetadata): void { + this.trackEvent(TelemetryEvents.EXECUTION_ERROR, metadata) + } + + trackExecutionSuccess(metadata: ExecutionSuccessMetadata): void { + this.trackEvent(TelemetryEvents.EXECUTION_SUCCESS, metadata) } getExecutionContext(): ExecutionContext { diff --git a/src/platform/telemetry/types.ts b/src/platform/telemetry/types.ts index c749ca569..d85921001 100644 --- a/src/platform/telemetry/types.ts +++ b/src/platform/telemetry/types.ts @@ -59,6 +59,23 @@ export interface ExecutionContext { template_license?: string } +/** + * Execution error metadata + */ +export interface ExecutionErrorMetadata { + jobId: string + nodeId?: string + nodeType?: string + error?: string +} + +/** + * Execution success metadata + */ +export interface ExecutionSuccessMetadata { + jobId: string +} + /** * Template metadata for workflow tracking */ @@ -94,34 +111,42 @@ export interface TelemetryProvider { // Workflow execution events trackWorkflowExecution(): void + trackExecutionError(metadata: ExecutionErrorMetadata): void + trackExecutionSuccess(metadata: ExecutionSuccessMetadata): void } /** * Telemetry event constants + * + * Event naming conventions: + * - 'app:' prefix: UI/user interaction events + * - No prefix: Backend/system events (execution lifecycle) */ export const TelemetryEvents = { // Authentication Flow - USER_AUTH_COMPLETED: 'user_auth_completed', + USER_AUTH_COMPLETED: 'app:user_auth_completed', // Subscription Flow - RUN_BUTTON_CLICKED: 'run_button_clicked', - SUBSCRIPTION_REQUIRED_MODAL_OPENED: 'subscription_required_modal_opened', - SUBSCRIBE_NOW_BUTTON_CLICKED: 'subscribe_now_button_clicked', + RUN_BUTTON_CLICKED: 'app:run_button_click', + SUBSCRIPTION_REQUIRED_MODAL_OPENED: 'app:subscription_required_modal_opened', + SUBSCRIBE_NOW_BUTTON_CLICKED: 'app:subscribe_now_button_clicked', // Onboarding Survey - USER_SURVEY_OPENED: 'user_survey_opened', - USER_SURVEY_SUBMITTED: 'user_survey_submitted', + USER_SURVEY_OPENED: 'app:user_survey_opened', + USER_SURVEY_SUBMITTED: 'app:user_survey_submitted', // Email Verification - USER_EMAIL_VERIFY_OPENED: 'user_email_verify_opened', - USER_EMAIL_VERIFY_REQUESTED: 'user_email_verify_requested', - USER_EMAIL_VERIFY_COMPLETED: 'user_email_verify_completed', + USER_EMAIL_VERIFY_OPENED: 'app:user_email_verify_opened', + USER_EMAIL_VERIFY_REQUESTED: 'app:user_email_verify_requested', + USER_EMAIL_VERIFY_COMPLETED: 'app:user_email_verify_completed', // Template Tracking - TEMPLATE_WORKFLOW_OPENED: 'template_workflow_opened', + TEMPLATE_WORKFLOW_OPENED: 'app:template_workflow_opened', - // Workflow Execution Tracking - WORKFLOW_EXECUTION_STARTED: 'workflow_execution_started' + // Execution Lifecycle + EXECUTION_START: 'execution_start', + EXECUTION_ERROR: 'execution_error', + EXECUTION_SUCCESS: 'execution_success' } as const export type TelemetryEventName = @@ -136,3 +161,5 @@ export type TelemetryEventProperties = | TemplateMetadata | ExecutionContext | RunButtonProperties + | ExecutionErrorMetadata + | ExecutionSuccessMetadata diff --git a/src/services/dialogService.ts b/src/services/dialogService.ts index ecd12766d..e67983407 100644 --- a/src/services/dialogService.ts +++ b/src/services/dialogService.ts @@ -488,7 +488,7 @@ export const useDialogService = () => { } function showSubscriptionRequiredDialog() { - if (!isCloud || !__BUILD_FLAGS__.REQUIRE_SUBSCRIPTION) { + if (!isCloud || !window.__CONFIG__?.subscription_required) { return } diff --git a/src/stores/executionStore.ts b/src/stores/executionStore.ts index 51e182640..9e4842396 100644 --- a/src/stores/executionStore.ts +++ b/src/stores/executionStore.ts @@ -5,6 +5,8 @@ import type ChatHistoryWidget from '@/components/graph/widgets/ChatHistoryWidget import { useNodeChatHistory } from '@/composables/node/useNodeChatHistory' import { useNodeProgressText } from '@/composables/node/useNodeProgressText' import type { LGraph, Subgraph } from '@/lib/litegraph/src/litegraph' +import { isCloud } from '@/platform/distribution/types' +import { useTelemetry } from '@/platform/telemetry' import type { ComfyWorkflow } from '@/platform/workflow/management/stores/workflowStore' import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore' import type { @@ -288,6 +290,11 @@ export const useExecutionStore = defineStore('execution', () => { } function handleExecutionSuccess() { + if (isCloud && activePromptId.value) { + useTelemetry()?.trackExecutionSuccess({ + jobId: activePromptId.value + }) + } resetExecutionState() } @@ -352,6 +359,14 @@ export const useExecutionStore = defineStore('execution', () => { function handleExecutionError(e: CustomEvent) { lastExecutionError.value = e.detail + if (isCloud) { + useTelemetry()?.trackExecutionError({ + jobId: e.detail.prompt_id, + nodeId: String(e.detail.node_id), + nodeType: e.detail.node_type, + error: e.detail.exception_message + }) + } resetExecutionState() } diff --git a/src/types/comfy.ts b/src/types/comfy.ts index f11488380..513724ab5 100644 --- a/src/types/comfy.ts +++ b/src/types/comfy.ts @@ -39,6 +39,22 @@ export interface TopbarBadge { * Optional badge label (e.g., "BETA", "ALPHA", "NEW") */ label?: string + /** + * Visual variant for the badge + * - info: Default informational badge (white label, gray background) + * - warning: Warning badge (orange theme, higher emphasis) + * - error: Error/alert badge (red theme, highest emphasis) + */ + variant?: 'info' | 'warning' | 'error' + /** + * Optional icon class (e.g., "pi-exclamation-triangle") + * If not provided, variant will determine the default icon + */ + icon?: string + /** + * Optional tooltip text to show on hover + */ + tooltip?: string } export type MissingNodeType = diff --git a/tests-ui/tests/platform/telemetry/useTelemetry.test.ts b/tests-ui/tests/platform/telemetry/useTelemetry.test.ts index fa3e584f2..b5c887c74 100644 --- a/tests-ui/tests/platform/telemetry/useTelemetry.test.ts +++ b/tests-ui/tests/platform/telemetry/useTelemetry.test.ts @@ -15,7 +15,7 @@ describe('useTelemetry', () => { // Should return null for OSS builds expect(provider).toBeNull() - }) + }, 10000) it('should return null consistently for OSS builds', async () => { const { useTelemetry } = await import('@/platform/telemetry') diff --git a/tests-ui/tests/store/executionStore.test.ts b/tests-ui/tests/store/executionStore.test.ts index 498148e57..8632c4922 100644 --- a/tests-ui/tests/store/executionStore.test.ts +++ b/tests-ui/tests/store/executionStore.test.ts @@ -1,22 +1,31 @@ import { createPinia, setActivePinia } from 'pinia' import { beforeEach, describe, expect, it, vi } from 'vitest' -import { useWorkflowStore } from '@/platform/workflow/management/stores/workflowStore' import { app } from '@/scripts/app' import { useExecutionStore } from '@/stores/executionStore' +// Create mock functions that will be shared +const mockNodeExecutionIdToNodeLocatorId = vi.fn() +const mockNodeIdToNodeLocatorId = vi.fn() +const mockNodeLocatorIdToNodeExecutionId = vi.fn() + // Mock the workflowStore -vi.mock('@/platform/workflow/management/stores/workflowStore', () => ({ - useWorkflowStore: vi.fn(() => ({ - nodeExecutionIdToNodeLocatorId: vi.fn(), - nodeIdToNodeLocatorId: vi.fn(), - nodeLocatorIdToNodeExecutionId: vi.fn() - })) -})) +vi.mock('@/platform/workflow/management/stores/workflowStore', async () => { + const { ComfyWorkflow } = await vi.importActual< + typeof import('@/platform/workflow/management/stores/workflowStore') + >('@/platform/workflow/management/stores/workflowStore') + return { + ComfyWorkflow, + useWorkflowStore: vi.fn(() => ({ + nodeExecutionIdToNodeLocatorId: mockNodeExecutionIdToNodeLocatorId, + nodeIdToNodeLocatorId: mockNodeIdToNodeLocatorId, + nodeLocatorIdToNodeExecutionId: mockNodeLocatorIdToNodeExecutionId + })) + } +}) // Remove any previous global types declare global { - // eslint-disable-next-line @typescript-eslint/no-empty-object-type interface Window {} } @@ -37,7 +46,8 @@ vi.mock('@/composables/node/useNodeProgressText', () => ({ vi.mock('@/scripts/app', () => ({ app: { graph: { - getNodeById: vi.fn() + getNodeById: vi.fn(), + _nodes: [] // Add _nodes array for workflowStore iteration }, revokePreviews: vi.fn(), nodePreviewImages: {} @@ -98,24 +108,16 @@ describe('executionStore - display_component handling', () => { describe('useExecutionStore - NodeLocatorId conversions', () => { let store: ReturnType - let workflowStore: ReturnType beforeEach(() => { - setActivePinia(createPinia()) - - // Create the mock workflowStore instance - const mockWorkflowStore = { - nodeExecutionIdToNodeLocatorId: vi.fn(), - nodeIdToNodeLocatorId: vi.fn(), - nodeLocatorIdToNodeExecutionId: vi.fn() - } - - // Mock the useWorkflowStore function to return our mock - vi.mocked(useWorkflowStore).mockReturnValue(mockWorkflowStore as any) - - workflowStore = mockWorkflowStore as any - store = useExecutionStore() vi.clearAllMocks() + // Reset mock implementations + mockNodeExecutionIdToNodeLocatorId.mockReset() + mockNodeIdToNodeLocatorId.mockReset() + mockNodeLocatorIdToNodeExecutionId.mockReset() + + setActivePinia(createPinia()) + store = useExecutionStore() }) describe('executionIdToNodeLocatorId', () => { @@ -168,24 +170,20 @@ describe('useExecutionStore - NodeLocatorId conversions', () => { describe('nodeLocatorIdToExecutionId', () => { it('should convert NodeLocatorId to execution ID', () => { const mockExecutionId = '123:456' - vi.spyOn(workflowStore, 'nodeLocatorIdToNodeExecutionId').mockReturnValue( - mockExecutionId as any - ) + mockNodeLocatorIdToNodeExecutionId.mockReturnValue(mockExecutionId) const result = store.nodeLocatorIdToExecutionId( 'a1b2c3d4-e5f6-7890-abcd-ef1234567890:456' ) - expect(workflowStore.nodeLocatorIdToNodeExecutionId).toHaveBeenCalledWith( + expect(mockNodeLocatorIdToNodeExecutionId).toHaveBeenCalledWith( 'a1b2c3d4-e5f6-7890-abcd-ef1234567890:456' ) expect(result).toBe(mockExecutionId) }) it('should return null when conversion fails', () => { - vi.spyOn(workflowStore, 'nodeLocatorIdToNodeExecutionId').mockReturnValue( - null - ) + mockNodeLocatorIdToNodeExecutionId.mockReturnValue(null) const result = store.nodeLocatorIdToExecutionId('invalid:format') diff --git a/vite.config.mts b/vite.config.mts index 7c09bda46..908e280b5 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -53,10 +53,6 @@ const DEV_SERVER_COMFYUI_URL = const cloudProxyConfig = DISTRIBUTION === 'cloud' ? { secure: false, changeOrigin: true } : {} -const BUILD_FLAGS = { - REQUIRE_SUBSCRIPTION: process.env.REQUIRE_SUBSCRIPTION === 'true' -} - export default defineConfig({ base: '', server: { @@ -306,9 +302,7 @@ export default defineConfig({ __ALGOLIA_APP_ID__: JSON.stringify(process.env.ALGOLIA_APP_ID || ''), __ALGOLIA_API_KEY__: JSON.stringify(process.env.ALGOLIA_API_KEY || ''), __USE_PROD_CONFIG__: process.env.USE_PROD_CONFIG === 'true', - __DISTRIBUTION__: JSON.stringify(DISTRIBUTION), - __BUILD_FLAGS__: JSON.stringify(BUILD_FLAGS), - __MIXPANEL_TOKEN__: JSON.stringify(process.env.MIXPANEL_TOKEN || '') + __DISTRIBUTION__: JSON.stringify(DISTRIBUTION) }, resolve: { diff --git a/vitest.setup.ts b/vitest.setup.ts index 8502a3de9..e3427e7c6 100644 --- a/vitest.setup.ts +++ b/vitest.setup.ts @@ -1,6 +1,17 @@ import { vi } from 'vitest' import 'vue' +// Augment Window interface for tests +declare global { + interface Window { + __CONFIG__: { + mixpanel_token?: string + subscription_required?: boolean + server_health_alert?: string + } + } +} + // Define global variables for tests globalThis.__COMFYUI_FRONTEND_VERSION__ = '1.24.0' globalThis.__SENTRY_ENABLED__ = false @@ -9,8 +20,11 @@ globalThis.__ALGOLIA_APP_ID__ = '' globalThis.__ALGOLIA_API_KEY__ = '' globalThis.__USE_PROD_CONFIG__ = false globalThis.__DISTRIBUTION__ = 'localhost' -globalThis.__BUILD_FLAGS__ = { - REQUIRE_SUBSCRIPTION: true + +// Define runtime config for tests +window.__CONFIG__ = { + subscription_required: true, + mixpanel_token: 'test-token' } // Mock Worker for extendable-media-recorder