mirror of
https://github.com/langgenius/dify.git
synced 2026-04-05 05:42:15 +08:00
chore: add support email env (#33075)
This commit is contained in:
@@ -70,6 +70,7 @@ const { mockConfig, mockEnv } = vi.hoisted(() => ({
|
|||||||
mockConfig: {
|
mockConfig: {
|
||||||
IS_CLOUD_EDITION: false,
|
IS_CLOUD_EDITION: false,
|
||||||
ZENDESK_WIDGET_KEY: '',
|
ZENDESK_WIDGET_KEY: '',
|
||||||
|
SUPPORT_EMAIL_ADDRESS: '',
|
||||||
},
|
},
|
||||||
mockEnv: {
|
mockEnv: {
|
||||||
env: {
|
env: {
|
||||||
@@ -80,6 +81,7 @@ const { mockConfig, mockEnv } = vi.hoisted(() => ({
|
|||||||
vi.mock('@/config', () => ({
|
vi.mock('@/config', () => ({
|
||||||
get IS_CLOUD_EDITION() { return mockConfig.IS_CLOUD_EDITION },
|
get IS_CLOUD_EDITION() { return mockConfig.IS_CLOUD_EDITION },
|
||||||
get ZENDESK_WIDGET_KEY() { return mockConfig.ZENDESK_WIDGET_KEY },
|
get ZENDESK_WIDGET_KEY() { return mockConfig.ZENDESK_WIDGET_KEY },
|
||||||
|
get SUPPORT_EMAIL_ADDRESS() { return mockConfig.SUPPORT_EMAIL_ADDRESS },
|
||||||
IS_DEV: false,
|
IS_DEV: false,
|
||||||
IS_CE_EDITION: false,
|
IS_CE_EDITION: false,
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ const { mockZendeskKey } = vi.hoisted(() => ({
|
|||||||
mockZendeskKey: { value: 'test-key' },
|
mockZendeskKey: { value: 'test-key' },
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
const { mockSupportEmailKey } = vi.hoisted(() => ({
|
||||||
|
mockSupportEmailKey: { value: '' },
|
||||||
|
}))
|
||||||
|
|
||||||
vi.mock('@/context/app-context', async (importOriginal) => {
|
vi.mock('@/context/app-context', async (importOriginal) => {
|
||||||
const actual = await importOriginal<typeof import('@/context/app-context')>()
|
const actual = await importOriginal<typeof import('@/context/app-context')>()
|
||||||
return {
|
return {
|
||||||
@@ -33,6 +37,7 @@ vi.mock('@/config', async (importOriginal) => {
|
|||||||
...actual,
|
...actual,
|
||||||
IS_CE_EDITION: false,
|
IS_CE_EDITION: false,
|
||||||
get ZENDESK_WIDGET_KEY() { return mockZendeskKey.value },
|
get ZENDESK_WIDGET_KEY() { return mockZendeskKey.value },
|
||||||
|
get SUPPORT_EMAIL_ADDRESS() { return mockSupportEmailKey.value },
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -84,6 +89,7 @@ describe('Support', () => {
|
|||||||
vi.clearAllMocks()
|
vi.clearAllMocks()
|
||||||
window.zE = vi.fn()
|
window.zE = vi.fn()
|
||||||
mockZendeskKey.value = 'test-key'
|
mockZendeskKey.value = 'test-key'
|
||||||
|
mockSupportEmailKey.value = ''
|
||||||
vi.mocked(useAppContext).mockReturnValue(baseAppContextValue)
|
vi.mocked(useAppContext).mockReturnValue(baseAppContextValue)
|
||||||
vi.mocked(useProviderContext).mockReturnValue({
|
vi.mocked(useProviderContext).mockReturnValue({
|
||||||
...baseProviderContextValue,
|
...baseProviderContextValue,
|
||||||
@@ -96,7 +102,7 @@ describe('Support', () => {
|
|||||||
|
|
||||||
const renderSupport = () => {
|
const renderSupport = () => {
|
||||||
return render(
|
return render(
|
||||||
<DropdownMenu open={true} onOpenChange={() => {}}>
|
<DropdownMenu open={true} onOpenChange={() => { }}>
|
||||||
<DropdownMenuTrigger>open</DropdownMenuTrigger>
|
<DropdownMenuTrigger>open</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent>
|
<DropdownMenuContent>
|
||||||
<Support closeAccountDropdown={mockCloseAccountDropdown} />
|
<Support closeAccountDropdown={mockCloseAccountDropdown} />
|
||||||
@@ -125,7 +131,7 @@ describe('Support', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Plan-based Channels', () => {
|
describe('Dedicated Channels', () => {
|
||||||
it('should show "Contact Us" when ZENDESK_WIDGET_KEY is present', () => {
|
it('should show "Contact Us" when ZENDESK_WIDGET_KEY is present', () => {
|
||||||
// Act
|
// Act
|
||||||
renderSupport()
|
renderSupport()
|
||||||
@@ -166,6 +172,27 @@ describe('Support', () => {
|
|||||||
expect(screen.getByText('common.userProfile.emailSupport')).toBeInTheDocument()
|
expect(screen.getByText('common.userProfile.emailSupport')).toBeInTheDocument()
|
||||||
expect(screen.queryByText('common.userProfile.contactUs')).not.toBeInTheDocument()
|
expect(screen.queryByText('common.userProfile.contactUs')).not.toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should show email support if specified in the config', () => {
|
||||||
|
// Arrange
|
||||||
|
mockZendeskKey.value = ''
|
||||||
|
mockSupportEmailKey.value = 'support@example.com'
|
||||||
|
vi.mocked(useProviderContext).mockReturnValue({
|
||||||
|
...baseProviderContextValue,
|
||||||
|
plan: {
|
||||||
|
...baseProviderContextValue.plan,
|
||||||
|
type: Plan.sandbox,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Act
|
||||||
|
renderSupport()
|
||||||
|
fireEvent.click(screen.getByText('common.userProfile.support'))
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(screen.queryByText('common.userProfile.emailSupport')).toBeInTheDocument()
|
||||||
|
expect(screen.getByText('common.userProfile.emailSupport')?.closest('a')?.getAttribute('href')).toMatch(new RegExp(`^mailto:${mockSupportEmailKey.value}`))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Interactions and Links', () => {
|
describe('Interactions and Links', () => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { DropdownMenuGroup, DropdownMenuItem, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger } from '@/app/components/base/ui/dropdown-menu'
|
import { DropdownMenuGroup, DropdownMenuItem, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger } from '@/app/components/base/ui/dropdown-menu'
|
||||||
import { toggleZendeskWindow } from '@/app/components/base/zendesk/utils'
|
import { toggleZendeskWindow } from '@/app/components/base/zendesk/utils'
|
||||||
import { Plan } from '@/app/components/billing/type'
|
import { Plan } from '@/app/components/billing/type'
|
||||||
import { ZENDESK_WIDGET_KEY } from '@/config'
|
import { SUPPORT_EMAIL_ADDRESS, ZENDESK_WIDGET_KEY } from '@/config'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
import { useProviderContext } from '@/context/provider-context'
|
import { useProviderContext } from '@/context/provider-context'
|
||||||
import { mailToSupport } from '../utils/util'
|
import { mailToSupport } from '../utils/util'
|
||||||
@@ -17,8 +17,8 @@ export default function Support({ closeAccountDropdown }: SupportProps) {
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { plan } = useProviderContext()
|
const { plan } = useProviderContext()
|
||||||
const { userProfile, langGeniusVersionInfo } = useAppContext()
|
const { userProfile, langGeniusVersionInfo } = useAppContext()
|
||||||
const hasDedicatedChannel = plan.type !== Plan.sandbox
|
const hasDedicatedChannel = plan.type !== Plan.sandbox || Boolean(SUPPORT_EMAIL_ADDRESS.trim())
|
||||||
const hasZendeskWidget = !!ZENDESK_WIDGET_KEY?.trim()
|
const hasZendeskWidget = Boolean(ZENDESK_WIDGET_KEY.trim())
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenuSub>
|
<DropdownMenuSub>
|
||||||
@@ -49,7 +49,7 @@ export default function Support({ closeAccountDropdown }: SupportProps) {
|
|||||||
{hasDedicatedChannel && !hasZendeskWidget && (
|
{hasDedicatedChannel && !hasZendeskWidget && (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="justify-between"
|
className="justify-between"
|
||||||
render={<a href={mailToSupport(userProfile.email, plan.type, langGeniusVersionInfo?.current_version)} rel="noopener noreferrer" target="_blank" />}
|
render={<a href={mailToSupport(userProfile.email, plan.type, langGeniusVersionInfo?.current_version, SUPPORT_EMAIL_ADDRESS)} rel="noopener noreferrer" target="_blank" />}
|
||||||
>
|
>
|
||||||
<MenuItemContent
|
<MenuItemContent
|
||||||
iconClassName="i-ri-mail-send-line"
|
iconClassName="i-ri-mail-send-line"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ export const generateMailToLink = (email: string, subject?: string, body?: strin
|
|||||||
return mailtoLink
|
return mailtoLink
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mailToSupport = (account: string, plan: string, version: string) => {
|
export const mailToSupport = (account: string, plan: string, version: string, supportEmailAddress?: string) => {
|
||||||
const subject = `Technical Support Request ${plan} ${account}`
|
const subject = `Technical Support Request ${plan} ${account}`
|
||||||
const body = `
|
const body = `
|
||||||
Please do not remove the following information:
|
Please do not remove the following information:
|
||||||
@@ -21,5 +21,5 @@ export const mailToSupport = (account: string, plan: string, version: string) =>
|
|||||||
Platform:
|
Platform:
|
||||||
Problem Description:
|
Problem Description:
|
||||||
`
|
`
|
||||||
return generateMailToLink('support@dify.ai', subject, body)
|
return generateMailToLink(supportEmailAddress || 'support@dify.ai', subject, body)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -342,6 +342,12 @@ export const ZENDESK_FIELD_IDS = {
|
|||||||
'',
|
'',
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const SUPPORT_EMAIL_ADDRESS = getStringConfig(
|
||||||
|
env.NEXT_PUBLIC_SUPPORT_EMAIL_ADDRESS,
|
||||||
|
'',
|
||||||
|
)
|
||||||
|
|
||||||
export const APP_VERSION = pkg.version
|
export const APP_VERSION = pkg.version
|
||||||
|
|
||||||
export const IS_MARKETPLACE = env.NEXT_PUBLIC_IS_MARKETPLACE
|
export const IS_MARKETPLACE = env.NEXT_PUBLIC_IS_MARKETPLACE
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ const clientSchema = {
|
|||||||
*/
|
*/
|
||||||
NEXT_PUBLIC_SENTRY_DSN: z.string().optional(),
|
NEXT_PUBLIC_SENTRY_DSN: z.string().optional(),
|
||||||
NEXT_PUBLIC_SITE_ABOUT: z.string().optional(),
|
NEXT_PUBLIC_SITE_ABOUT: z.string().optional(),
|
||||||
|
NEXT_PUBLIC_SUPPORT_EMAIL_ADDRESS: z.email().optional(),
|
||||||
NEXT_PUBLIC_SUPPORT_MAIL_LOGIN: coercedBoolean.default(false),
|
NEXT_PUBLIC_SUPPORT_MAIL_LOGIN: coercedBoolean.default(false),
|
||||||
/**
|
/**
|
||||||
* The timeout for the text generation in millisecond
|
* The timeout for the text generation in millisecond
|
||||||
@@ -184,6 +185,7 @@ export const env = createEnv({
|
|||||||
NEXT_PUBLIC_PUBLIC_API_PREFIX: isServer ? process.env.NEXT_PUBLIC_PUBLIC_API_PREFIX : getRuntimeEnvFromBody('publicApiPrefix'),
|
NEXT_PUBLIC_PUBLIC_API_PREFIX: isServer ? process.env.NEXT_PUBLIC_PUBLIC_API_PREFIX : getRuntimeEnvFromBody('publicApiPrefix'),
|
||||||
NEXT_PUBLIC_SENTRY_DSN: isServer ? process.env.NEXT_PUBLIC_SENTRY_DSN : getRuntimeEnvFromBody('sentryDsn'),
|
NEXT_PUBLIC_SENTRY_DSN: isServer ? process.env.NEXT_PUBLIC_SENTRY_DSN : getRuntimeEnvFromBody('sentryDsn'),
|
||||||
NEXT_PUBLIC_SITE_ABOUT: isServer ? process.env.NEXT_PUBLIC_SITE_ABOUT : getRuntimeEnvFromBody('siteAbout'),
|
NEXT_PUBLIC_SITE_ABOUT: isServer ? process.env.NEXT_PUBLIC_SITE_ABOUT : getRuntimeEnvFromBody('siteAbout'),
|
||||||
|
NEXT_PUBLIC_SUPPORT_EMAIL_ADDRESS: isServer ? process.env.NEXT_PUBLIC_SUPPORT_EMAIL_ADDRESS : getRuntimeEnvFromBody('supportEmailAddress'),
|
||||||
NEXT_PUBLIC_SUPPORT_MAIL_LOGIN: isServer ? process.env.NEXT_PUBLIC_SUPPORT_MAIL_LOGIN : getRuntimeEnvFromBody('supportMailLogin'),
|
NEXT_PUBLIC_SUPPORT_MAIL_LOGIN: isServer ? process.env.NEXT_PUBLIC_SUPPORT_MAIL_LOGIN : getRuntimeEnvFromBody('supportMailLogin'),
|
||||||
NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS: isServer ? process.env.NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS : getRuntimeEnvFromBody('textGenerationTimeoutMs'),
|
NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS: isServer ? process.env.NEXT_PUBLIC_TEXT_GENERATION_TIMEOUT_MS : getRuntimeEnvFromBody('textGenerationTimeoutMs'),
|
||||||
NEXT_PUBLIC_TOP_K_MAX_VALUE: isServer ? process.env.NEXT_PUBLIC_TOP_K_MAX_VALUE : getRuntimeEnvFromBody('topKMaxValue'),
|
NEXT_PUBLIC_TOP_K_MAX_VALUE: isServer ? process.env.NEXT_PUBLIC_TOP_K_MAX_VALUE : getRuntimeEnvFromBody('topKMaxValue'),
|
||||||
|
|||||||
Reference in New Issue
Block a user