/
githubmirror
/
halo
Обзор
Документация
Войти
/
githubmirror
/
halo
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ui/console-src/modules/system/plugins/composables/use-plugin.ts
442 строки
12 KB
Ryan Wang
Refresh plugin status while starting (#10178)
29 июл 2026, 13:56
Не верифицирован
29 июл 2026, 13:56
61585f6
Код
Авторство
О чём код?
import { PluginStatusPhaseEnum, consoleApiClient, coreApiClient, type Plugin, type SettingForm, } from "@halo-dev/api-client"; import { Dialog, Toast, VLoading } from "@halo-dev/components"; import { utils, type PluginTab } from "@halo-dev/ui-shared"; import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query"; import { useRouteQuery } from "@vueuse/router"; import type { Ref } from "vue"; import { computed, defineAsyncComponent, ref, shallowRef } from "vue"; import { useI18n } from "vue-i18n"; import { usePluginModuleStore } from "@/stores/plugin"; export function getPluginStatusRefetchInterval( enabled: boolean | undefined, phase: PluginStatusPhaseEnum | undefined ) { if ( enabled && phase !== PluginStatusPhaseEnum.Started && phase !== PluginStatusPhaseEnum.Failed ) { return 1000; } return false; } export function usePluginLifeCycle(plugin?: Ref<Plugin | undefined>) { const { t } = useI18n(); const queryClient = useQueryClient(); const isStarted = computed(() => { return ( plugin?.value?.status?.phase === PluginStatusPhaseEnum.Started && plugin.value?.spec.enabled ); }); const getStatusDotState = () => { const { phase } = plugin?.value?.status || {}; const { enabled } = plugin?.value?.spec || {}; if (enabled && phase === PluginStatusPhaseEnum.Failed) { return "error"; } if (phase === PluginStatusPhaseEnum.Disabling) { return "warning"; } return "default"; }; const getStatusMessage = () => { if (!plugin?.value) return; const { phase } = plugin.value.status || {}; if ( phase === PluginStatusPhaseEnum.Failed || phase === PluginStatusPhaseEnum.Disabling ) { const lastCondition = plugin.value.status?.conditions?.[0]; return ( [lastCondition?.reason, lastCondition?.message] .filter(Boolean) .join(": ") || "Unknown" ); } // Starting up if ( phase !== (PluginStatusPhaseEnum.Started || PluginStatusPhaseEnum.Failed) ) { return t("core.common.status.starting_up"); } }; const { isLoading: changingStatus, mutate: changeStatus } = useMutation({ mutationKey: ["change-plugin-status"], mutationFn: async () => { if (!plugin?.value) return; const { enabled } = plugin.value.spec; return await consoleApiClient.plugin.plugin.changePluginRunningState({ name: plugin.value.metadata.name, pluginRunningStateRequest: { enable: !enabled, }, }); }, retry: 3, retryDelay: 1000, onSuccess() { window.location.reload(); }, }); function reload() { Dialog.warning({ title: t("core.plugin.operations.reload.title"), description: t("core.plugin.operations.reload.description"), confirmType: "danger", confirmText: t("core.common.buttons.confirm"), cancelText: t("core.common.buttons.cancel"), onConfirm: async () => { if (!plugin?.value) { return; } await consoleApiClient.plugin.plugin.reloadPlugin({ name: plugin.value.metadata.name, }); Toast.success(t("core.plugin.operations.reload.toast_success")); await queryClient.invalidateQueries({ queryKey: ["plugins"], }); await queryClient.invalidateQueries({ queryKey: ["plugin", plugin.value.metadata.name], }); }, }); } function resetPluginConfig() { Dialog.warning({ title: t("core.plugin.operations.reset.title"), description: t("core.plugin.operations.reset.description"), confirmType: "danger", confirmText: t("core.common.buttons.confirm"), cancelText: t("core.common.buttons.cancel"), onConfirm: async () => { if (!plugin?.value) { return; } await consoleApiClient.plugin.plugin.resetPluginConfig({ name: plugin.value.metadata.name, }); await queryClient.invalidateQueries({ queryKey: ["core:plugin:configMap:data"], }); Toast.success(t("core.plugin.operations.reset.toast_success")); }, }); } const uninstall = ({ deleteExtensions, onSuccess, }: { deleteExtensions?: boolean; onSuccess?: () => void } = {}) => { if (!plugin?.value) return; const { enabled } = plugin.value.spec; Dialog.warning({ title: `${ deleteExtensions ? t("core.plugin.operations.uninstall_and_delete_config.title") : t("core.plugin.operations.uninstall.title") }`, description: `${ enabled ? t("core.plugin.operations.uninstall_when_enabled.description") : t("core.common.dialog.descriptions.cannot_be_recovered") }`, confirmType: "danger", confirmText: `${ enabled ? t("core.plugin.operations.uninstall_when_enabled.confirm_text") : t("core.common.buttons.uninstall") }`, cancelText: t("core.common.buttons.cancel"), onConfirm: async () => { if (!plugin.value) return; await consoleApiClient.plugin.plugin.changePluginRunningState({ name: plugin.value.metadata.name, pluginRunningStateRequest: { enable: false, }, }); await coreApiClient.plugin.plugin.deletePlugin({ name: plugin.value.metadata.name, }); // delete plugin setting and configMap if (deleteExtensions) { const { settingName, configMapName } = plugin.value.spec; if (settingName) { await coreApiClient.setting.deleteSetting( { name: settingName, }, { mute: true, } ); } if (configMapName) { await coreApiClient.configMap.deleteConfigMap( { name: configMapName, }, { mute: true, } ); } } Toast.success(t("core.common.toast.uninstall_success")); onSuccess?.(); }, }); }; return { isStarted, getStatusDotState, getStatusMessage, changeStatus, changingStatus, reload, uninstall, resetPluginConfig, }; } export function usePluginBatchOperations(names: Ref<string[]>) { const { t } = useI18n(); function handleUninstallInBatch(deleteExtensions: boolean) { Dialog.warning({ title: `${ deleteExtensions ? t( "core.plugin.operations.uninstall_and_delete_config_in_batch.title" ) : t("core.plugin.operations.uninstall_in_batch.title") }`, description: t("core.common.dialog.descriptions.cannot_be_recovered"), confirmType: "danger", confirmText: t("core.common.buttons.uninstall"), cancelText: t("core.common.buttons.cancel"), onConfirm: async () => { try { for (let i = 0; i < names.value.length; i++) { await coreApiClient.plugin.plugin.deletePlugin({ name: names.value[i], }); if (deleteExtensions) { const { data: plugin } = await coreApiClient.plugin.plugin.getPlugin({ name: names.value[i], }); const { settingName, configMapName } = plugin.spec; if (settingName) { await coreApiClient.setting.deleteSetting( { name: settingName, }, { mute: true, } ); } if (configMapName) { await coreApiClient.configMap.deleteConfigMap( { name: configMapName, }, { mute: true, } ); } } } window.location.reload(); } catch (e) { console.error("Failed to uninstall plugin in batch", e); } }, }); } function handleChangeStatusInBatch(enabled: boolean) { Dialog.info({ title: enabled ? t("core.plugin.operations.change_status_in_batch.activate_title") : t("core.plugin.operations.change_status_in_batch.inactivate_title"), confirmText: t("core.common.buttons.confirm"), cancelText: t("core.common.buttons.cancel"), onConfirm: async () => { try { for (let i = 0; i < names.value.length; i++) { await consoleApiClient.plugin.plugin.changePluginRunningState({ name: names.value[i], pluginRunningStateRequest: { enable: enabled, }, }); } window.location.reload(); } catch (e) { console.error("Failed to change plugin status in batch", e); } }, }); } return { handleUninstallInBatch, handleChangeStatusInBatch }; } export function usePluginDetailTabs( pluginName: Ref<string | undefined>, recordsActiveTab: boolean ) { const { t } = useI18n(); const initialTabs = [ { id: "detail", label: t("core.plugin.tabs.detail"), component: defineAsyncComponent({ loader: () => import("../components/tabs/Detail.vue"), loadingComponent: VLoading, }), }, ]; const tabs = shallowRef<PluginTab[]>(initialTabs); const activeTab = recordsActiveTab ? useRouteQuery<string>("tab", tabs.value[0].id) : ref(tabs.value[0].id); const { data: plugin } = useQuery({ queryKey: ["plugin", pluginName], queryFn: async () => { const { data } = await coreApiClient.plugin.plugin.getPlugin({ name: pluginName.value as string, }); return data; }, async onSuccess(data) { if ( !data.spec.settingName || !utils.permission.has(["system:plugins:manage"]) ) { tabs.value = [...initialTabs, ...(await getTabsFromExtensions())]; } }, refetchInterval: (data) => getPluginStatusRefetchInterval(data?.spec.enabled, data?.status?.phase), }); const { data: setting } = useQuery({ queryKey: ["plugin-setting", plugin], queryFn: async () => { const { data } = await consoleApiClient.plugin.plugin.fetchPluginSetting({ name: plugin.value?.metadata.name as string, }); return data; }, enabled: computed(() => { return ( !!plugin.value && !!plugin.value.spec.settingName && utils.permission.has(["system:plugins:manage"]) ); }), async onSuccess(data) { if (data) { const { forms } = data.spec; tabs.value = [ ...initialTabs, ...(await getTabsFromExtensions()), ...forms.map((item: SettingForm) => { return { id: item.group, label: item.label || "", component: defineAsyncComponent({ loader: () => import("../components/tabs/Setting.vue"), loadingComponent: VLoading, }), }; }), ] as PluginTab[]; } }, }); async function getTabsFromExtensions() { const { pluginModuleMap } = usePluginModuleStore(); const currentPluginModule = pluginModuleMap[pluginName.value as string]; if (!currentPluginModule) { return []; } const callbackFunction = currentPluginModule?.extensionPoints?.["plugin:self:tabs:create"]; if (typeof callbackFunction !== "function") { return []; } const pluginTabs = await callbackFunction(); return pluginTabs.filter((tab) => { return utils.permission.has(tab.permissions || []); }); } return { plugin, setting, tabs, activeTab, }; }