make "require subscription" toggleable in build (#6144)

## Summary

Adds build time feature flags system starting with a flag that indicates
whether subscription is required to use the app. This is only used on
cloud.

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6144-make-require-subscription-toggleable-in-build-2916d73d3650813bb140c5e96bcce1ce)
by [Unito](https://www.unito.io)
This commit is contained in:
Christian Byrne
2025-10-19 10:30:14 -07:00
committed by GitHub
parent fc69924c4a
commit 1f5191847a
8 changed files with 48 additions and 23 deletions

5
global.d.ts vendored
View File

@@ -5,6 +5,11 @@ declare const __ALGOLIA_APP_ID__: string
declare const __ALGOLIA_API_KEY__: string declare const __ALGOLIA_API_KEY__: string
declare const __USE_PROD_CONFIG__: boolean declare const __USE_PROD_CONFIG__: boolean
type BuildFeatureFlags = {
REQUIRE_SUBSCRIPTION: boolean
}
declare const __BUILD_FLAGS__: BuildFeatureFlags
interface Navigator { interface Navigator {
/** /**
* Used by the electron API. This is a WICG non-standard API, but is guaranteed to exist in Electron. * Used by the electron API. This is a WICG non-standard API, but is guaranteed to exist in Electron.

View File

@@ -2,6 +2,6 @@ import { defineAsyncComponent } from 'vue'
import { isCloud } from '@/platform/distribution/types' import { isCloud } from '@/platform/distribution/types'
export default isCloud export default isCloud && __BUILD_FLAGS__.REQUIRE_SUBSCRIPTION
? defineAsyncComponent(() => import('./CloudRunButtonWrapper.vue')) ? defineAsyncComponent(() => import('./CloudRunButtonWrapper.vue'))
: defineAsyncComponent(() => import('./ComfyQueueButton.vue')) : defineAsyncComponent(() => import('./ComfyQueueButton.vue'))

View File

@@ -26,5 +26,8 @@ import './widgetInputs'
if (isCloud) { if (isCloud) {
import('./cloudBadge') import('./cloudBadge')
import('./cloudSubscription')
if (__BUILD_FLAGS__.REQUIRE_SUBSCRIPTION) {
import('./cloudSubscription')
}
} }

View File

@@ -26,7 +26,7 @@ interface CloudSubscriptionStatusResponse {
const subscriptionStatus = ref<CloudSubscriptionStatusResponse | null>(null) const subscriptionStatus = ref<CloudSubscriptionStatusResponse | null>(null)
const isActiveSubscription = computed(() => { const isActiveSubscription = computed(() => {
if (!isCloud) return true if (!isCloud || !__BUILD_FLAGS__.REQUIRE_SUBSCRIPTION) return true
return subscriptionStatus.value?.is_active ?? false return subscriptionStatus.value?.is_active ?? false
}) })

View File

@@ -80,21 +80,22 @@ export function useSettingUI(
) )
} }
const subscriptionPanel: SettingPanelItem | null = !isCloud const subscriptionPanel: SettingPanelItem | null =
? null !isCloud || !__BUILD_FLAGS__.REQUIRE_SUBSCRIPTION
: { ? null
node: { : {
key: 'subscription', node: {
label: 'PlanCredits', key: 'subscription',
children: [] label: 'PlanCredits',
}, children: []
component: defineAsyncComponent( },
() => component: defineAsyncComponent(
import( () =>
'@/platform/cloud/subscription/components/SubscriptionPanel.vue' import(
) '@/platform/cloud/subscription/components/SubscriptionPanel.vue'
) )
} )
}
const userPanel: SettingPanelItem = { const userPanel: SettingPanelItem = {
node: { node: {
@@ -148,7 +149,9 @@ export function useSettingUI(
keybindingPanel, keybindingPanel,
extensionPanel, extensionPanel,
...(isElectron() ? [serverConfigPanel] : []), ...(isElectron() ? [serverConfigPanel] : []),
...(isCloud && subscriptionPanel ? [subscriptionPanel] : []) ...(isCloud && __BUILD_FLAGS__.REQUIRE_SUBSCRIPTION && subscriptionPanel
? [subscriptionPanel]
: [])
].filter((panel) => panel.component) ].filter((panel) => panel.component)
) )
@@ -180,10 +183,16 @@ export function useSettingUI(
label: 'Account', label: 'Account',
children: [ children: [
userPanel.node, userPanel.node,
...(isLoggedIn.value && isCloud && subscriptionPanel ...(isLoggedIn.value &&
isCloud &&
__BUILD_FLAGS__.REQUIRE_SUBSCRIPTION &&
subscriptionPanel
? [subscriptionPanel.node] ? [subscriptionPanel.node]
: []), : []),
...(isLoggedIn.value && !isCloud ? [creditsPanel.node] : []) ...(isLoggedIn.value &&
!(isCloud && __BUILD_FLAGS__.REQUIRE_SUBSCRIPTION)
? [creditsPanel.node]
: [])
].map(translateCategory) ].map(translateCategory)
}, },
// Normal settings stored in the settingStore // Normal settings stored in the settingStore

View File

@@ -488,7 +488,7 @@ export const useDialogService = () => {
} }
function showSubscriptionRequiredDialog() { function showSubscriptionRequiredDialog() {
if (!isCloud) { if (!isCloud || !__BUILD_FLAGS__.REQUIRE_SUBSCRIPTION) {
return return
} }

View File

@@ -32,6 +32,10 @@ const DISTRIBUTION = (process.env.DISTRIBUTION || 'localhost') as
| 'localhost' | 'localhost'
| 'cloud' | 'cloud'
const BUILD_FLAGS = {
REQUIRE_SUBSCRIPTION: process.env.REQUIRE_SUBSCRIPTION === 'true'
}
export default defineConfig({ export default defineConfig({
base: '', base: '',
server: { server: {
@@ -267,7 +271,8 @@ export default defineConfig({
__ALGOLIA_APP_ID__: JSON.stringify(process.env.ALGOLIA_APP_ID || ''), __ALGOLIA_APP_ID__: JSON.stringify(process.env.ALGOLIA_APP_ID || ''),
__ALGOLIA_API_KEY__: JSON.stringify(process.env.ALGOLIA_API_KEY || ''), __ALGOLIA_API_KEY__: JSON.stringify(process.env.ALGOLIA_API_KEY || ''),
__USE_PROD_CONFIG__: process.env.USE_PROD_CONFIG === 'true', __USE_PROD_CONFIG__: process.env.USE_PROD_CONFIG === 'true',
__DISTRIBUTION__: JSON.stringify(DISTRIBUTION) __DISTRIBUTION__: JSON.stringify(DISTRIBUTION),
__BUILD_FLAGS__: JSON.stringify(BUILD_FLAGS)
}, },
resolve: { resolve: {

View File

@@ -9,6 +9,9 @@ globalThis.__ALGOLIA_APP_ID__ = ''
globalThis.__ALGOLIA_API_KEY__ = '' globalThis.__ALGOLIA_API_KEY__ = ''
globalThis.__USE_PROD_CONFIG__ = false globalThis.__USE_PROD_CONFIG__ = false
globalThis.__DISTRIBUTION__ = 'localhost' globalThis.__DISTRIBUTION__ = 'localhost'
globalThis.__BUILD_FLAGS__ = {
REQUIRE_SUBSCRIPTION: true
}
// Mock Worker for extendable-media-recorder // Mock Worker for extendable-media-recorder
globalThis.Worker = vi.fn().mockImplementation(() => ({ globalThis.Worker = vi.fn().mockImplementation(() => ({