mirror of https://github.com/halo-dev/halo
feat: add support for displaying enabled plugins on actuator page (#4897)
parent
03a9fc5080
commit
3306b1f881
|
@ -9,49 +9,80 @@ import {
|
||||||
Toast,
|
Toast,
|
||||||
VDescription,
|
VDescription,
|
||||||
VDescriptionItem,
|
VDescriptionItem,
|
||||||
|
VTag,
|
||||||
|
VLoading,
|
||||||
} from "@halo-dev/components";
|
} from "@halo-dev/components";
|
||||||
import { computed, onMounted, ref } from "vue";
|
import { computed } from "vue";
|
||||||
import type { Info, GlobalInfo, Startup } from "@/types";
|
import type { Info, GlobalInfo, Startup } from "@/types";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { formatDatetime } from "@/utils/date";
|
import { formatDatetime } from "@/utils/date";
|
||||||
import { useClipboard } from "@vueuse/core";
|
import { useClipboard } from "@vueuse/core";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { useThemeStore } from "@console/stores/theme";
|
||||||
|
import type { Plugin } from "@halo-dev/api-client";
|
||||||
|
import { apiClient } from "@/utils/api-client";
|
||||||
|
import { useQuery } from "@tanstack/vue-query";
|
||||||
|
import { usePermission } from "@/utils/permission";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const themeStore = useThemeStore();
|
||||||
|
const { currentUserHasPermission } = usePermission();
|
||||||
|
|
||||||
const info = ref<Info>();
|
const { data: info } = useQuery<Info>({
|
||||||
const globalInfo = ref<GlobalInfo>();
|
queryKey: ["system-info"],
|
||||||
const startup = ref<Startup>();
|
queryFn: async () => {
|
||||||
|
const { data } = await axios.get<Info>(
|
||||||
|
`${import.meta.env.VITE_API_URL}/actuator/info`,
|
||||||
|
{
|
||||||
|
withCredentials: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
retry: 0,
|
||||||
|
});
|
||||||
|
|
||||||
const handleFetchActuatorInfo = async () => {
|
const { data: globalInfo } = useQuery<GlobalInfo>({
|
||||||
const { data } = await axios.get(
|
queryKey: ["system-global-info"],
|
||||||
`${import.meta.env.VITE_API_URL}/actuator/info`,
|
queryFn: async () => {
|
||||||
{
|
const { data } = await axios.get<GlobalInfo>(
|
||||||
withCredentials: true,
|
`${import.meta.env.VITE_API_URL}/actuator/globalinfo`,
|
||||||
}
|
{
|
||||||
);
|
withCredentials: true,
|
||||||
info.value = data;
|
}
|
||||||
};
|
);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
retry: 0,
|
||||||
|
});
|
||||||
|
|
||||||
const handleFetchActuatorGlobalInfo = async () => {
|
const { data: startup } = useQuery<Startup>({
|
||||||
const { data } = await axios.get(
|
queryKey: ["system-startup-info"],
|
||||||
`${import.meta.env.VITE_API_URL}/actuator/globalinfo`,
|
queryFn: async () => {
|
||||||
{
|
const { data } = await axios.get<Startup>(
|
||||||
withCredentials: true,
|
`${import.meta.env.VITE_API_URL}/actuator/startup`,
|
||||||
}
|
{
|
||||||
);
|
withCredentials: true,
|
||||||
globalInfo.value = data;
|
}
|
||||||
};
|
);
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
retry: 0,
|
||||||
|
});
|
||||||
|
|
||||||
const handleFetchActuatorStartup = async () => {
|
const { data: plugins, isLoading: isPluginsLoading } = useQuery<Plugin[]>({
|
||||||
const { data } = await axios.get(
|
queryKey: ["enabled-plugins"],
|
||||||
`${import.meta.env.VITE_API_URL}/actuator/startup`,
|
queryFn: async () => {
|
||||||
{
|
const { data } = await apiClient.plugin.listPlugins({
|
||||||
withCredentials: true,
|
page: 0,
|
||||||
}
|
size: 0,
|
||||||
);
|
enabled: true,
|
||||||
startup.value = data;
|
});
|
||||||
};
|
|
||||||
|
return data.items;
|
||||||
|
},
|
||||||
|
enabled: computed(() => currentUserHasPermission(["system:plugins:view"])),
|
||||||
|
});
|
||||||
|
|
||||||
const isExternalUrlValid = computed(() => {
|
const isExternalUrlValid = computed(() => {
|
||||||
if (!globalInfo.value?.useAbsolutePermalink) {
|
if (!globalInfo.value?.useAbsolutePermalink) {
|
||||||
|
@ -67,43 +98,101 @@ const isExternalUrlValid = computed(() => {
|
||||||
return url.host === currentHost && url.protocol === currentProtocol;
|
return url.host === currentHost && url.protocol === currentProtocol;
|
||||||
});
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
handleFetchActuatorInfo();
|
|
||||||
handleFetchActuatorGlobalInfo();
|
|
||||||
handleFetchActuatorStartup();
|
|
||||||
});
|
|
||||||
|
|
||||||
// copy system information to clipboard
|
// copy system information to clipboard
|
||||||
const { copy, isSupported } = useClipboard({ legacy: true });
|
const { copy, isSupported } = useClipboard({ legacy: true });
|
||||||
|
|
||||||
|
interface CopyItem {
|
||||||
|
label: string;
|
||||||
|
value?: string;
|
||||||
|
href?: string;
|
||||||
|
children?: CopyItem[];
|
||||||
|
}
|
||||||
|
|
||||||
const handleCopy = () => {
|
const handleCopy = () => {
|
||||||
if (!isSupported.value) {
|
if (!isSupported.value) {
|
||||||
Toast.warning(t("core.actuator.actions.copy.toast_browser_not_supported"));
|
Toast.warning(t("core.actuator.actions.copy.toast_browser_not_supported"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const text = `
|
const copyItems: CopyItem[] = [
|
||||||
- ${t("core.actuator.copy_results.external_url", {
|
{
|
||||||
external_url: globalInfo.value?.externalUrl,
|
label: t("core.actuator.fields.external_url"),
|
||||||
})}
|
value: globalInfo.value?.externalUrl || "",
|
||||||
- ${t("core.actuator.copy_results.start_time", {
|
},
|
||||||
start_time: formatDatetime(startup.value?.timeline.startTime),
|
{
|
||||||
})}
|
label: t("core.actuator.fields.start_time"),
|
||||||
- ${t("core.actuator.fields.version", { version: info.value?.build?.version })}
|
value: formatDatetime(startup.value?.timeline.startTime) || "",
|
||||||
- ${t("core.actuator.copy_results.build_time", {
|
},
|
||||||
build_time: formatDatetime(info.value?.build?.time),
|
{
|
||||||
})}
|
label: t("core.actuator.fields.version"),
|
||||||
- Git Commit:${info.value?.git?.commit.id}
|
value: info.value?.build?.version || "",
|
||||||
- Java:${info.value?.java.runtime.name} / ${info.value?.java.runtime.version}
|
},
|
||||||
- ${t("core.actuator.copy_results.database", {
|
{
|
||||||
database: [info.value?.database.name, info.value?.database.version].join(
|
label: t("core.actuator.fields.build_time"),
|
||||||
" / "
|
value: formatDatetime(info.value?.build?.time) || "",
|
||||||
),
|
},
|
||||||
})}
|
{
|
||||||
- ${t("core.actuator.copy_results.os", {
|
label: "Git Commit",
|
||||||
os: [info.value?.os.name, info.value?.os.version].join(" / "),
|
value: info.value?.git?.commit.id || "",
|
||||||
})}
|
},
|
||||||
`;
|
{
|
||||||
|
label: "Java",
|
||||||
|
value:
|
||||||
|
[info.value?.java.runtime.name, info.value?.java.runtime.version]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" / ")
|
||||||
|
.trim() || "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t("core.actuator.fields.database"),
|
||||||
|
value:
|
||||||
|
[info.value?.database.name, info.value?.database.version]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" / ") || "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t("core.actuator.fields.os"),
|
||||||
|
value:
|
||||||
|
[info.value?.os.name, info.value?.os.version]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(" / ") || "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t("core.actuator.fields.activated_theme"),
|
||||||
|
value: themeStore.activatedTheme?.spec.displayName || "",
|
||||||
|
href:
|
||||||
|
themeStore.activatedTheme?.spec.repo ||
|
||||||
|
themeStore.activatedTheme?.spec.homepage,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t("core.actuator.fields.enabled_plugins"),
|
||||||
|
children: plugins.value?.map((plugin) => ({
|
||||||
|
value: plugin.spec.displayName,
|
||||||
|
href: plugin.spec.repo || plugin.spec.homepage,
|
||||||
|
})) as CopyItem[],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const text = copyItems
|
||||||
|
.map((item) => {
|
||||||
|
if (item.children?.length) {
|
||||||
|
const childrenText = item.children
|
||||||
|
.map(
|
||||||
|
(child) =>
|
||||||
|
` - ${
|
||||||
|
child.href ? `[${child.value}](${child.href})` : child.value
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
.filter(Boolean)
|
||||||
|
.join("\n");
|
||||||
|
return `- ${item.label}:\n${childrenText}`;
|
||||||
|
} else {
|
||||||
|
return `- ${item.label}: ${
|
||||||
|
item.href ? `[${item.value}](${item.href})` : item.value || ""
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
copy(text);
|
copy(text);
|
||||||
|
|
||||||
|
@ -182,17 +271,55 @@ const handleDownloadLogfile = () => {
|
||||||
</VAlert>
|
</VAlert>
|
||||||
</VDescriptionItem>
|
</VDescriptionItem>
|
||||||
<VDescriptionItem
|
<VDescriptionItem
|
||||||
|
v-if="startup?.timeline.startTime"
|
||||||
:label="$t('core.actuator.fields.start_time')"
|
:label="$t('core.actuator.fields.start_time')"
|
||||||
:content="formatDatetime(startup?.timeline.startTime)"
|
:content="formatDatetime(startup?.timeline.startTime)"
|
||||||
/>
|
/>
|
||||||
<VDescriptionItem
|
<VDescriptionItem
|
||||||
:label="$t('core.actuator.fields.timezone')"
|
v-if="themeStore.activatedTheme"
|
||||||
:content="globalInfo?.timeZone"
|
:label="$t('core.actuator.fields.activated_theme')"
|
||||||
/>
|
>
|
||||||
|
<VTag @click="$router.push({ name: 'ThemeDetail' })">
|
||||||
|
<template v-if="themeStore.activatedTheme.spec.logo" #leftIcon>
|
||||||
|
<img
|
||||||
|
class="h-3.5 w-3.5 rounded-sm"
|
||||||
|
:src="themeStore.activatedTheme.spec.logo"
|
||||||
|
:alt="themeStore.activatedTheme.spec.displayName"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
{{ themeStore.activatedTheme.spec.displayName }}
|
||||||
|
</VTag>
|
||||||
|
</VDescriptionItem>
|
||||||
<VDescriptionItem
|
<VDescriptionItem
|
||||||
:label="$t('core.actuator.fields.locale')"
|
v-permission="['system:plugins:view']"
|
||||||
:content="globalInfo?.locale"
|
:label="$t('core.actuator.fields.enabled_plugins')"
|
||||||
/>
|
>
|
||||||
|
<VLoading v-if="isPluginsLoading" />
|
||||||
|
<span v-else-if="!plugins?.length">
|
||||||
|
{{ $t("core.common.text.none") }}
|
||||||
|
</span>
|
||||||
|
<div v-else class="flex flex-wrap gap-1.5">
|
||||||
|
<VTag
|
||||||
|
v-for="plugin in plugins"
|
||||||
|
:key="plugin.metadata.name"
|
||||||
|
@click="
|
||||||
|
$router.push({
|
||||||
|
name: 'PluginDetail',
|
||||||
|
params: { name: plugin.metadata.name },
|
||||||
|
})
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<template v-if="plugin.status?.logo" #leftIcon>
|
||||||
|
<img
|
||||||
|
class="h-3.5 w-3.5 rounded-sm"
|
||||||
|
:src="plugin.status?.logo"
|
||||||
|
:alt="plugin.spec.displayName"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
{{ plugin.spec.displayName }}
|
||||||
|
</VTag>
|
||||||
|
</div>
|
||||||
|
</VDescriptionItem>
|
||||||
</VDescription>
|
</VDescription>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -249,6 +376,14 @@ const handleDownloadLogfile = () => {
|
||||||
<VDescriptionItem :label="$t('core.actuator.fields.os')">
|
<VDescriptionItem :label="$t('core.actuator.fields.os')">
|
||||||
{{ info.os.name }} {{ info.os.version }} / {{ info.os.arch }}
|
{{ info.os.name }} {{ info.os.version }} / {{ info.os.arch }}
|
||||||
</VDescriptionItem>
|
</VDescriptionItem>
|
||||||
|
<VDescriptionItem
|
||||||
|
:label="$t('core.actuator.fields.timezone')"
|
||||||
|
:content="globalInfo?.timeZone"
|
||||||
|
/>
|
||||||
|
<VDescriptionItem
|
||||||
|
:label="$t('core.actuator.fields.locale')"
|
||||||
|
:content="globalInfo?.locale"
|
||||||
|
/>
|
||||||
<VDescriptionItem
|
<VDescriptionItem
|
||||||
:label="$t('core.actuator.fields.log')"
|
:label="$t('core.actuator.fields.log')"
|
||||||
vertical-center
|
vertical-center
|
||||||
|
|
|
@ -1019,13 +1019,15 @@ core:
|
||||||
fields:
|
fields:
|
||||||
external_url: External url
|
external_url: External url
|
||||||
start_time: Start time
|
start_time: Start time
|
||||||
timezone: Timezone
|
timezone: System Timezone
|
||||||
locale: Locale
|
locale: System Locale
|
||||||
version: Version
|
version: Version
|
||||||
build_time: Build time
|
build_time: Build time
|
||||||
database: Database
|
database: Database
|
||||||
os: Operating system
|
os: Operating system
|
||||||
log: System log
|
log: System log
|
||||||
|
activated_theme: Activated theme
|
||||||
|
enabled_plugins: Enabled plugins
|
||||||
fields_values:
|
fields_values:
|
||||||
external_url:
|
external_url:
|
||||||
not_setup: Not setup
|
not_setup: Not setup
|
||||||
|
|
|
@ -1019,13 +1019,15 @@ core:
|
||||||
fields:
|
fields:
|
||||||
external_url: 外部访问地址
|
external_url: 外部访问地址
|
||||||
start_time: 启动时间
|
start_time: 启动时间
|
||||||
timezone: 时区
|
timezone: 系统时区
|
||||||
locale: 语言
|
locale: 系统语言
|
||||||
version: 版本
|
version: 版本
|
||||||
build_time: 构建时间
|
build_time: 构建时间
|
||||||
database: 数据库
|
database: 数据库
|
||||||
os: 操作系统
|
os: 操作系统
|
||||||
log: 运行日志
|
log: 运行日志
|
||||||
|
activated_theme: 已激活主题
|
||||||
|
enabled_plugins: 已启动插件
|
||||||
fields_values:
|
fields_values:
|
||||||
external_url:
|
external_url:
|
||||||
not_setup: 未设置
|
not_setup: 未设置
|
||||||
|
|
|
@ -1019,13 +1019,15 @@ core:
|
||||||
fields:
|
fields:
|
||||||
external_url: 外部訪問地址
|
external_url: 外部訪問地址
|
||||||
start_time: 啟動時間
|
start_time: 啟動時間
|
||||||
timezone: 時區
|
timezone: 系統時區
|
||||||
locale: 語言
|
locale: 系統語言
|
||||||
version: 版本
|
version: 版本
|
||||||
build_time: 構建時間
|
build_time: 構建時間
|
||||||
database: 資料庫
|
database: 資料庫
|
||||||
os: 操作系統
|
os: 操作系統
|
||||||
log: 運行日誌
|
log: 運行日誌
|
||||||
|
activated_theme: 已啟動主題
|
||||||
|
enabled_plugins: 已啟動插件
|
||||||
fields_values:
|
fields_values:
|
||||||
external_url:
|
external_url:
|
||||||
not_setup: 未設置
|
not_setup: 未設置
|
||||||
|
|
Loading…
Reference in New Issue