Files
dify/web/features/deployments/store.ts
Stephen Zhou 1aea4e00a4 tweaks
2026-04-29 13:25:41 +08:00

232 lines
7.1 KiB
TypeScript

import type { DeploymentAppData } from './data'
import type { AppInfo } from './types'
import type { AccessSubject, APIToken, BindingsProto } from '@/contract/console/deployments'
import { create } from 'zustand'
import {
cancelDeployment,
createApiKey,
createDeployment,
deleteApiKey,
patchAccessChannel,
refreshDeploymentAppData,
rollbackEnvironment,
undeployEnvironment,
updateEnvironmentAccessPolicy,
} from './data'
export type StartDeployParams = {
appId: string
environmentId: string
releaseId?: string
releaseNote?: string
bindings?: BindingsProto
}
type OpenDeployDrawerParams = {
appId: string
environmentId?: string
releaseId?: string
}
type OpenRollbackParams = {
appId: string
environmentId: string
targetReleaseId: string
deploymentId?: string
}
type CreatedApiToken = Pick<APIToken, 'id' | 'environmentId' | 'maskedPrefix' | 'name'> & {
appId: string
token: string
}
export type CreateInstanceParams = {
appId: string
name: string
description?: string
}
type DeploymentsState = {
sourceApps: AppInfo[]
appData: Record<string, DeploymentAppData>
createdApiToken?: CreatedApiToken
deployDrawer: {
open: boolean
appId?: string
environmentId?: string
releaseId?: string
}
rollbackModal: {
open: boolean
appId?: string
environmentId?: string
deploymentId?: string
targetReleaseId?: string
}
createInstanceModal: { open: boolean }
openDeployDrawer: (params: OpenDeployDrawerParams) => void
closeDeployDrawer: () => void
openRollbackModal: (params: OpenRollbackParams) => void
closeRollbackModal: () => void
openCreateInstanceModal: () => void
closeCreateInstanceModal: () => void
seedInstancesFromApps: (apps: AppInfo[]) => void
applyAppData: (data: DeploymentAppData) => void
refreshAppData: (appId: string) => Promise<void>
createInstance: (params: CreateInstanceParams) => string
updateInstance: (appId: string, patch: Partial<Pick<AppInfo, 'name' | 'description'>>) => void
switchSourceApp: (appId: string, nextAppId: string) => void
deleteInstance: (appId: string) => void
startDeploy: (params: StartDeployParams) => Promise<void>
retryDeploy: (appId: string, environmentId: string, targetReleaseId: string) => Promise<void>
rollbackDeployment: (appId: string, environmentId: string, targetReleaseId: string) => Promise<void>
undeployDeployment: (appId: string, environmentId: string, deploymentId?: string, isDeploying?: boolean) => Promise<void>
generateApiKey: (appId: string, environmentId: string) => Promise<void>
revokeApiKey: (appId: string, environmentId: string, apiKeyId: string) => Promise<void>
clearCreatedApiToken: () => void
toggleAccessChannel: (appId: string, channel: string, enabled: boolean, expectedVersion: number) => Promise<void>
setEnvironmentAccessPolicy: (
appId: string,
environmentId: string,
channel: string,
enabled: boolean,
accessMode: string,
subjects: AccessSubject[],
expectedVersion: number,
) => Promise<void>
}
export const useDeploymentsStore = create<DeploymentsState>((set, get) => ({
sourceApps: [],
appData: {},
createdApiToken: undefined,
deployDrawer: { open: false },
rollbackModal: { open: false },
createInstanceModal: { open: false },
openDeployDrawer: params => set({
deployDrawer: {
open: true,
appId: params.appId,
environmentId: params.environmentId,
releaseId: params.releaseId,
},
}),
closeDeployDrawer: () => set({ deployDrawer: { open: false } }),
openRollbackModal: ({ appId, environmentId, deploymentId, targetReleaseId }) => set({
rollbackModal: { open: true, appId, environmentId, deploymentId, targetReleaseId },
}),
closeRollbackModal: () => set({ rollbackModal: { open: false } }),
openCreateInstanceModal: () => set({ createInstanceModal: { open: true } }),
closeCreateInstanceModal: () => set({ createInstanceModal: { open: false } }),
seedInstancesFromApps: apps => set(state => ({
sourceApps: apps,
appData: Object.fromEntries(
Object.entries(state.appData).filter(([appId]) => apps.some(app => app.id === appId)),
),
})),
applyAppData: data => set(state => ({
appData: {
...state.appData,
[data.appId]: data,
},
})),
refreshAppData: async (appId) => {
const data = await refreshDeploymentAppData(appId)
get().applyAppData(data)
},
createInstance: ({ appId }) => {
set({ createInstanceModal: { open: false } })
return appId
},
updateInstance: () => undefined,
switchSourceApp: () => undefined,
deleteInstance: () => undefined,
startDeploy: async ({ appId, environmentId, releaseId, releaseNote, bindings }) => {
set({ deployDrawer: { open: false } })
await createDeployment({ appId, environmentId, releaseId, releaseNote, bindings })
await get().refreshAppData(appId)
},
retryDeploy: async (appId, environmentId, targetReleaseId) => {
await rollbackEnvironment(appId, environmentId, targetReleaseId)
await get().refreshAppData(appId)
},
rollbackDeployment: async (appId, environmentId, targetReleaseId) => {
set({ rollbackModal: { open: false } })
await rollbackEnvironment(appId, environmentId, targetReleaseId)
await get().refreshAppData(appId)
},
undeployDeployment: async (appId, environmentId, deploymentId, isDeploying) => {
if (isDeploying && deploymentId)
await cancelDeployment(appId, environmentId, deploymentId)
else
await undeployEnvironment(appId, environmentId)
await get().refreshAppData(appId)
},
generateApiKey: async (appId, environmentId) => {
const appData = get().appData[appId]
const existingCount = appData?.accessConfig.developerApi?.apiKeys?.filter(key => key.environmentId === environmentId).length ?? 0
const environmentName = appData
?.environmentDeployments
.environmentDeployments
?.find(row => row.environment?.id === environmentId)
?.environment
?.name ?? 'env'
const label = `${environmentName}-key-${String(existingCount + 1).padStart(3, '0')}`
const response = await createApiKey(appId, environmentId, label)
await get().refreshAppData(appId)
if (response.apiToken?.token) {
set({
createdApiToken: {
id: response.apiToken.id,
appId,
environmentId,
maskedPrefix: response.apiToken.maskedPrefix,
name: response.apiToken.name || label,
token: response.apiToken.token,
},
})
}
},
revokeApiKey: async (appId, environmentId, apiKeyId) => {
await deleteApiKey(appId, environmentId, apiKeyId)
await get().refreshAppData(appId)
},
clearCreatedApiToken: () => set({ createdApiToken: undefined }),
toggleAccessChannel: async (appId, channel, enabled, expectedVersion) => {
await patchAccessChannel(appId, channel, enabled, expectedVersion)
await get().refreshAppData(appId)
},
setEnvironmentAccessPolicy: async (appId, environmentId, channel, enabled, accessMode, subjects, expectedVersion) => {
await updateEnvironmentAccessPolicy(appId, environmentId, channel, enabled, accessMode, subjects, expectedVersion)
await get().refreshAppData(appId)
},
}))