From 7f2d094cf3a811b5ae1de5c14e8d3ac37d24aa11 Mon Sep 17 00:00:00 2001
From: Stephen Zhou <38493346+hyoban@users.noreply.github.com>
Date: Thu, 30 Apr 2026 10:44:03 +0800
Subject: [PATCH] tweaks
---
.../deployments/components/deploy-drawer.tsx | 38 +-
.../components/deploy-drawer/form.tsx | 1 -
.../deployments/components/rollback-modal.tsx | 58 ++-
web/features/deployments/data.ts | 348 +---------------
.../deployments/detail/access-tab.tsx | 33 +-
.../detail/access-tab/permissions.tsx | 2 -
.../deployments/detail/deploy-tab.tsx | 37 +-
web/features/deployments/detail/index.tsx | 54 ++-
.../deployments/detail/overview-tab.tsx | 38 +-
.../deployments/detail/settings-tab.tsx | 48 ++-
.../deployments/detail/versions-tab.tsx | 29 +-
.../versions-tab/deploy-release-menu.tsx | 35 +-
.../deployments/hooks/use-deployment-data.ts | 41 --
.../hooks/use-deployment-mutations.ts | 377 ++++++++++--------
.../deployments/hooks/use-source-apps.ts | 81 ----
web/features/deployments/list/index.tsx | 37 +-
.../deployments/list/instance-card.tsx | 87 +---
web/features/deployments/nav/index.tsx | 28 +-
web/features/deployments/utils.ts | 74 +++-
19 files changed, 630 insertions(+), 816 deletions(-)
delete mode 100644 web/features/deployments/hooks/use-deployment-data.ts
delete mode 100644 web/features/deployments/hooks/use-source-apps.ts
diff --git a/web/features/deployments/components/deploy-drawer.tsx b/web/features/deployments/components/deploy-drawer.tsx
index 2ea1ab97c4..0ba3ebfe30 100644
--- a/web/features/deployments/components/deploy-drawer.tsx
+++ b/web/features/deployments/components/deploy-drawer.tsx
@@ -2,11 +2,17 @@
import type { FC } from 'react'
import { Dialog, DialogCloseButton, DialogContent } from '@langgenius/dify-ui/dialog'
+import { skipToken, useQuery } from '@tanstack/react-query'
+import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
-import { useDeploymentAppData } from '../hooks/use-deployment-data'
+import { consoleQuery } from '@/service/client'
+import {
+ DEPLOYMENT_PAGE_SIZE,
+ SOURCE_APPS_PAGE_SIZE,
+} from '../data'
import { useStartDeployment } from '../hooks/use-deployment-mutations'
-import { useSourceApps } from '../hooks/use-source-apps'
import { useDeploymentsStore } from '../store'
+import { environmentOptionsFromList } from '../utils'
import { DeployForm } from './deploy-drawer/form'
const DeployDrawer: FC = () => {
@@ -16,13 +22,31 @@ const DeployDrawer: FC = () => {
const closeDeployDrawer = useDeploymentsStore(state => state.closeDeployDrawer)
const startDeploy = useStartDeployment()
const open = drawer.open
- const { environmentOptions } = useSourceApps({ enabled: open })
- const { data: appData } = useDeploymentAppData(drawerAppId, {
+ const listQuery = useQuery(consoleQuery.deployments.list.queryOptions({
+ input: {
+ query: {
+ pageNumber: 1,
+ resultsPerPage: SOURCE_APPS_PAGE_SIZE,
+ },
+ },
+ enabled: open,
+ }))
+ const { data: releaseHistory } = useQuery(consoleQuery.deployments.releaseHistory.queryOptions({
+ input: drawerAppId
+ ? {
+ params: { appInstanceId: drawerAppId },
+ query: {
+ pageNumber: 1,
+ resultsPerPage: DEPLOYMENT_PAGE_SIZE,
+ },
+ }
+ : skipToken,
enabled: open && Boolean(drawerAppId),
- })
+ }))
+ const environmentOptions = useMemo(() => environmentOptionsFromList(listQuery.data), [listQuery.data])
const environments = environmentOptions
- const releases = appData?.releaseHistory.data?.map(row => row.release ?? row).filter(release => release.id) ?? []
+ const releases = releaseHistory?.data?.map(row => row.release ?? row).filter(release => release.id) ?? []
const defaultReleaseId = releases[0]?.id
const formKey = `${drawer.appId ?? 'none'}-${drawer.environmentId ?? 'any'}-${drawer.releaseId ?? 'new'}-${open ? '1' : '0'}`
@@ -35,7 +59,7 @@ const DeployDrawer: FC = () => {
{!drawerAppId
?
{t('deployDrawer.notFound')}
- : !appData
+ : !releaseHistory
? (
diff --git a/web/features/deployments/components/deploy-drawer/form.tsx b/web/features/deployments/components/deploy-drawer/form.tsx
index 0dcb52afff..2eafcd9787 100644
--- a/web/features/deployments/components/deploy-drawer/form.tsx
+++ b/web/features/deployments/components/deploy-drawer/form.tsx
@@ -112,7 +112,6 @@ export const DeployForm: FC
= ({
},
}
: skipToken,
- staleTime: 30 * 1000,
}))
const previewBindings = releasePreview.data?.bindings ?? []
const modelBindings = previewBindings.filter(isRuntimeModelBinding)
diff --git a/web/features/deployments/components/rollback-modal.tsx b/web/features/deployments/components/rollback-modal.tsx
index b4dd227232..9d30c73097 100644
--- a/web/features/deployments/components/rollback-modal.tsx
+++ b/web/features/deployments/components/rollback-modal.tsx
@@ -9,19 +9,27 @@ import {
AlertDialogDescription,
AlertDialogTitle,
} from '@langgenius/dify-ui/alert-dialog'
+import { skipToken, useQuery } from '@tanstack/react-query'
+import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
-import { toAppInfoFromOverview } from '../data'
-import { useCachedDeploymentAppData } from '../hooks/use-deployment-data'
+import { consoleQuery } from '@/service/client'
+import {
+ DEPLOYMENT_PAGE_SIZE,
+ SOURCE_APPS_PAGE_SIZE,
+} from '../data'
import { useStartDeployment } from '../hooks/use-deployment-mutations'
-import { useSourceApps } from '../hooks/use-source-apps'
import { useDeploymentsStore } from '../store'
import {
activeRelease,
deployedRows,
environmentId,
environmentName,
+ environmentOptionsFromList,
releaseCommit,
releaseLabel,
+ sourceAppMapFromApps,
+ sourceAppsFromList,
+ toAppInfoFromOverview,
} from '../utils'
const InfoRow: FC<{ label: string, value: string }> = ({ label, value }) => {
@@ -36,20 +44,54 @@ const InfoRow: FC<{ label: string, value: string }> = ({ label, value }) => {
const RollbackModal: FC = () => {
const { t } = useTranslation('deployments')
const modal = useDeploymentsStore(state => state.rollbackModal)
- const { data: appData } = useCachedDeploymentAppData(modal.appId)
const closeRollbackModal = useDeploymentsStore(state => state.closeRollbackModal)
const rollbackDeployment = useStartDeployment()
- const { appMap, environmentOptions } = useSourceApps()
+ const appInput = modal.appId
+ ? { params: { appInstanceId: modal.appId } }
+ : undefined
+ const pagedInput = appInput
+ ? {
+ ...appInput,
+ query: {
+ pageNumber: 1,
+ resultsPerPage: DEPLOYMENT_PAGE_SIZE,
+ },
+ }
+ : undefined
+ const { data: overview } = useQuery(consoleQuery.deployments.overview.queryOptions({
+ input: appInput ?? skipToken,
+ enabled: modal.open && Boolean(modal.appId),
+ }))
+ const { data: environmentDeployments } = useQuery(consoleQuery.deployments.environmentDeployments.queryOptions({
+ input: pagedInput ?? skipToken,
+ enabled: modal.open && Boolean(modal.appId),
+ }))
+ const { data: releaseHistory } = useQuery(consoleQuery.deployments.releaseHistory.queryOptions({
+ input: pagedInput ?? skipToken,
+ enabled: modal.open && Boolean(modal.appId),
+ }))
+ const listQuery = useQuery(consoleQuery.deployments.list.queryOptions({
+ input: {
+ query: {
+ pageNumber: 1,
+ resultsPerPage: SOURCE_APPS_PAGE_SIZE,
+ },
+ },
+ enabled: modal.open,
+ }))
+ const sourceApps = useMemo(() => sourceAppsFromList(listQuery.data), [listQuery.data])
+ const appMap = useMemo(() => sourceAppMapFromApps(sourceApps), [sourceApps])
+ const environmentOptions = useMemo(() => environmentOptionsFromList(listQuery.data), [listQuery.data])
- const currentRow = deployedRows(appData?.environmentDeployments.data)
+ const currentRow = deployedRows(environmentDeployments?.data)
.find(row => environmentId(row.environment) === modal.environmentId)
const targetRelease = [
- ...(appData?.releaseHistory.data?.map(row => row.release ?? row).filter(release => !!release?.id) ?? []),
+ ...(releaseHistory?.data?.map(row => row.release ?? row).filter(release => !!release?.id) ?? []),
].find(release => release?.id === modal.targetReleaseId)
const currentRelease = activeRelease(currentRow)
const environment = currentRow?.environment
?? environmentOptions.find(env => env.id === modal.environmentId)
- const app = toAppInfoFromOverview(appData?.overview.instance)
+ const app = toAppInfoFromOverview(overview?.instance)
?? (modal.appId ? appMap.get(modal.appId) : undefined)
const confirm = () => {
diff --git a/web/features/deployments/data.ts b/web/features/deployments/data.ts
index bfa5a0adf6..0ba9317308 100644
--- a/web/features/deployments/data.ts
+++ b/web/features/deployments/data.ts
@@ -1,346 +1,2 @@
-import type { AppInfo, AppMode } from './types'
-import type {
- AccessSubject,
- AppDeploymentSummary,
- AppInstanceOverview,
- ConsoleReleaseSummary,
- CreateAppInstanceReply,
- GetAccessConfigReply,
- GetDeploymentOverviewReply,
- ListAppDeploymentsReply,
- ListEnvironmentDeploymentsReply,
- ListReleaseHistoryReply,
-} from '@/contract/console/deployments'
-import { queryOptions } from '@tanstack/react-query'
-import { consoleClient } from '@/service/client'
-
-const DEPLOYMENT_PAGE_SIZE = 100
-const DEPLOYMENT_APP_DATA_STALE_TIME = 30 * 1000
-const DEPLOYMENT_READINESS_RETRY_DELAYS = [0, 300, 700, 1200]
-
-export type DeploymentAppData = {
- appId: string
- overview: GetDeploymentOverviewReply
- environmentDeployments: ListEnvironmentDeploymentsReply
- releaseHistory: ListReleaseHistoryReply
- accessConfig: GetAccessConfigReply
-}
-
-export type CreateDeploymentParams = {
- appId: string
- environmentId: string
- releaseId?: string
- releaseNote?: string
-}
-
-export type CreateInstanceParams = {
- sourceAppId: string
- name: string
- description?: string
-}
-
-export type UpdateInstanceParams = {
- name: string
- description?: string
-}
-
-export type ListAppDeploymentsQuery = {
- environmentId?: string
- notDeployed?: boolean
- query?: string
- pageNumber?: number
- resultsPerPage?: number
-}
-
-export function toAppInfoFromSummary(summary: AppDeploymentSummary): AppInfo | undefined {
- if (!summary.id || !summary.name)
- return undefined
-
- return {
- id: summary.id,
- name: summary.name,
- mode: (summary.mode || 'workflow') as AppMode,
- iconType: 'emoji',
- icon: summary.icon,
- description: summary.description ?? undefined,
- sourceAppId: summary.sourceAppId,
- sourceAppName: summary.sourceAppName,
- }
-}
-
-export function toAppInfoFromOverview(instance?: AppInstanceOverview): AppInfo | undefined {
- if (!instance?.id)
- return undefined
-
- return {
- id: instance.id,
- name: instance.name ?? instance.id,
- mode: (instance.mode || 'workflow') as AppMode,
- iconType: 'emoji',
- icon: instance.icon,
- description: instance.description ?? undefined,
- sourceAppId: instance.sourceAppId,
- sourceAppName: instance.sourceAppName,
- }
-}
-
-export const deploymentAppDataQueryKey = (appId: string) => ['console', 'deployments', 'app-data', appId] as const
-
-export const listAppDeployments = async (query: ListAppDeploymentsQuery): Promise => {
- return consoleClient.deployments.list({
- query,
- })
-}
-
-export const fetchDeploymentAppData = async (appId: string): Promise => {
- const input = { params: { appInstanceId: appId } }
- const [
- overview,
- environmentDeployments,
- releaseHistory,
- accessConfig,
- ] = await Promise.all([
- consoleClient.deployments.overview(input),
- consoleClient.deployments.environmentDeployments({
- ...input,
- query: {
- pageNumber: 1,
- resultsPerPage: DEPLOYMENT_PAGE_SIZE,
- },
- }),
- consoleClient.deployments.releaseHistory({
- ...input,
- query: {
- pageNumber: 1,
- resultsPerPage: DEPLOYMENT_PAGE_SIZE,
- },
- }),
- consoleClient.deployments.accessConfig(input),
- ])
-
- return {
- appId,
- overview,
- environmentDeployments,
- releaseHistory,
- accessConfig,
- }
-}
-
-export const deploymentAppDataQueryOptions = (appId: string) =>
- queryOptions({
- queryKey: deploymentAppDataQueryKey(appId),
- queryFn: () => fetchDeploymentAppData(appId),
- staleTime: DEPLOYMENT_APP_DATA_STALE_TIME,
- })
-
-const wait = (delay: number) => new Promise(resolve => setTimeout(resolve, delay))
-
-export const refreshDeploymentAppDataWhenReady = async (appId: string): Promise => {
- let lastError: unknown
-
- for (const delay of DEPLOYMENT_READINESS_RETRY_DELAYS) {
- if (delay > 0)
- await wait(delay)
-
- try {
- return await fetchDeploymentAppData(appId)
- }
- catch (error) {
- lastError = error
- }
- }
-
- throw lastError
-}
-
-export const waitForAppInstanceInDeploymentList = async (appInstanceId: string): Promise => {
- let lastError: unknown
-
- for (const delay of DEPLOYMENT_READINESS_RETRY_DELAYS) {
- if (delay > 0)
- await wait(delay)
-
- try {
- const response = await listAppDeployments({
- pageNumber: 1,
- resultsPerPage: DEPLOYMENT_PAGE_SIZE,
- })
- if (response.data?.some(app => app.id === appInstanceId))
- return response
- }
- catch (error) {
- lastError = error
- }
- }
-
- if (lastError)
- throw lastError
-
- return undefined
-}
-
-export const createRelease = async (appId: string, releaseNote?: string): Promise => {
- const trimmedReleaseNote = releaseNote?.trim()
- const response = await consoleClient.deployments.createRelease({
- params: {
- appInstanceId: appId,
- },
- body: {
- name: trimmedReleaseNote || 'Release',
- description: trimmedReleaseNote || undefined,
- },
- })
- if (!response.release)
- throw new Error('Create release did not return a release.')
- return response.release
-}
-
-export const createDeployment = async ({
- appId,
- environmentId,
- releaseId,
- releaseNote,
-}: CreateDeploymentParams) => {
- let targetReleaseId = releaseId
- await consoleClient.deployments.previewRelease({
- params: {
- appInstanceId: appId,
- },
- body: {
- releaseId: targetReleaseId,
- },
- })
- if (!targetReleaseId) {
- const release = await createRelease(appId, releaseNote)
- targetReleaseId = release.id
- }
- if (!targetReleaseId)
- throw new Error('Failed to create a deployable release.')
-
- return consoleClient.deployments.createDeployment({
- params: {
- appInstanceId: appId,
- },
- body: {
- environmentId,
- releaseId: targetReleaseId,
- },
- })
-}
-
-export const cancelDeployment = async (appId: string, runtimeInstanceId: string) => {
- return consoleClient.deployments.cancelDeployment({
- params: {
- appInstanceId: appId,
- runtimeInstanceId,
- },
- })
-}
-
-export const undeployEnvironment = async (appId: string, runtimeInstanceId: string) => {
- return consoleClient.deployments.undeployEnvironment({
- params: {
- appInstanceId: appId,
- runtimeInstanceId,
- },
- })
-}
-
-export const createApiKey = async (appId: string, environmentId: string, name: string) => {
- return consoleClient.deployments.createEnvironmentAPIToken({
- params: {
- appInstanceId: appId,
- },
- body: {
- environmentId,
- name,
- },
- })
-}
-
-export const deleteApiKey = async (appId: string, apiKeyId: string) => {
- return consoleClient.deployments.deleteEnvironmentAPIToken({
- params: {
- appInstanceId: appId,
- apiKeyId,
- },
- })
-}
-
-export const patchAccessChannel = async (appId: string, enabled: boolean) => {
- return consoleClient.deployments.patchAccessChannel({
- params: {
- appInstanceId: appId,
- },
- body: {
- enabled,
- },
- })
-}
-
-export const patchDeveloperAPI = async (appId: string, enabled: boolean) => {
- return consoleClient.deployments.patchDeveloperAPI({
- params: {
- appInstanceId: appId,
- },
- body: {
- enabled,
- },
- })
-}
-
-export const updateEnvironmentAccessPolicy = async (
- appId: string,
- environmentId: string,
- accessMode: string,
- subjects: AccessSubject[] = [],
-) => {
- return consoleClient.deployments.updateEnvironmentAccessPolicy({
- params: {
- appInstanceId: appId,
- environmentId,
- },
- body: {
- accessMode,
- subjects,
- },
- })
-}
-
-export const createAppInstance = async ({
- sourceAppId,
- name,
- description,
-}: CreateInstanceParams): Promise => {
- return consoleClient.deployments.createInstance({
- body: {
- sourceAppId,
- name,
- description,
- },
- })
-}
-
-export const updateAppInstance = async (
- appId: string,
- { name, description }: UpdateInstanceParams,
-) => {
- return consoleClient.deployments.updateInstance({
- params: {
- appInstanceId: appId,
- },
- body: {
- name,
- description,
- },
- })
-}
-
-export const deleteAppInstance = async (appId: string) => {
- return consoleClient.deployments.deleteInstance({
- params: {
- appInstanceId: appId,
- },
- })
-}
+export const DEPLOYMENT_PAGE_SIZE = 100
+export const SOURCE_APPS_PAGE_SIZE = 100
diff --git a/web/features/deployments/detail/access-tab.tsx b/web/features/deployments/detail/access-tab.tsx
index b5fbf695f8..f3575ffdea 100644
--- a/web/features/deployments/detail/access-tab.tsx
+++ b/web/features/deployments/detail/access-tab.tsx
@@ -6,8 +6,10 @@ import type {
AccessSubject,
ConsoleEnvironmentSummary,
} from '@/contract/console/deployments'
+import { useQuery } from '@tanstack/react-query'
import { useMemo, useState } from 'react'
-import { useCachedDeploymentAppData } from '../hooks/use-deployment-data'
+import { consoleQuery } from '@/service/client'
+import { DEPLOYMENT_PAGE_SIZE } from '../data'
import {
useGenerateDeploymentApiKey,
useRevokeDeploymentApiKey,
@@ -37,7 +39,19 @@ type AccessTabProps = {
}
const AccessTab: FC = ({ instanceId: appId }) => {
- const { data: appData } = useCachedDeploymentAppData(appId)
+ const appInput = { params: { appInstanceId: appId } }
+ const { data: accessConfig } = useQuery(consoleQuery.deployments.accessConfig.queryOptions({
+ input: appInput,
+ }))
+ const { data: environmentDeployments } = useQuery(consoleQuery.deployments.environmentDeployments.queryOptions({
+ input: {
+ ...appInput,
+ query: {
+ pageNumber: 1,
+ resultsPerPage: DEPLOYMENT_PAGE_SIZE,
+ },
+ },
+ }))
const [createdApiToken, setCreatedApiToken] = useState<{
appId: string
token: string
@@ -47,10 +61,9 @@ const AccessTab: FC = ({ instanceId: appId }) => {
const toggleAccessChannel = useToggleDeploymentAccessChannel()
const setEnvironmentAccessPolicy = useSetEnvironmentAccessPolicy()
- const accessConfig = appData?.accessConfig
const deploymentRows = useMemo(
- () => deployedRows(appData?.environmentDeployments.data),
- [appData?.environmentDeployments.data],
+ () => deployedRows(environmentDeployments?.data),
+ [environmentDeployments?.data],
)
const policies = accessConfig?.permissions ?? EMPTY_ACCESS_PERMISSIONS
const deployedEnvs = useMemo(
@@ -63,9 +76,17 @@ const AccessTab: FC = ({ instanceId: appId }) => {
)
const apiEnabled = accessConfig?.developerApi?.enabled ?? false
const apiKeys = accessConfig?.developerApi?.apiKeys ?? []
+ const createApiKeyLabel = (environmentId: string) => {
+ const existingCount = apiKeys.filter(key =>
+ (key.environmentId ?? key.environment?.id) === environmentId,
+ ).length
+ const name = deployedEnvs.find(env => env.id === environmentId)?.name ?? 'env'
+
+ return `${name}-key-${String(existingCount + 1).padStart(3, '0')}`
+ }
const handleGenerateApiKey = (environmentId: string) => {
generateApiKey.mutate(
- { appId, environmentId },
+ { appId, environmentId, name: createApiKeyLabel(environmentId) },
{
onSuccess: (response) => {
if (response.apiToken?.token)
diff --git a/web/features/deployments/detail/access-tab/permissions.tsx b/web/features/deployments/detail/access-tab/permissions.tsx
index 26d6afac2e..1248299520 100644
--- a/web/features/deployments/detail/access-tab/permissions.tsx
+++ b/web/features/deployments/detail/access-tab/permissions.tsx
@@ -200,7 +200,6 @@ const SubjectPicker: FC = ({
},
}
: skipToken,
- staleTime: 30 * 1000,
}))
const subjects = useMemo(
() => subjectsQuery.data?.data
@@ -326,7 +325,6 @@ export const EnvironmentPermissionRow: FC = ({
},
}
: skipToken,
- staleTime: 30 * 1000,
}))
const detailPolicy = policyQuery.data?.policy
const policyKind = accessModeToPermissionKey(detailPolicy?.accessMode ?? summaryPolicy?.accessMode)
diff --git a/web/features/deployments/detail/deploy-tab.tsx b/web/features/deployments/detail/deploy-tab.tsx
index a5858ba091..94c07a0aa5 100644
--- a/web/features/deployments/detail/deploy-tab.tsx
+++ b/web/features/deployments/detail/deploy-tab.tsx
@@ -8,11 +8,15 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from '@langgenius/dify-ui/dropdown-menu'
+import { useQuery } from '@tanstack/react-query'
import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { useCachedDeploymentAppData } from '../hooks/use-deployment-data'
+import { consoleQuery } from '@/service/client'
+import {
+ DEPLOYMENT_PAGE_SIZE,
+ SOURCE_APPS_PAGE_SIZE,
+} from '../data'
import { useUndeployDeployment } from '../hooks/use-deployment-mutations'
-import { useSourceApps } from '../hooks/use-source-apps'
import { useDeploymentsStore } from '../store'
import {
activeRelease,
@@ -23,6 +27,7 @@ import {
environmentId,
environmentMode,
environmentName,
+ environmentOptionsFromList,
isUndeployedDeploymentRow,
releaseCommit,
releaseLabel,
@@ -38,18 +43,34 @@ type DeployTabProps = {
const DeployTab: FC = ({ instanceId: appId }) => {
const { t } = useTranslation('deployments')
- const { data: appData } = useCachedDeploymentAppData(appId)
+ const { data: environmentDeployments } = useQuery(consoleQuery.deployments.environmentDeployments.queryOptions({
+ input: {
+ params: { appInstanceId: appId },
+ query: {
+ pageNumber: 1,
+ resultsPerPage: DEPLOYMENT_PAGE_SIZE,
+ },
+ },
+ }))
const openDeployDrawer = useDeploymentsStore(state => state.openDeployDrawer)
const undeployDeployment = useUndeployDeployment()
- const { environmentOptions } = useSourceApps()
+ const listQuery = useQuery(consoleQuery.deployments.list.queryOptions({
+ input: {
+ query: {
+ pageNumber: 1,
+ resultsPerPage: SOURCE_APPS_PAGE_SIZE,
+ },
+ },
+ }))
+ const environmentOptions = useMemo(() => environmentOptionsFromList(listQuery.data), [listQuery.data])
const rows = useMemo(
- () => appData?.environmentDeployments.data?.filter(row => row.environment?.id) ?? [],
- [appData?.environmentDeployments.data],
+ () => environmentDeployments?.data?.filter(row => row.environment?.id) ?? [],
+ [environmentDeployments?.data],
)
const deployedRuntimeRows = useMemo(
- () => deployedRows(appData?.environmentDeployments.data),
- [appData?.environmentDeployments.data],
+ () => deployedRows(environmentDeployments?.data),
+ [environmentDeployments?.data],
)
const deployedEnvIds = new Set(deployedRuntimeRows.map(row => environmentId(row.environment)))
diff --git a/web/features/deployments/detail/index.tsx b/web/features/deployments/detail/index.tsx
index 96bb7a54bd..87d8829895 100644
--- a/web/features/deployments/detail/index.tsx
+++ b/web/features/deployments/detail/index.tsx
@@ -3,17 +3,23 @@
import type { FC, ReactNode } from 'react'
import type { InstanceDetailTabKey } from './tabs'
import { Button } from '@langgenius/dify-ui/button'
+import { useQuery } from '@tanstack/react-query'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { getAppModeLabel } from '@/app/components/app-sidebar/app-info/app-mode-labels'
import useDocumentTitle from '@/hooks/use-document-title'
import { useRouter, useSelectedLayoutSegment } from '@/next/navigation'
+import { consoleQuery } from '@/service/client'
import DeployDrawer from '../components/deploy-drawer'
import RollbackModal from '../components/rollback-modal'
-import { toAppInfoFromOverview } from '../data'
-import { useDeploymentAppData } from '../hooks/use-deployment-data'
-import { useSourceApps } from '../hooks/use-source-apps'
-import { deployedRows, deploymentStatus } from '../utils'
+import {
+ SOURCE_APPS_PAGE_SIZE,
+} from '../data'
+import {
+ deploymentSummariesFromList,
+ sourceAppMapFromApps,
+ sourceAppsFromList,
+} from '../utils'
import { DeploymentSidebar } from './deployment-sidebar'
import { isInstanceDetailTabKey } from './tabs'
@@ -29,25 +35,31 @@ const InstanceDetail: FC = ({ instanceId, children }) => {
const selectedSegment = useSelectedLayoutSegment()
const selectedTab = selectedSegment ?? undefined
const activeTab: InstanceDetailTabKey = isInstanceDetailTabKey(selectedTab) ? selectedTab : 'overview'
- const detailQuery = useDeploymentAppData(instanceId, { enabled: Boolean(instanceId) })
- const appData = detailQuery.data
- const { appMap, isLoading: isLoadingApps } = useSourceApps()
+ const listQuery = useQuery(consoleQuery.deployments.list.queryOptions({
+ input: {
+ query: {
+ pageNumber: 1,
+ resultsPerPage: SOURCE_APPS_PAGE_SIZE,
+ },
+ },
+ }))
useDocumentTitle(t('documentTitle.detail'))
- const detailApp = useMemo(
- () => toAppInfoFromOverview(appData?.overview.instance),
- [appData?.overview.instance],
- )
+ const apps = useMemo(() => sourceAppsFromList(listQuery.data), [listQuery.data])
+ const appMap = useMemo(() => sourceAppMapFromApps(apps), [apps])
+ const summaries = useMemo(() => deploymentSummariesFromList(listQuery.data), [listQuery.data])
const app = useMemo(
- () => detailApp ?? appMap.get(instanceId),
- [detailApp, instanceId, appMap],
- )
- const appDeployments = useMemo(
- () => deployedRows(appData?.environmentDeployments.data),
- [appData?.environmentDeployments.data],
+ () => appMap.get(instanceId),
+ [instanceId, appMap],
)
+ const summary = summaries[instanceId]
+ const statusCount = (status: string) =>
+ summary?.statuses?.find(item => item.status === status)?.count ?? 0
+ const envCount = summary?.statuses
+ ?.filter(item => item.status !== 'undeployed')
+ .reduce((total, item) => total + (item.count ?? 0), 0) ?? 0
- if (!app && (isLoadingApps || detailQuery.isLoading || detailQuery.isFetching)) {
+ if (!app && listQuery.isLoading) {
return (
@@ -67,8 +79,8 @@ const InstanceDetail: FC
= ({ instanceId, children }) => {
)
}
- const deployingCount = appDeployments.filter(row => deploymentStatus(row) === 'deploying').length
- const failedCount = appDeployments.filter(row => deploymentStatus(row) === 'deploy_failed').length
+ const deployingCount = statusCount('deploying')
+ const failedCount = statusCount('failed') + statusCount('deploy_failed')
const appModeLabel = app ? getAppModeLabel(app.mode, tCommon) : t('detail.sourceAppDeleted')
return (
@@ -92,7 +104,7 @@ const InstanceDetail: FC = ({ instanceId, children }) => {
{t(`tabs.${activeTab}.description`)}
-
{t('detail.envCount', { count: appDeployments.length })}
+
{t('detail.envCount', { count: envCount })}
{deployingCount > 0 && (
<>
·
diff --git a/web/features/deployments/detail/overview-tab.tsx b/web/features/deployments/detail/overview-tab.tsx
index 4401d50ea0..cb9695ac04 100644
--- a/web/features/deployments/detail/overview-tab.tsx
+++ b/web/features/deployments/detail/overview-tab.tsx
@@ -2,16 +2,24 @@
import type { FC, ReactNode } from 'react'
import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { useQuery } from '@tanstack/react-query'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { getAppModeLabel } from '@/app/components/app-sidebar/app-info/app-mode-labels'
import { useRouter } from '@/next/navigation'
+import { consoleQuery } from '@/service/client'
import { StatusBadge } from '../components/status-badge'
-import { toAppInfoFromOverview } from '../data'
-import { useCachedDeploymentAppData } from '../hooks/use-deployment-data'
-import { useSourceApps } from '../hooks/use-source-apps'
+import {
+ SOURCE_APPS_PAGE_SIZE,
+} from '../data'
import { useDeploymentsStore } from '../store'
-import { releaseLabel, webappUrl } from '../utils'
+import {
+ releaseLabel,
+ sourceAppMapFromApps,
+ sourceAppsFromList,
+ toAppInfoFromOverview,
+ webappUrl,
+} from '../utils'
type OverviewTabProps = {
instanceId: string
@@ -92,10 +100,24 @@ const OverviewTab: FC
= ({ instanceId }) => {
const { t } = useTranslation('deployments')
const { t: tCommon } = useTranslation()
const router = useRouter()
- const { data: appData } = useCachedDeploymentAppData(instanceId)
+ const input = { params: { appInstanceId: instanceId } }
+ const { data: overview } = useQuery(consoleQuery.deployments.overview.queryOptions({
+ input,
+ }))
+ const { data: accessConfig } = useQuery(consoleQuery.deployments.accessConfig.queryOptions({
+ input,
+ }))
+ const listQuery = useQuery(consoleQuery.deployments.list.queryOptions({
+ input: {
+ query: {
+ pageNumber: 1,
+ resultsPerPage: SOURCE_APPS_PAGE_SIZE,
+ },
+ },
+ }))
const openDeployDrawer = useDeploymentsStore(state => state.openDeployDrawer)
- const { appMap } = useSourceApps()
- const overview = appData?.overview
+ const sourceApps = useMemo(() => sourceAppsFromList(listQuery.data), [listQuery.data])
+ const appMap = useMemo(() => sourceAppMapFromApps(sourceApps), [sourceApps])
const app = toAppInfoFromOverview(overview?.instance) ?? appMap.get(instanceId)
const overviewApp = overview?.instance
const deployments = useMemo(
@@ -113,7 +135,7 @@ const OverviewTab: FC = ({ instanceId }) => {
const appModeLabel = getAppModeLabel(overviewApp?.mode ?? app.mode, tCommon)
const webappAccessUrl = webappUrl(overview?.access?.webappUrl)
const cliUrl = overview?.access?.cliUrl
- const apiKeysCount = overview?.access?.apiKeyCount ?? appData?.accessConfig.developerApi?.apiKeys?.length ?? 0
+ const apiKeysCount = overview?.access?.apiKeyCount ?? accessConfig?.developerApi?.apiKeys?.length ?? 0
return (
diff --git a/web/features/deployments/detail/settings-tab.tsx b/web/features/deployments/detail/settings-tab.tsx
index 8764856220..0d9cc8466d 100644
--- a/web/features/deployments/detail/settings-tab.tsx
+++ b/web/features/deployments/detail/settings-tab.tsx
@@ -14,18 +14,24 @@ import {
import { Button } from '@langgenius/dify-ui/button'
import { toast } from '@langgenius/dify-ui/toast'
import { useQuery } from '@tanstack/react-query'
-import { useState } from 'react'
+import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useRouter } from '@/next/navigation'
import { consoleQuery } from '@/service/client'
-import { toAppInfoFromOverview } from '../data'
-import { useCachedDeploymentAppData } from '../hooks/use-deployment-data'
+import {
+ DEPLOYMENT_PAGE_SIZE,
+ SOURCE_APPS_PAGE_SIZE,
+} from '../data'
import {
useDeleteDeploymentInstance,
useUpdateDeploymentInstance,
} from '../hooks/use-deployment-mutations'
-import { useSourceApps } from '../hooks/use-source-apps'
-import { deployedRows } from '../utils'
+import {
+ deployedRows,
+ sourceAppMapFromApps,
+ sourceAppsFromList,
+ toAppInfoFromOverview,
+} from '../utils'
type SettingsTabProps = {
instanceId: string
@@ -181,24 +187,40 @@ const SettingsForm: FC
= ({ app, settings, hasDeployments, on
const SettingsTab: FC = ({ instanceId }) => {
const router = useRouter()
- const { data: appData } = useCachedDeploymentAppData(instanceId)
const updateInstance = useUpdateDeploymentInstance()
const deleteInstance = useDeleteDeploymentInstance()
- const { appMap } = useSourceApps()
- const app = toAppInfoFromOverview(appData?.overview.instance) ?? appMap.get(instanceId)
- const settingsQuery = useQuery(consoleQuery.deployments.settings.queryOptions({
+ const appInput = { params: { appInstanceId: instanceId } }
+ const listQuery = useQuery(consoleQuery.deployments.list.queryOptions({
input: {
- params: {
- appInstanceId: instanceId,
+ query: {
+ pageNumber: 1,
+ resultsPerPage: SOURCE_APPS_PAGE_SIZE,
},
},
- staleTime: 30 * 1000,
+ }))
+ const { data: overview } = useQuery(consoleQuery.deployments.overview.queryOptions({
+ input: appInput,
+ }))
+ const { data: environmentDeployments } = useQuery(consoleQuery.deployments.environmentDeployments.queryOptions({
+ input: {
+ ...appInput,
+ query: {
+ pageNumber: 1,
+ resultsPerPage: DEPLOYMENT_PAGE_SIZE,
+ },
+ },
+ }))
+ const sourceApps = useMemo(() => sourceAppsFromList(listQuery.data), [listQuery.data])
+ const appMap = useMemo(() => sourceAppMapFromApps(sourceApps), [sourceApps])
+ const app = toAppInfoFromOverview(overview?.instance) ?? appMap.get(instanceId)
+ const settingsQuery = useQuery(consoleQuery.deployments.settings.queryOptions({
+ input: appInput,
}))
if (!app)
return null
- const hasDeployments = deployedRows(appData?.environmentDeployments.data).length > 0
+ const hasDeployments = deployedRows(environmentDeployments?.data).length > 0
const formKey = `${app.id}-${settingsQuery.data?.name ?? app.name}-${settingsQuery.data?.description ?? app.description ?? ''}`
return (
diff --git a/web/features/deployments/detail/versions-tab.tsx b/web/features/deployments/detail/versions-tab.tsx
index 6b9d832a9b..db9b64e393 100644
--- a/web/features/deployments/detail/versions-tab.tsx
+++ b/web/features/deployments/detail/versions-tab.tsx
@@ -2,9 +2,11 @@
import type { FC } from 'react'
import { cn } from '@langgenius/dify-ui/cn'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
+import { useQuery } from '@tanstack/react-query'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
-import { useCachedDeploymentAppData } from '../hooks/use-deployment-data'
+import { consoleQuery } from '@/service/client'
+import { DEPLOYMENT_PAGE_SIZE } from '../data'
import {
deployedRows,
formatDate,
@@ -23,14 +25,29 @@ type VersionsTabProps = {
const VersionsTab: FC = ({ instanceId: appId }) => {
const { t } = useTranslation('deployments')
- const { data: appData } = useCachedDeploymentAppData(appId)
+ const query = {
+ pageNumber: 1,
+ resultsPerPage: DEPLOYMENT_PAGE_SIZE,
+ }
+ const { data: releaseHistory } = useQuery(consoleQuery.deployments.releaseHistory.queryOptions({
+ input: {
+ params: { appInstanceId: appId },
+ query,
+ },
+ }))
+ const { data: environmentDeployments } = useQuery(consoleQuery.deployments.environmentDeployments.queryOptions({
+ input: {
+ params: { appInstanceId: appId },
+ query,
+ },
+ }))
const releaseRows = useMemo(
- () => appData?.releaseHistory.data?.filter(row => (row.release ?? row).id) ?? [],
- [appData?.releaseHistory.data],
+ () => releaseHistory?.data?.filter(row => (row.release ?? row).id) ?? [],
+ [releaseHistory?.data],
)
const deploymentRows = useMemo(
- () => deployedRows(appData?.environmentDeployments.data),
- [appData?.environmentDeployments.data],
+ () => deployedRows(environmentDeployments?.data),
+ [environmentDeployments?.data],
)
return (
diff --git a/web/features/deployments/detail/versions-tab/deploy-release-menu.tsx b/web/features/deployments/detail/versions-tab/deploy-release-menu.tsx
index 2d2a94e469..e06f6ea3db 100644
--- a/web/features/deployments/detail/versions-tab/deploy-release-menu.tsx
+++ b/web/features/deployments/detail/versions-tab/deploy-release-menu.tsx
@@ -8,10 +8,14 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from '@langgenius/dify-ui/dropdown-menu'
-import { useState } from 'react'
+import { useQuery } from '@tanstack/react-query'
+import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { useCachedDeploymentAppData } from '../../hooks/use-deployment-data'
-import { useSourceApps } from '../../hooks/use-source-apps'
+import { consoleQuery } from '@/service/client'
+import {
+ DEPLOYMENT_PAGE_SIZE,
+ SOURCE_APPS_PAGE_SIZE,
+} from '../../data'
import { useDeploymentsStore } from '../../store'
import {
activeRelease,
@@ -20,6 +24,7 @@ import {
deploymentStatus,
environmentId,
environmentName,
+ environmentOptionsFromList,
} from '../../utils'
type DeployReleaseMenuProps = {
@@ -29,14 +34,32 @@ type DeployReleaseMenuProps = {
export const DeployReleaseMenu: FC = ({ appId, releaseId }) => {
const { t } = useTranslation('deployments')
- const { data: appData } = useCachedDeploymentAppData(appId)
const openDeployDrawer = useDeploymentsStore(state => state.openDeployDrawer)
const openRollbackModal = useDeploymentsStore(state => state.openRollbackModal)
const [open, setOpen] = useState(false)
- const { environmentOptions } = useSourceApps({ enabled: open })
+ const listQuery = useQuery(consoleQuery.deployments.list.queryOptions({
+ input: {
+ query: {
+ pageNumber: 1,
+ resultsPerPage: SOURCE_APPS_PAGE_SIZE,
+ },
+ },
+ enabled: open,
+ }))
+ const { data: environmentDeployments } = useQuery(consoleQuery.deployments.environmentDeployments.queryOptions({
+ input: {
+ params: { appInstanceId: appId },
+ query: {
+ pageNumber: 1,
+ resultsPerPage: DEPLOYMENT_PAGE_SIZE,
+ },
+ },
+ enabled: open,
+ }))
+ const environmentOptions = useMemo(() => environmentOptionsFromList(listQuery.data), [listQuery.data])
const environments = environmentOptions.filter(env => env.id)
- const deploymentRows = deployedRows(appData?.environmentDeployments.data)
+ const deploymentRows = deployedRows(environmentDeployments?.data)
return (
diff --git a/web/features/deployments/hooks/use-deployment-data.ts b/web/features/deployments/hooks/use-deployment-data.ts
deleted file mode 100644
index 6edc4ff9d7..0000000000
--- a/web/features/deployments/hooks/use-deployment-data.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-'use client'
-
-import { useQuery } from '@tanstack/react-query'
-import { useMemo } from 'react'
-import {
- deploymentAppDataQueryOptions,
- toAppInfoFromOverview,
-} from '../data'
-
-type UseDeploymentDataOptions = {
- enabled?: boolean
-}
-
-export function useDeploymentAppData(appId?: string, options: UseDeploymentDataOptions = {}) {
- const { enabled = true } = options
-
- return useQuery({
- ...deploymentAppDataQueryOptions(appId ?? ''),
- enabled: enabled && Boolean(appId),
- })
-}
-
-export function useCachedDeploymentAppData(appId?: string) {
- return useQuery({
- ...deploymentAppDataQueryOptions(appId ?? ''),
- enabled: false,
- })
-}
-
-export function useDeploymentAppInfo(appId?: string, options: UseDeploymentDataOptions = {}) {
- const query = useDeploymentAppData(appId, options)
- const app = useMemo(
- () => toAppInfoFromOverview(query.data?.overview.instance),
- [query.data?.overview.instance],
- )
-
- return {
- ...query,
- data: app,
- }
-}
diff --git a/web/features/deployments/hooks/use-deployment-mutations.ts b/web/features/deployments/hooks/use-deployment-mutations.ts
index 90d5625939..3559259cfe 100644
--- a/web/features/deployments/hooks/use-deployment-mutations.ts
+++ b/web/features/deployments/hooks/use-deployment-mutations.ts
@@ -1,35 +1,34 @@
'use client'
import type {
- CreateDeploymentParams,
- CreateInstanceParams,
- DeploymentAppData,
- UpdateInstanceParams,
-} from '../data'
-import type { AccessSubject, ConsoleReleaseSummary } from '@/contract/console/deployments'
+ AccessSubject,
+ ConsoleReleaseSummary,
+} from '@/contract/console/deployments'
import { useMutation, useQueryClient } from '@tanstack/react-query'
-import { consoleQuery } from '@/service/client'
-import {
- cancelDeployment,
- createApiKey,
- createAppInstance,
- createDeployment,
- deleteApiKey,
- deleteAppInstance,
- deploymentAppDataQueryKey,
- patchAccessChannel,
- patchDeveloperAPI,
- refreshDeploymentAppDataWhenReady,
- undeployEnvironment,
- updateAppInstance,
- updateEnvironmentAccessPolicy,
- waitForAppInstanceInDeploymentList,
-} from '../data'
+import { consoleClient, consoleQuery } from '@/service/client'
+import { DEPLOYMENT_PAGE_SIZE } from '../data'
export type CreateDeploymentInstanceResult = {
appInstanceId: string
initialRelease?: ConsoleReleaseSummary
- appData?: DeploymentAppData
+}
+
+type CreateDeploymentParams = {
+ appId: string
+ environmentId: string
+ releaseId?: string
+ releaseNote?: string
+}
+
+type CreateInstanceParams = {
+ sourceAppId: string
+ name: string
+ description?: string
+}
+
+type UpdateInstanceParams = {
+ name: string
+ description?: string
}
type UpdateDeploymentInstanceParams = {
@@ -45,9 +44,12 @@ type UndeployDeploymentParams = {
type GenerateApiKeyParams = {
appId: string
environmentId: string
+ name: string
}
-type RevokeApiKeyParams = GenerateApiKeyParams & {
+type RevokeApiKeyParams = {
+ appId: string
+ environmentId: string
apiKeyId: string
}
@@ -64,22 +66,9 @@ type SetEnvironmentAccessPolicyParams = {
subjects: AccessSubject[]
}
-const createApiKeyLabel = (
- appData: DeploymentAppData | undefined,
- environmentId: string,
-) => {
- const existingCount = appData?.accessConfig.developerApi?.apiKeys?.filter(key =>
- (key.environmentId ?? key.environment?.id) === environmentId,
- ).length ?? 0
- const environmentName = appData
- ?.environmentDeployments
- .data
- ?.find(row => row.environment?.id === environmentId)
- ?.environment
- ?.name ?? 'env'
+const DEPLOYMENT_READINESS_RETRY_DELAYS = [0, 300, 700, 1200]
- return `${environmentName}-key-${String(existingCount + 1).padStart(3, '0')}`
-}
+const wait = (delay: number) => new Promise(resolve => setTimeout(resolve, delay))
export const useCreateDeploymentInstance = () => {
const queryClient = useQueryClient()
@@ -87,37 +76,39 @@ export const useCreateDeploymentInstance = () => {
return useMutation({
mutationKey: consoleQuery.deployments.createInstance.mutationKey(),
mutationFn: async (params: CreateInstanceParams): Promise => {
- const response = await createAppInstance(params)
+ const response = await consoleClient.deployments.createInstance({
+ body: {
+ sourceAppId: params.sourceAppId,
+ name: params.name,
+ description: params.description,
+ },
+ })
if (!response.appInstanceId)
throw new Error('Create app instance did not return an appInstanceId.')
- const [appData] = await Promise.all([
- refreshDeploymentAppDataWhenReady(response.appInstanceId).catch(() => undefined),
- waitForAppInstanceInDeploymentList(response.appInstanceId).catch(() => undefined),
- ])
+ for (const delay of DEPLOYMENT_READINESS_RETRY_DELAYS) {
+ if (delay > 0)
+ await wait(delay)
+
+ const listResponse = await consoleClient.deployments.list({
+ query: {
+ pageNumber: 1,
+ resultsPerPage: DEPLOYMENT_PAGE_SIZE,
+ },
+ }).catch(() => undefined)
+ if (listResponse?.data?.some(app => app.id === response.appInstanceId))
+ break
+ }
return {
appInstanceId: response.appInstanceId,
initialRelease: response.initialRelease,
- appData,
}
},
- onSuccess: async (result) => {
- if (result.appData) {
- queryClient.setQueryData(
- deploymentAppDataQueryKey(result.appInstanceId),
- result.appData,
- )
- }
-
- await Promise.all([
- queryClient.invalidateQueries({
- queryKey: consoleQuery.deployments.list.key(),
- }),
- queryClient.invalidateQueries({
- queryKey: deploymentAppDataQueryKey(result.appInstanceId),
- }),
- ])
+ onSuccess: async () => {
+ await queryClient.invalidateQueries({
+ queryKey: consoleQuery.deployments.key(),
+ })
},
})
}
@@ -128,25 +119,19 @@ export const useUpdateDeploymentInstance = () => {
return useMutation({
mutationKey: consoleQuery.deployments.updateInstance.mutationKey(),
mutationFn: ({ appId, ...patch }: UpdateDeploymentInstanceParams) =>
- updateAppInstance(appId, patch),
- onSuccess: async (_data, variables) => {
- await Promise.all([
- queryClient.invalidateQueries({
- queryKey: consoleQuery.deployments.list.key(),
- }),
- queryClient.invalidateQueries({
- queryKey: deploymentAppDataQueryKey(variables.appId),
- }),
- queryClient.invalidateQueries({
- queryKey: consoleQuery.deployments.settings.queryKey({
- input: {
- params: {
- appInstanceId: variables.appId,
- },
- },
- }),
- }),
- ])
+ consoleClient.deployments.updateInstance({
+ params: {
+ appInstanceId: appId,
+ },
+ body: {
+ name: patch.name,
+ description: patch.description,
+ },
+ }),
+ onSuccess: async () => {
+ await queryClient.invalidateQueries({
+ queryKey: consoleQuery.deployments.key(),
+ })
},
})
}
@@ -156,22 +141,14 @@ export const useDeleteDeploymentInstance = () => {
return useMutation({
mutationKey: consoleQuery.deployments.deleteInstance.mutationKey(),
- mutationFn: (appId: string) => deleteAppInstance(appId),
- onSuccess: async (_data, appId) => {
- queryClient.removeQueries({
- queryKey: deploymentAppDataQueryKey(appId),
- })
- queryClient.removeQueries({
- queryKey: consoleQuery.deployments.settings.queryKey({
- input: {
- params: {
- appInstanceId: appId,
- },
- },
- }),
- })
+ mutationFn: (appId: string) => consoleClient.deployments.deleteInstance({
+ params: {
+ appInstanceId: appId,
+ },
+ }),
+ onSuccess: async () => {
await queryClient.invalidateQueries({
- queryKey: consoleQuery.deployments.list.key(),
+ queryKey: consoleQuery.deployments.key(),
})
},
})
@@ -182,16 +159,55 @@ export const useStartDeployment = () => {
return useMutation({
mutationKey: consoleQuery.deployments.createDeployment.mutationKey(),
- mutationFn: (params: CreateDeploymentParams) => createDeployment(params),
- onSuccess: async (_data, variables) => {
- await Promise.all([
- queryClient.invalidateQueries({
- queryKey: consoleQuery.deployments.list.key(),
- }),
- queryClient.invalidateQueries({
- queryKey: deploymentAppDataQueryKey(variables.appId),
- }),
- ])
+ mutationFn: async ({
+ appId,
+ environmentId,
+ releaseId,
+ releaseNote,
+ }: CreateDeploymentParams) => {
+ let targetReleaseId = releaseId
+ await consoleClient.deployments.previewRelease({
+ params: {
+ appInstanceId: appId,
+ },
+ body: {
+ releaseId: targetReleaseId,
+ },
+ })
+
+ if (!targetReleaseId) {
+ const trimmedReleaseNote = releaseNote?.trim()
+ const response = await consoleClient.deployments.createRelease({
+ params: {
+ appInstanceId: appId,
+ },
+ body: {
+ name: trimmedReleaseNote || 'Release',
+ description: trimmedReleaseNote || undefined,
+ },
+ })
+ if (!response.release)
+ throw new Error('Create release did not return a release.')
+ targetReleaseId = response.release.id
+ }
+
+ if (!targetReleaseId)
+ throw new Error('Failed to create a deployable release.')
+
+ return consoleClient.deployments.createDeployment({
+ params: {
+ appInstanceId: appId,
+ },
+ body: {
+ environmentId,
+ releaseId: targetReleaseId,
+ },
+ })
+ },
+ onSuccess: async () => {
+ await queryClient.invalidateQueries({
+ queryKey: consoleQuery.deployments.key(),
+ })
},
})
}
@@ -204,19 +220,25 @@ export const useUndeployDeployment = () => {
mutationFn: ({ appId, runtimeInstanceId, isDeploying }: UndeployDeploymentParams) => {
if (!runtimeInstanceId)
return Promise.resolve(undefined)
- if (isDeploying)
- return cancelDeployment(appId, runtimeInstanceId)
- return undeployEnvironment(appId, runtimeInstanceId)
+ if (isDeploying) {
+ return consoleClient.deployments.cancelDeployment({
+ params: {
+ appInstanceId: appId,
+ runtimeInstanceId,
+ },
+ })
+ }
+ return consoleClient.deployments.undeployEnvironment({
+ params: {
+ appInstanceId: appId,
+ runtimeInstanceId,
+ },
+ })
},
- onSuccess: async (_data, variables) => {
- await Promise.all([
- queryClient.invalidateQueries({
- queryKey: consoleQuery.deployments.list.key(),
- }),
- queryClient.invalidateQueries({
- queryKey: deploymentAppDataQueryKey(variables.appId),
- }),
- ])
+ onSuccess: async () => {
+ await queryClient.invalidateQueries({
+ queryKey: consoleQuery.deployments.key(),
+ })
},
})
}
@@ -226,22 +248,20 @@ export const useGenerateDeploymentApiKey = () => {
return useMutation({
mutationKey: consoleQuery.deployments.createEnvironmentAPIToken.mutationKey(),
- mutationFn: ({ appId, environmentId }: GenerateApiKeyParams) => {
- const appData = queryClient.getQueryData(
- deploymentAppDataQueryKey(appId),
- )
-
- return createApiKey(appId, environmentId, createApiKeyLabel(appData, environmentId))
- },
- onSuccess: async (_data, variables) => {
- await Promise.all([
- queryClient.invalidateQueries({
- queryKey: consoleQuery.deployments.list.key(),
- }),
- queryClient.invalidateQueries({
- queryKey: deploymentAppDataQueryKey(variables.appId),
- }),
- ])
+ mutationFn: ({ appId, environmentId, name }: GenerateApiKeyParams) =>
+ consoleClient.deployments.createEnvironmentAPIToken({
+ params: {
+ appInstanceId: appId,
+ },
+ body: {
+ environmentId,
+ name,
+ },
+ }),
+ onSuccess: async () => {
+ await queryClient.invalidateQueries({
+ queryKey: consoleQuery.deployments.key(),
+ })
},
})
}
@@ -251,16 +271,16 @@ export const useRevokeDeploymentApiKey = () => {
return useMutation({
mutationKey: consoleQuery.deployments.deleteEnvironmentAPIToken.mutationKey(),
- mutationFn: ({ appId, apiKeyId }: RevokeApiKeyParams) => deleteApiKey(appId, apiKeyId),
- onSuccess: async (_data, variables) => {
- await Promise.all([
- queryClient.invalidateQueries({
- queryKey: consoleQuery.deployments.list.key(),
- }),
- queryClient.invalidateQueries({
- queryKey: deploymentAppDataQueryKey(variables.appId),
- }),
- ])
+ mutationFn: ({ appId, apiKeyId }: RevokeApiKeyParams) => consoleClient.deployments.deleteEnvironmentAPIToken({
+ params: {
+ appInstanceId: appId,
+ apiKeyId,
+ },
+ }),
+ onSuccess: async () => {
+ await queryClient.invalidateQueries({
+ queryKey: consoleQuery.deployments.key(),
+ })
},
})
}
@@ -271,19 +291,29 @@ export const useToggleDeploymentAccessChannel = () => {
return useMutation({
mutationKey: consoleQuery.deployments.patchAccessChannel.mutationKey(),
mutationFn: ({ appId, channel, enabled }: ToggleAccessChannelParams) => {
- if (channel === 'api')
- return patchDeveloperAPI(appId, enabled)
- return patchAccessChannel(appId, enabled)
+ if (channel === 'api') {
+ return consoleClient.deployments.patchDeveloperAPI({
+ params: {
+ appInstanceId: appId,
+ },
+ body: {
+ enabled,
+ },
+ })
+ }
+ return consoleClient.deployments.patchAccessChannel({
+ params: {
+ appInstanceId: appId,
+ },
+ body: {
+ enabled,
+ },
+ })
},
- onSuccess: async (_data, variables) => {
- await Promise.all([
- queryClient.invalidateQueries({
- queryKey: consoleQuery.deployments.list.key(),
- }),
- queryClient.invalidateQueries({
- queryKey: deploymentAppDataQueryKey(variables.appId),
- }),
- ])
+ onSuccess: async () => {
+ await queryClient.invalidateQueries({
+ queryKey: consoleQuery.deployments.key(),
+ })
},
})
}
@@ -298,27 +328,20 @@ export const useSetEnvironmentAccessPolicy = () => {
environmentId,
accessMode,
subjects,
- }: SetEnvironmentAccessPolicyParams) =>
- updateEnvironmentAccessPolicy(appId, environmentId, accessMode, subjects),
- onSuccess: async (_data, variables) => {
- await Promise.all([
- queryClient.invalidateQueries({
- queryKey: consoleQuery.deployments.list.key(),
- }),
- queryClient.invalidateQueries({
- queryKey: deploymentAppDataQueryKey(variables.appId),
- }),
- queryClient.invalidateQueries({
- queryKey: consoleQuery.deployments.environmentAccessPolicy.queryKey({
- input: {
- params: {
- appInstanceId: variables.appId,
- environmentId: variables.environmentId,
- },
- },
- }),
- }),
- ])
+ }: SetEnvironmentAccessPolicyParams) => consoleClient.deployments.updateEnvironmentAccessPolicy({
+ params: {
+ appInstanceId: appId,
+ environmentId,
+ },
+ body: {
+ accessMode,
+ subjects,
+ },
+ }),
+ onSuccess: async () => {
+ await queryClient.invalidateQueries({
+ queryKey: consoleQuery.deployments.key(),
+ })
},
})
}
diff --git a/web/features/deployments/hooks/use-source-apps.ts b/web/features/deployments/hooks/use-source-apps.ts
deleted file mode 100644
index 34e553a633..0000000000
--- a/web/features/deployments/hooks/use-source-apps.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-'use client'
-import type { AppInfo } from '../types'
-import type { AppDeploymentSummary, EnvironmentOption } from '@/contract/console/deployments'
-import { useQuery } from '@tanstack/react-query'
-import { useMemo } from 'react'
-import { consoleQuery } from '@/service/client'
-import { toAppInfoFromSummary } from '../data'
-
-const MAX_SOURCE_APPS = 100
-
-type UseSourceAppsOptions = {
- enabled?: boolean
- environmentId?: string
- keyword?: string
- notDeployed?: boolean
-}
-
-type DeploymentEnvironmentFilter = {
- id?: string
- name?: string
- kind?: string
- disabled?: boolean
- disabledReason?: string
-}
-
-export function useSourceApps(options: UseSourceAppsOptions = {}) {
- const { enabled = true, environmentId, keyword, notDeployed } = options
- const query = useMemo(() => ({
- pageNumber: 1,
- resultsPerPage: MAX_SOURCE_APPS,
- ...(environmentId ? { environmentId } : {}),
- ...(notDeployed ? { notDeployed: true } : {}),
- ...(keyword?.trim() ? { query: keyword.trim() } : {}),
- }), [environmentId, keyword, notDeployed])
-
- const listQuery = useQuery(consoleQuery.deployments.list.queryOptions({
- input: { query },
- enabled,
- staleTime: 30 * 1000,
- }))
-
- const apps = useMemo(() => {
- return (listQuery.data?.data ?? [])
- .map(toAppInfoFromSummary)
- .filter((app): app is AppInfo => Boolean(app))
- }, [listQuery.data?.data])
-
- const appMap = useMemo