chore: not request system-features for cloud edition (#36891)

Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
This commit is contained in:
非法操作
2026-06-01 17:31:16 +08:00
committed by GitHub
parent 71ffaacb58
commit fc7716704d
7 changed files with 416 additions and 12 deletions
+15
View File
@@ -92,3 +92,18 @@ NEXT_PUBLIC_AMPLITUDE_API_KEY=
# number of concurrency
NEXT_PUBLIC_BATCH_CONCURRENCY=5
# Cloud system-features frontend defaults.
# These values are only used when NEXT_PUBLIC_EDITION=CLOUD (IS_CLOUD_EDITION).
NEXT_PUBLIC_ENABLE_MARKETPLACE=true
NEXT_PUBLIC_ENABLE_EMAIL_CODE_LOGIN=true
NEXT_PUBLIC_ENABLE_EMAIL_PASSWORD_LOGIN=false
NEXT_PUBLIC_ENABLE_SOCIAL_OAUTH_LOGIN=true
NEXT_PUBLIC_ENABLE_COLLABORATION_MODE=false
NEXT_PUBLIC_ALLOW_REGISTER=true
NEXT_PUBLIC_ALLOW_CREATE_WORKSPACE=true
NEXT_PUBLIC_IS_EMAIL_SETUP=true
NEXT_PUBLIC_ENABLE_CHANGE_EMAIL=true
NEXT_PUBLIC_CREATORS_PLATFORM_FEATURES_ENABLED=true
NEXT_PUBLIC_ENABLE_TRIAL_APP=true
NEXT_PUBLIC_ENABLE_EXPLORE_BANNER=true
+52
View File
@@ -0,0 +1,52 @@
import type { SystemFeatures } from '@/types/feature'
import { env } from '@/env'
import { defaultSystemFeatures, InstallationScope, LicenseStatus } from '@/types/feature'
export const cloudSystemFeatures: SystemFeatures = {
...defaultSystemFeatures,
sso_enforced_for_signin: false,
sso_enforced_for_signin_protocol: '',
enable_marketplace: env.NEXT_PUBLIC_ENABLE_MARKETPLACE,
enable_email_code_login: env.NEXT_PUBLIC_ENABLE_EMAIL_CODE_LOGIN,
enable_email_password_login: env.NEXT_PUBLIC_ENABLE_EMAIL_PASSWORD_LOGIN,
enable_social_oauth_login: env.NEXT_PUBLIC_ENABLE_SOCIAL_OAUTH_LOGIN,
enable_collaboration_mode: env.NEXT_PUBLIC_ENABLE_COLLABORATION_MODE,
is_allow_register: env.NEXT_PUBLIC_ALLOW_REGISTER,
is_allow_create_workspace: env.NEXT_PUBLIC_ALLOW_CREATE_WORKSPACE,
is_email_setup: env.NEXT_PUBLIC_IS_EMAIL_SETUP,
enable_change_email: env.NEXT_PUBLIC_ENABLE_CHANGE_EMAIL,
license: {
...defaultSystemFeatures.license,
status: LicenseStatus.NONE,
expired_at: '',
},
branding: {
enabled: false,
application_title: '',
login_page_logo: '',
workspace_logo: '',
favicon: '',
},
webapp_auth: {
enabled: false,
allow_sso: false,
sso_config: {
protocol: '',
},
allow_email_code_login: false,
allow_email_password_login: false,
},
plugin_installation_permission: {
plugin_installation_scope: InstallationScope.ALL,
restrict_to_marketplace_only: false,
},
enable_creators_platform: env.NEXT_PUBLIC_CREATORS_PLATFORM_FEATURES_ENABLED,
enable_trial_app: env.NEXT_PUBLIC_ENABLE_TRIAL_APP,
enable_explore_banner: env.NEXT_PUBLIC_ENABLE_EXPLORE_BANNER,
}
+15
View File
@@ -28,6 +28,21 @@ export NEXT_TELEMETRY_DISABLED=${NEXT_TELEMETRY_DISABLED}
export NEXT_PUBLIC_AMPLITUDE_API_KEY=${AMPLITUDE_API_KEY}
# Cloud system-features frontend defaults.
# These values are only used when EDITION=CLOUD (IS_CLOUD_EDITION).
export NEXT_PUBLIC_ENABLE_MARKETPLACE=${NEXT_PUBLIC_ENABLE_MARKETPLACE:-${MARKETPLACE_ENABLED}}
export NEXT_PUBLIC_ENABLE_EMAIL_CODE_LOGIN=${NEXT_PUBLIC_ENABLE_EMAIL_CODE_LOGIN:-${ENABLE_EMAIL_CODE_LOGIN}}
export NEXT_PUBLIC_ENABLE_EMAIL_PASSWORD_LOGIN=${NEXT_PUBLIC_ENABLE_EMAIL_PASSWORD_LOGIN:-${ENABLE_EMAIL_PASSWORD_LOGIN}}
export NEXT_PUBLIC_ENABLE_SOCIAL_OAUTH_LOGIN=${NEXT_PUBLIC_ENABLE_SOCIAL_OAUTH_LOGIN:-${ENABLE_SOCIAL_OAUTH_LOGIN}}
export NEXT_PUBLIC_ENABLE_COLLABORATION_MODE=${NEXT_PUBLIC_ENABLE_COLLABORATION_MODE:-${ENABLE_COLLABORATION_MODE}}
export NEXT_PUBLIC_ALLOW_REGISTER=${NEXT_PUBLIC_ALLOW_REGISTER:-${ALLOW_REGISTER}}
export NEXT_PUBLIC_ALLOW_CREATE_WORKSPACE=${NEXT_PUBLIC_ALLOW_CREATE_WORKSPACE:-${ALLOW_CREATE_WORKSPACE}}
export NEXT_PUBLIC_IS_EMAIL_SETUP=${NEXT_PUBLIC_IS_EMAIL_SETUP:-${IS_EMAIL_SETUP}}
export NEXT_PUBLIC_ENABLE_CHANGE_EMAIL=${NEXT_PUBLIC_ENABLE_CHANGE_EMAIL:-${ENABLE_CHANGE_EMAIL}}
export NEXT_PUBLIC_CREATORS_PLATFORM_FEATURES_ENABLED=${NEXT_PUBLIC_CREATORS_PLATFORM_FEATURES_ENABLED:-${CREATORS_PLATFORM_FEATURES_ENABLED}}
export NEXT_PUBLIC_ENABLE_TRIAL_APP=${NEXT_PUBLIC_ENABLE_TRIAL_APP:-${ENABLE_TRIAL_APP}}
export NEXT_PUBLIC_ENABLE_EXPLORE_BANNER=${NEXT_PUBLIC_ENABLE_EXPLORE_BANNER:-${ENABLE_EXPLORE_BANNER}}
export NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS=${TEXT_GENERATION_TIMEOUT_MS}
export NEXT_PUBLIC_CSP_WHITELIST=${CSP_WHITELIST}
export NEXT_PUBLIC_ALLOW_EMBED=${ALLOW_EMBED}
+37 -1
View File
@@ -13,7 +13,7 @@ const coercedBoolean = z.string()
.transform(s => s === 'true' || s === '1')
const coercedNumber = z.coerce.number().int().positive()
/// keep-sorted
/// Keep keys sorted except grouped feature-specific blocks.
const clientSchema = {
/**
* Default is not allow to embed into iframe to prevent Clickjacking: https://owasp.org/www-community/attacks/Clickjacking
@@ -63,6 +63,24 @@ const clientSchema = {
* The deployment edition, SELF_HOSTED
*/
NEXT_PUBLIC_EDITION: z.enum(['SELF_HOSTED', 'CLOUD']).default('SELF_HOSTED'),
/**
* Cloud-only system-features defaults.
* These values are only used when NEXT_PUBLIC_EDITION=CLOUD (IS_CLOUD_EDITION).
*/
NEXT_PUBLIC_ENABLE_MARKETPLACE: coercedBoolean.default(true),
NEXT_PUBLIC_ENABLE_EMAIL_CODE_LOGIN: coercedBoolean.default(true),
NEXT_PUBLIC_ENABLE_EMAIL_PASSWORD_LOGIN: coercedBoolean.default(false),
NEXT_PUBLIC_ENABLE_SOCIAL_OAUTH_LOGIN: coercedBoolean.default(true),
NEXT_PUBLIC_ENABLE_COLLABORATION_MODE: coercedBoolean.default(false),
NEXT_PUBLIC_ALLOW_REGISTER: coercedBoolean.default(true),
NEXT_PUBLIC_ALLOW_CREATE_WORKSPACE: coercedBoolean.default(true),
NEXT_PUBLIC_IS_EMAIL_SETUP: coercedBoolean.default(true),
NEXT_PUBLIC_ENABLE_CHANGE_EMAIL: coercedBoolean.default(true),
NEXT_PUBLIC_CREATORS_PLATFORM_FEATURES_ENABLED: coercedBoolean.default(true),
NEXT_PUBLIC_ENABLE_TRIAL_APP: coercedBoolean.default(true),
NEXT_PUBLIC_ENABLE_EXPLORE_BANNER: coercedBoolean.default(true),
/**
* Enable inline LaTeX rendering with single dollar signs ($...$)
* Default is false for security reasons to prevent conflicts with regular text
@@ -171,6 +189,24 @@ export const env = createEnv({
NEXT_PUBLIC_DEPLOY_ENV: isServer ? process.env.NEXT_PUBLIC_DEPLOY_ENV : getRuntimeEnvFromBody('deployEnv'),
NEXT_PUBLIC_DISABLE_UPLOAD_IMAGE_AS_ICON: isServer ? process.env.NEXT_PUBLIC_DISABLE_UPLOAD_IMAGE_AS_ICON : getRuntimeEnvFromBody('disableUploadImageAsIcon'),
NEXT_PUBLIC_EDITION: isServer ? process.env.NEXT_PUBLIC_EDITION : getRuntimeEnvFromBody('edition'),
/**
* Cloud-only system-features defaults.
* These values are only used when NEXT_PUBLIC_EDITION=CLOUD (IS_CLOUD_EDITION).
*/
NEXT_PUBLIC_ENABLE_MARKETPLACE: isServer ? process.env.NEXT_PUBLIC_ENABLE_MARKETPLACE : getRuntimeEnvFromBody('enableMarketplace'),
NEXT_PUBLIC_ENABLE_EMAIL_CODE_LOGIN: isServer ? process.env.NEXT_PUBLIC_ENABLE_EMAIL_CODE_LOGIN : getRuntimeEnvFromBody('enableEmailCodeLogin'),
NEXT_PUBLIC_ENABLE_EMAIL_PASSWORD_LOGIN: isServer ? process.env.NEXT_PUBLIC_ENABLE_EMAIL_PASSWORD_LOGIN : getRuntimeEnvFromBody('enableEmailPasswordLogin'),
NEXT_PUBLIC_ENABLE_SOCIAL_OAUTH_LOGIN: isServer ? process.env.NEXT_PUBLIC_ENABLE_SOCIAL_OAUTH_LOGIN : getRuntimeEnvFromBody('enableSocialOauthLogin'),
NEXT_PUBLIC_ENABLE_COLLABORATION_MODE: isServer ? process.env.NEXT_PUBLIC_ENABLE_COLLABORATION_MODE : getRuntimeEnvFromBody('enableCollaborationMode'),
NEXT_PUBLIC_ALLOW_REGISTER: isServer ? process.env.NEXT_PUBLIC_ALLOW_REGISTER : getRuntimeEnvFromBody('allowRegister'),
NEXT_PUBLIC_ALLOW_CREATE_WORKSPACE: isServer ? process.env.NEXT_PUBLIC_ALLOW_CREATE_WORKSPACE : getRuntimeEnvFromBody('allowCreateWorkspace'),
NEXT_PUBLIC_IS_EMAIL_SETUP: isServer ? process.env.NEXT_PUBLIC_IS_EMAIL_SETUP : getRuntimeEnvFromBody('isEmailSetup'),
NEXT_PUBLIC_ENABLE_CHANGE_EMAIL: isServer ? process.env.NEXT_PUBLIC_ENABLE_CHANGE_EMAIL : getRuntimeEnvFromBody('enableChangeEmail'),
NEXT_PUBLIC_CREATORS_PLATFORM_FEATURES_ENABLED: isServer ? process.env.NEXT_PUBLIC_CREATORS_PLATFORM_FEATURES_ENABLED : getRuntimeEnvFromBody('creatorsPlatformFeaturesEnabled'),
NEXT_PUBLIC_ENABLE_TRIAL_APP: isServer ? process.env.NEXT_PUBLIC_ENABLE_TRIAL_APP : getRuntimeEnvFromBody('enableTrialApp'),
NEXT_PUBLIC_ENABLE_EXPLORE_BANNER: isServer ? process.env.NEXT_PUBLIC_ENABLE_EXPLORE_BANNER : getRuntimeEnvFromBody('enableExploreBanner'),
NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX: isServer ? process.env.NEXT_PUBLIC_ENABLE_SINGLE_DOLLAR_LATEX : getRuntimeEnvFromBody('enableSingleDollarLatex'),
NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL: isServer ? process.env.NEXT_PUBLIC_ENABLE_WEBSITE_FIRECRAWL : getRuntimeEnvFromBody('enableWebsiteFirecrawl'),
NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER: isServer ? process.env.NEXT_PUBLIC_ENABLE_WEBSITE_JINAREADER : getRuntimeEnvFromBody('enableWebsiteJinareader'),
@@ -0,0 +1,261 @@
import type { SystemFeatures } from '@/types/feature'
import { describe, expect, it, vi } from 'vitest'
import { defaultSystemFeatures } from '@/types/feature'
type LoadOptions = {
cloudEnv?: Partial<typeof defaultCloudEnv>
isCloudEdition: boolean
systemFeaturesResult?: SystemFeatures
systemFeaturesError?: Error
}
const defaultCloudEnv = {
NEXT_PUBLIC_ALLOW_CREATE_WORKSPACE: true,
NEXT_PUBLIC_ALLOW_REGISTER: true,
NEXT_PUBLIC_CREATORS_PLATFORM_FEATURES_ENABLED: true,
NEXT_PUBLIC_ENABLE_CHANGE_EMAIL: true,
NEXT_PUBLIC_ENABLE_COLLABORATION_MODE: false,
NEXT_PUBLIC_ENABLE_EMAIL_CODE_LOGIN: true,
NEXT_PUBLIC_ENABLE_EMAIL_PASSWORD_LOGIN: false,
NEXT_PUBLIC_ENABLE_EXPLORE_BANNER: true,
NEXT_PUBLIC_ENABLE_MARKETPLACE: true,
NEXT_PUBLIC_ENABLE_SOCIAL_OAUTH_LOGIN: true,
NEXT_PUBLIC_ENABLE_TRIAL_APP: true,
NEXT_PUBLIC_IS_EMAIL_SETUP: true,
}
const queryKey = ['console', 'systemFeatures'] as const
const queryContext = {
queryKey,
signal: new AbortController().signal,
meta: undefined,
} as never
const loadSystemFeaturesModule = async ({
cloudEnv,
isCloudEdition,
systemFeaturesResult = defaultSystemFeatures,
systemFeaturesError,
}: LoadOptions) => {
vi.resetModules()
const systemFeatures = systemFeaturesError
? vi.fn().mockRejectedValue(systemFeaturesError)
: vi.fn().mockResolvedValue(systemFeaturesResult)
vi.doMock('@/config', () => ({
IS_CLOUD_EDITION: isCloudEdition,
}))
vi.doMock('@/env', () => ({
env: {
...defaultCloudEnv,
...cloudEnv,
},
}))
vi.doMock('../client', () => ({
consoleClient: {
systemFeatures,
},
consoleQuery: {
systemFeatures: {
queryKey: () => queryKey,
},
},
}))
const module = await import('../system-features')
return {
module,
systemFeatures,
}
}
const loadServerSystemFeaturesModule = async ({
cloudEnv,
isCloudEdition,
systemFeaturesResult = defaultSystemFeatures,
systemFeaturesError,
}: LoadOptions) => {
vi.resetModules()
const getServerConsoleClientContext = vi.fn().mockResolvedValue({ cookie: 'session=1' })
const systemFeatures = systemFeaturesError
? vi.fn().mockRejectedValue(systemFeaturesError)
: vi.fn().mockResolvedValue(systemFeaturesResult)
vi.doMock('@/config', () => ({
IS_CLOUD_EDITION: isCloudEdition,
}))
vi.doMock('@/env', () => ({
env: {
...defaultCloudEnv,
...cloudEnv,
},
}))
vi.doMock('../server', () => ({
getServerConsoleClientContext,
serverConsoleClient: {
systemFeatures,
},
serverConsoleQuery: {
systemFeatures: {
queryKey: () => queryKey,
},
},
}))
const module = await import('../server-system-features')
return {
getServerConsoleClientContext,
module,
systemFeatures,
}
}
describe('systemFeaturesQueryOptions', () => {
it('should return Cloud defaults without calling system-features when Cloud edition is enabled', async () => {
const { module, systemFeatures } = await loadSystemFeaturesModule({
isCloudEdition: true,
})
const options = module.systemFeaturesQueryOptions()
const data = await options.queryFn?.(queryContext)
expect(systemFeatures).not.toHaveBeenCalled()
expect(options.staleTime).toBe(Infinity)
expect(data).toMatchObject({
enable_marketplace: true,
enable_email_code_login: true,
enable_email_password_login: false,
enable_social_oauth_login: true,
enable_trial_app: true,
})
})
it('should use Cloud environment flags with local defaults for fixed fields', async () => {
const { module } = await loadSystemFeaturesModule({
isCloudEdition: true,
cloudEnv: {
NEXT_PUBLIC_ENABLE_MARKETPLACE: false,
NEXT_PUBLIC_ENABLE_EMAIL_PASSWORD_LOGIN: true,
NEXT_PUBLIC_ENABLE_COLLABORATION_MODE: true,
NEXT_PUBLIC_ALLOW_REGISTER: false,
NEXT_PUBLIC_ENABLE_EXPLORE_BANNER: false,
},
})
const options = module.systemFeaturesQueryOptions()
const data = await options.queryFn?.(queryContext)
expect(data).toMatchObject({
enable_marketplace: false,
enable_email_password_login: true,
enable_collaboration_mode: true,
is_allow_register: false,
enable_explore_banner: false,
branding: {
enabled: false,
application_title: '',
favicon: '',
},
license: {
status: 'none',
},
})
})
it('should fetch system-features when Cloud edition is disabled', async () => {
const systemFeaturesResult = {
...defaultSystemFeatures,
enable_marketplace: true,
}
const { module, systemFeatures } = await loadSystemFeaturesModule({
isCloudEdition: false,
systemFeaturesResult,
})
const options = module.systemFeaturesQueryOptions()
const data = await options.queryFn?.(queryContext)
expect(systemFeatures).toHaveBeenCalledTimes(1)
expect(data).toBe(systemFeaturesResult)
})
it('should fall back to defaults when the non-Cloud request fails', async () => {
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
const { module, systemFeatures } = await loadSystemFeaturesModule({
isCloudEdition: false,
systemFeaturesError: new Error('network failed'),
})
const options = module.systemFeaturesQueryOptions()
const data = await options.queryFn?.(queryContext)
expect(systemFeatures).toHaveBeenCalledTimes(1)
expect(data).toEqual(defaultSystemFeatures)
errorSpy.mockRestore()
})
})
describe('serverSystemFeaturesQueryOptions', () => {
it('should prefetch Cloud defaults without calling server system-features when Cloud edition is enabled', async () => {
const { getServerConsoleClientContext, module, systemFeatures } = await loadServerSystemFeaturesModule({
isCloudEdition: true,
cloudEnv: {
NEXT_PUBLIC_ENABLE_MARKETPLACE: false,
NEXT_PUBLIC_ENABLE_EMAIL_PASSWORD_LOGIN: true,
},
})
const options = module.serverSystemFeaturesQueryOptions()
const data = await options.queryFn?.(queryContext)
expect(getServerConsoleClientContext).not.toHaveBeenCalled()
expect(systemFeatures).not.toHaveBeenCalled()
expect(options.staleTime).toBe(Infinity)
expect(data).toMatchObject({
enable_marketplace: false,
enable_email_password_login: true,
})
})
it('should fetch server system-features when Cloud edition is disabled', async () => {
const systemFeaturesResult = {
...defaultSystemFeatures,
enable_marketplace: true,
}
const { getServerConsoleClientContext, module, systemFeatures } = await loadServerSystemFeaturesModule({
isCloudEdition: false,
systemFeaturesResult,
})
const options = module.serverSystemFeaturesQueryOptions()
const data = await options.queryFn?.(queryContext)
expect(getServerConsoleClientContext).toHaveBeenCalledTimes(1)
expect(systemFeatures).toHaveBeenCalledTimes(1)
expect(systemFeatures).toHaveBeenCalledWith(undefined, {
context: { cookie: 'session=1' },
})
expect(data).toBe(systemFeaturesResult)
})
it('should fall back to defaults when the non-Cloud server request fails', async () => {
const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
const { module, systemFeatures } = await loadServerSystemFeaturesModule({
isCloudEdition: false,
systemFeaturesError: new Error('server failed'),
})
const options = module.serverSystemFeaturesQueryOptions()
const data = await options.queryFn?.(queryContext)
expect(systemFeatures).toHaveBeenCalledTimes(1)
expect(data).toEqual(defaultSystemFeatures)
errorSpy.mockRestore()
})
})
+16 -3
View File
@@ -1,5 +1,7 @@
import type { SystemFeatures } from '@/types/feature'
import { queryOptions } from '@tanstack/react-query'
import { IS_CLOUD_EDITION } from '@/config'
import { cloudSystemFeatures } from '@/config/cloud-system-features'
import { defaultSystemFeatures } from '@/types/feature'
import {
getServerConsoleClientContext,
@@ -7,9 +9,19 @@ import {
serverConsoleQuery,
} from './server'
export const serverSystemFeaturesQueryOptions = () =>
queryOptions<SystemFeatures>({
queryKey: serverConsoleQuery.systemFeatures.queryKey(),
export const serverSystemFeaturesQueryOptions = () => {
const queryKey = serverConsoleQuery.systemFeatures.queryKey()
if (IS_CLOUD_EDITION) {
return queryOptions<SystemFeatures>({
queryKey,
queryFn: async () => cloudSystemFeatures,
staleTime: Infinity,
})
}
return queryOptions<SystemFeatures>({
queryKey,
queryFn: async () => {
try {
return await serverConsoleClient.systemFeatures(undefined, {
@@ -22,3 +34,4 @@ export const serverSystemFeaturesQueryOptions = () =>
}
},
})
}
+20 -8
View File
@@ -1,5 +1,7 @@
import type { SystemFeatures } from '@/types/feature'
import { queryOptions } from '@tanstack/react-query'
import { IS_CLOUD_EDITION } from '@/config'
import { cloudSystemFeatures } from '@/config/cloud-system-features'
import { defaultSystemFeatures } from '@/types/feature'
import { consoleClient, consoleQuery } from './client'
@@ -12,15 +14,24 @@ import { consoleClient, consoleQuery } from './client'
* availability via defaults; the trade-off is acceptable because /system-features
* is a small, dependency-free endpoint in the community edition.
*
* No `staleTime` override either: inherit the 5-minute default from
* query-client-server.ts. Combined with `refetchOnWindowFocus`, this lets us
* recover from a transient startup failure (which got cached as "successful
* defaults") within ~5 minutes or on tab focus. `staleTime: Infinity` would
* pin the whole tab to defaults until reload — strictly worse than main.
* For Cloud, this query is intentionally local-only and uses `staleTime:
* Infinity`: the payload comes from frontend config/defaults, so refetching
* would only re-run the same local merge. For non-Cloud, do not override
* `staleTime`: inherit the 5-minute default from query-client-server.ts.
*/
export const systemFeaturesQueryOptions = () =>
queryOptions<SystemFeatures>({
queryKey: consoleQuery.systemFeatures.queryKey(),
export const systemFeaturesQueryOptions = () => {
const queryKey = consoleQuery.systemFeatures.queryKey()
if (IS_CLOUD_EDITION) {
return queryOptions<SystemFeatures>({
queryKey,
queryFn: async () => cloudSystemFeatures,
staleTime: Infinity,
})
}
return queryOptions<SystemFeatures>({
queryKey,
queryFn: async () => {
try {
return await consoleClient.systemFeatures()
@@ -31,3 +42,4 @@ export const systemFeaturesQueryOptions = () =>
}
},
})
}