diff --git a/web/features/deployments/detail/access-tab.tsx b/web/features/deployments/detail/access-tab.tsx index 49f683835e..1f4ec0d700 100644 --- a/web/features/deployments/detail/access-tab.tsx +++ b/web/features/deployments/detail/access-tab.tsx @@ -1,74 +1,17 @@ 'use client' -import type { - ConsoleEnvironment, - EnvironmentAccessRow, -} from '@dify/contracts/enterprise/types.gen' -import { useQuery } from '@tanstack/react-query' -import { consoleQuery } from '@/service/client' -import { - deployedRows, -} from '../utils' import { AccessChannelsSection } from './access-tab/channels-section' import { DeveloperApiSection } from './access-tab/developer-api-section' import { AccessPermissionsSection } from './access-tab/permissions-section' -import { getUrlOrigin } from './access-tab/url' - -const EMPTY_ACCESS_PERMISSIONS: EnvironmentAccessRow[] = [] - -function uniqueEnvironments(environments: (ConsoleEnvironment | undefined)[]) { - return environments.filter((environment, index): environment is ConsoleEnvironment => { - if (!environment?.id) - return false - return environments.findIndex(candidate => candidate?.id === environment.id) === index - }) -} export function AccessTab({ instanceId: appId }: { instanceId: string }) { - const appInput = { params: { appInstanceId: appId } } - const { data: accessConfig } = useQuery(consoleQuery.enterprise.appDeploy.getAppInstanceAccess.queryOptions({ - input: appInput, - })) - const { data: environmentDeployments } = useQuery(consoleQuery.enterprise.appDeploy.listRuntimeInstances.queryOptions({ - input: appInput, - })) - const deploymentRows = deployedRows(environmentDeployments?.data) - const policies = accessConfig?.permissions ?? EMPTY_ACCESS_PERMISSIONS - const deployedEnvs = uniqueEnvironments([ - ...deploymentRows.map(row => row.environment), - ...policies.map(policy => policy.environment), - ...(accessConfig?.accessChannels?.webappRows?.map(row => row.environment) ?? []), - ]) - const apiEnabled = accessConfig?.developerApi?.enabled ?? false - const apiKeys = accessConfig?.developerApi?.apiKeys ?? [] - const webappRows = accessConfig?.accessChannels?.webappRows?.filter(row => row.url) ?? [] - const runEnabled = accessConfig?.accessChannels?.enabled ?? false - const cliDomain = getUrlOrigin(accessConfig?.accessChannels?.cli?.url) - const cliDocsUrl = cliDomain ? `${cliDomain}/cli` : undefined - return (
- - - + + +
) } diff --git a/web/features/deployments/detail/access-tab/channels-section.tsx b/web/features/deployments/detail/access-tab/channels-section.tsx index 842ee329cd..900b459c0a 100644 --- a/web/features/deployments/detail/access-tab/channels-section.tsx +++ b/web/features/deployments/detail/access-tab/channels-section.tsx @@ -1,19 +1,15 @@ 'use client' -import type { WebAppAccessRow } from '@dify/contracts/enterprise/types.gen' import { Switch } from '@langgenius/dify-ui/switch' -import { useMutation } from '@tanstack/react-query' +import { useMutation, useQuery } from '@tanstack/react-query' import { useTranslation } from 'react-i18next' import { consoleQuery } from '@/service/client' import { environmentName, webappUrl } from '../../utils' import { CopyPill, EndpointRow, Section } from './common' +import { getUrlOrigin } from './url' type AccessChannelsSectionProps = { appId: string - runEnabled: boolean - webappRows: WebAppAccessRow[] - cliDomain?: string - cliDocsUrl?: string } function AccessChannelsSwitch({ appId, checked }: { @@ -37,12 +33,17 @@ function AccessChannelsSwitch({ appId, checked }: { export function AccessChannelsSection({ appId, - runEnabled, - webappRows, - cliDomain, - cliDocsUrl, }: AccessChannelsSectionProps) { const { t } = useTranslation('deployments') + const { data: accessConfig } = useQuery(consoleQuery.enterprise.appDeploy.getAppInstanceAccess.queryOptions({ + input: { + params: { appInstanceId: appId }, + }, + })) + const runEnabled = accessConfig?.accessChannels?.enabled ?? false + const webappRows = accessConfig?.accessChannels?.webappRows?.filter(row => row.url) ?? [] + const cliDomain = getUrlOrigin(accessConfig?.accessChannels?.cli?.url) + const cliDocsUrl = cliDomain ? `${cliDomain}/cli` : undefined return (
{ + if (!environment?.id) + return false + return environments.findIndex(candidate => candidate?.id === environment.id) === index + }) +} + +export function useAccessEnvironmentScope(appId: string) { + const appInput = { params: { appInstanceId: appId } } + const { data: accessConfig } = useQuery(consoleQuery.enterprise.appDeploy.getAppInstanceAccess.queryOptions({ + input: appInput, + })) + const { data: environmentDeployments } = useQuery(consoleQuery.enterprise.appDeploy.listRuntimeInstances.queryOptions({ + input: appInput, + })) + const deploymentRows = deployedRows(environmentDeployments?.data) + const policies = accessConfig?.permissions ?? EMPTY_ACCESS_PERMISSIONS + const environments = uniqueEnvironments([ + ...deploymentRows.map(row => row.environment), + ...policies.map(policy => policy.environment), + ...(accessConfig?.accessChannels?.webappRows?.map(row => row.environment) ?? []), + ]) + + return { + accessConfig, + environments, + policies, + } +} diff --git a/web/features/deployments/detail/overview-tab.tsx b/web/features/deployments/detail/overview-tab.tsx index 25e9a722e6..423299e331 100644 --- a/web/features/deployments/detail/overview-tab.tsx +++ b/web/features/deployments/detail/overview-tab.tsx @@ -101,12 +101,41 @@ function DeployFromOverviewButton({ appId }: { ) } -export function OverviewTab({ instanceId }: { - instanceId: string +function BasicInfoSection({ appId }: { + appId: string }) { const { t } = useTranslation('deployments') const { t: tCommon } = useTranslation() - const input = { params: { appInstanceId: instanceId } } + const { data: overview } = useQuery(consoleQuery.enterprise.appDeploy.getAppInstanceOverview.queryOptions({ + input: { + params: { appInstanceId: appId }, + }, + })) + const overviewApp = overview?.instance + + if (!overviewApp?.id) + return null + + const appName = overviewApp.name ?? overviewApp.id + const appModeLabel = getAppModeLabel(toAppMode(overviewApp.mode), tCommon) + + return ( +
+
+ + + + +
+
+ ) +} + +function DeploymentStatusSection({ appId }: { + appId: string +}) { + const { t } = useTranslation('deployments') + const input = { params: { appInstanceId: appId } } const { data: overview } = useQuery(consoleQuery.enterprise.appDeploy.getAppInstanceOverview.queryOptions({ input, })) @@ -119,9 +148,6 @@ export function OverviewTab({ instanceId }: { }, }, })) - const { data: accessConfig } = useQuery(consoleQuery.enterprise.appDeploy.getAppInstanceAccess.queryOptions({ - input, - })) const overviewApp = overview?.instance const deployments = overview?.deployments?.filter(row => row.environment?.id && row.status?.toLowerCase() !== 'undeployed') ?? [] const releaseRows = releaseHistory?.data?.filter(row => row.id) ?? [] @@ -130,112 +156,124 @@ export function OverviewTab({ instanceId }: { if (!overviewApp?.id) return null - const appId = overviewApp.id - const appName = overviewApp.name ?? appId - const appModeLabel = getAppModeLabel(toAppMode(overviewApp.mode), tCommon) + return ( +
}> + {t('overview.viewDeployments')} + + + )} + > + {deployments.length === 0 + ? ( +
+ +
+ {releaseRows.length === 0 + ? t(canCreateRelease ? 'overview.noReleaseYet' : 'overview.noReleaseSourceUnavailable') + : t('overview.notDeployedYet')} +
+ {releaseRows.length === 0 + ? canCreateRelease + ? ( + + ) + : ( + + ) + : ( + + )} +
+ ) + : ( +
+ {deployments.map((row) => { + const status = overviewDeploymentStatus(row.status) + return ( +
+
+ {row.environment?.name || row.environment?.id} + + {releaseLabel(row.release) || t('overview.emptyValue')} + +
+ +
+ ) + })} +
+ )} +
+ ) +} + +function AccessStatusSection({ appId }: { + appId: string +}) { + const { t } = useTranslation('deployments') + const input = { params: { appInstanceId: appId } } + const { data: overview } = useQuery(consoleQuery.enterprise.appDeploy.getAppInstanceOverview.queryOptions({ + input, + })) + const { data: accessConfig } = useQuery(consoleQuery.enterprise.appDeploy.getAppInstanceAccess.queryOptions({ + input, + })) const webappAccessUrl = webappUrl(overview?.access?.webappUrl) const cliUrl = overview?.access?.cliUrl const apiUrl = overview?.access?.apiUrl ?? accessConfig?.developerApi?.apiUrl const apiKeysCount = overview?.access?.apiKeyCount ?? accessConfig?.developerApi?.apiKeys?.length ?? 0 + return ( +
}> + {t('overview.configureAccess')} + + + )} + > +
+ + + +
+
+ ) +} + +export function OverviewTab({ instanceId: appId }: { + instanceId: string +}) { return (
-
-
- - - - -
-
- -
}> - {t('overview.viewDeployments')} - - - )} - > - {deployments.length === 0 - ? ( -
- -
- {releaseRows.length === 0 - ? t(canCreateRelease ? 'overview.noReleaseYet' : 'overview.noReleaseSourceUnavailable') - : t('overview.notDeployedYet')} -
- {releaseRows.length === 0 - ? canCreateRelease - ? ( - - ) - : ( - - ) - : ( - - )} -
- ) - : ( -
- {deployments.map((row) => { - const status = overviewDeploymentStatus(row.status) - return ( -
-
- {row.environment?.name || row.environment?.id} - - {releaseLabel(row.release) || t('overview.emptyValue')} - -
- -
- ) - })} -
- )} -
- -
}> - {t('overview.configureAccess')} - - - )} - > -
- - - -
-
+ + +
) } diff --git a/web/features/deployments/detail/settings-tab.tsx b/web/features/deployments/detail/settings-tab.tsx index d87552d781..d3278872e6 100644 --- a/web/features/deployments/detail/settings-tab.tsx +++ b/web/features/deployments/detail/settings-tab.tsx @@ -216,16 +216,13 @@ function SettingsForm({ app, settings }: SettingsFormProps) { ) } -export function SettingsTab({ instanceId }: { - instanceId: string +function SettingsFormSection({ appId }: { + appId: string }) { - const appInput = { params: { appInstanceId: instanceId } } + const appInput = { params: { appInstanceId: appId } } const { data: overview } = useQuery(consoleQuery.enterprise.appDeploy.getAppInstanceOverview.queryOptions({ input: appInput, })) - const { data: environmentDeployments } = useQuery(consoleQuery.enterprise.appDeploy.listRuntimeInstances.queryOptions({ - input: appInput, - })) const app = overview?.instance const settingsQuery = useQuery(consoleQuery.enterprise.appDeploy.getAppInstanceSettings.queryOptions({ input: appInput, @@ -234,22 +231,54 @@ export function SettingsTab({ instanceId }: { if (!app?.id) return null - const hasDeployments = deployedRows(environmentDeployments?.data).length > 0 const appName = app.name ?? app.id const formKey = `${app.id}-${settingsQuery.data?.name ?? appName}-${settingsQuery.data?.description ?? app.description ?? ''}` + return ( + + ) +} + +function DeleteInstanceControlSection({ appId }: { + appId: string +}) { + const appInput = { params: { appInstanceId: appId } } + const { data: overview } = useQuery(consoleQuery.enterprise.appDeploy.getAppInstanceOverview.queryOptions({ + input: appInput, + })) + const { data: environmentDeployments } = useQuery(consoleQuery.enterprise.appDeploy.listRuntimeInstances.queryOptions({ + input: appInput, + })) + const settingsQuery = useQuery(consoleQuery.enterprise.appDeploy.getAppInstanceSettings.queryOptions({ + input: appInput, + })) + const app = overview?.instance + + if (!app?.id) + return null + + const hasDeployments = deployedRows(environmentDeployments?.data).length > 0 + + return ( + + ) +} + +export function SettingsTab({ instanceId: appId }: { + instanceId: string +}) { return (
- - + +
) } diff --git a/web/features/deployments/detail/versions-tab.tsx b/web/features/deployments/detail/versions-tab.tsx index 83a68e5ffc..b2bbb60349 100644 --- a/web/features/deployments/detail/versions-tab.tsx +++ b/web/features/deployments/detail/versions-tab.tsx @@ -1,26 +1,15 @@ 'use client' import { Button } from '@langgenius/dify-ui/button' -import { cn } from '@langgenius/dify-ui/cn' import { Dialog, DialogCloseButton, DialogContent, DialogDescription, DialogTitle } from '@langgenius/dify-ui/dialog' import { toast } from '@langgenius/dify-ui/toast' -import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { useMutation, useQuery } from '@tanstack/react-query' import { useState } from 'react' import { useTranslation } from 'react-i18next' import Input from '@/app/components/base/input' import { consoleQuery } from '@/service/client' import { DEPLOYMENT_PAGE_SIZE } from '../data' -import { - deployedRows, - formatDate, - releaseCommit, - releaseLabel, -} from '../utils' -import { DeployReleaseMenu } from './versions-tab/deploy-release-menu' -import { DeployedToBadge } from './versions-tab/deployed-to-badge' -import { getReleaseDeployments } from './versions-tab/release-deployments' - -const GRID_TEMPLATE = 'grid-cols-[minmax(0,0.9fr)_minmax(0,1fr)_minmax(0,0.8fr)_minmax(0,1.5fr)_96px]' +import { deployedRows } from '../utils' +import { ReleaseHistoryTable } from './versions-tab/release-history-table' function CreateReleaseControl({ appId, canCreateRelease }: { appId: string @@ -212,104 +201,11 @@ export function VersionsTab({ instanceId: appId }: { ) : ( -
-
-
{t('versions.col.release')}
-
{t('versions.col.createdAt')}
-
{t('versions.col.author')}
-
{t('versions.col.deployedTo')}
-
{t('versions.col.action')}
-
- - {releaseRows.map((row) => { - const release = row - const releaseDeployments = getReleaseDeployments(row, deploymentRows) - return ( -
-
-
-
- - - {releaseLabel(release)} - - )} - /> - - {t('versions.commitTooltip', { commit: releaseCommit(release) })} - - -
- {formatDate(release.createdAt)} - · - {row.createdBy?.name ?? '—'} -
-
-
- -
-
-
-
- {t('versions.col.deployedTo')} -
-
- {releaseDeployments.length === 0 - ? - : releaseDeployments.map(item => ( - - ))} -
-
-
-
-
- - - {releaseLabel(release)} - - )} - /> - - {t('versions.commitTooltip', { commit: releaseCommit(release) })} - - -
-
{formatDate(release.createdAt)}
-
{row.createdBy?.name ?? '—'}
-
- {releaseDeployments.length === 0 - ? - : releaseDeployments.map(item => ( - - ))} -
-
- -
-
-
- ) - })} -
+ )} ) diff --git a/web/features/deployments/detail/versions-tab/release-history-table.tsx b/web/features/deployments/detail/versions-tab/release-history-table.tsx new file mode 100644 index 0000000000..0dc1d1aeda --- /dev/null +++ b/web/features/deployments/detail/versions-tab/release-history-table.tsx @@ -0,0 +1,125 @@ +'use client' + +import type { ReleaseRow, RuntimeInstanceRow } from '@dify/contracts/enterprise/types.gen' +import { cn } from '@langgenius/dify-ui/cn' +import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' +import { useTranslation } from 'react-i18next' +import { + formatDate, + releaseCommit, + releaseLabel, +} from '../../utils' +import { DeployReleaseMenu } from './deploy-release-menu' +import { DeployedToBadge } from './deployed-to-badge' +import { getReleaseDeployments } from './release-deployments' + +const GRID_TEMPLATE = 'grid-cols-[minmax(0,0.9fr)_minmax(0,1fr)_minmax(0,0.8fr)_minmax(0,1.5fr)_96px]' + +export function ReleaseHistoryTable({ appId, releaseRows, deploymentRows }: { + appId: string + releaseRows: ReleaseRow[] + deploymentRows: RuntimeInstanceRow[] +}) { + const { t } = useTranslation('deployments') + + return ( +
+
+
{t('versions.col.release')}
+
{t('versions.col.createdAt')}
+
{t('versions.col.author')}
+
{t('versions.col.deployedTo')}
+
{t('versions.col.action')}
+
+ + {releaseRows.map((row) => { + const release = row + const releaseDeployments = getReleaseDeployments(row, deploymentRows) + return ( +
+
+
+
+ + + {releaseLabel(release)} + + )} + /> + + {t('versions.commitTooltip', { commit: releaseCommit(release) })} + + +
+ {formatDate(release.createdAt)} + · + {row.createdBy?.name ?? '—'} +
+
+
+ +
+
+
+
+ {t('versions.col.deployedTo')} +
+
+ {releaseDeployments.length === 0 + ? + : releaseDeployments.map(item => ( + + ))} +
+
+
+
+
+ + + {releaseLabel(release)} + + )} + /> + + {t('versions.commitTooltip', { commit: releaseCommit(release) })} + + +
+
{formatDate(release.createdAt)}
+
{row.createdBy?.name ?? '—'}
+
+ {releaseDeployments.length === 0 + ? + : releaseDeployments.map(item => ( + + ))} +
+
+ +
+
+
+ ) + })} +
+ ) +}