feat: add support for uploading plugins (#590)

Signed-off-by: Ryan Wang <i@ryanc.cc>

<!--  Thanks for sending a pull request!  Here are some tips for you:
1. 如果这是你的第一次,请阅读我们的贡献指南:<https://github.com/halo-dev/halo/blob/master/CONTRIBUTING.md>。
1. If this is your first time, please read our contributor guidelines: <https://github.com/halo-dev/halo/blob/master/CONTRIBUTING.md>.
2. 请根据你解决问题的类型为 Pull Request 添加合适的标签。
2. Please label this pull request according to what type of issue you are addressing, especially if this is a release targeted pull request.
3. 请确保你已经添加并运行了适当的测试。
3. Ensure you have added or ran the appropriate tests for your PR.
-->

#### What type of PR is this?

<!--
添加其中一个类别:
Add one of the following kinds:

/kind bug
/kind cleanup
/kind documentation
/kind feature
/kind optimization

适当添加其中一个或多个类别(可选):
Optionally add one or more of the following kinds if applicable:

/kind api-change
/kind deprecation
/kind failing-test
/kind flake
/kind regression
-->

/kind feature
/milestone 2.0

#### What this PR does / why we need it:

添加上传插件的支持。

see https://github.com/halo-dev/halo/pull/2271

#### Which issue(s) this PR fixes:

<!--
PR 合并时自动关闭 issue。
Automatically closes linked issue when PR is merged.

用法:`Fixes #<issue 号>`,或者 `Fixes (粘贴 issue 完整链接)`
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
None

#### Screenshots:

<img width="1156" alt="image" src="https://user-images.githubusercontent.com/21301288/180416186-49c56971-c0b9-4761-ae9e-8531e433a9c0.png">
<img width="1156" alt="image" src="https://user-images.githubusercontent.com/21301288/180416203-e4b35266-3645-497b-9d08-d6407b2dd7f7.png">


<!--
如果此 PR 有 UI 的改动,最好截图说明这个 PR 的改动。
If there are UI changes to this PR, it is best to take a screenshot to illustrate the changes to this PR.

eg.

Before:

![screenshot-before](https://user-images.githubusercontent.com/screenshot.png)

After:

![screenshot-after](https://user-images.githubusercontent.com/screenshot.png)
-->

#### Special notes for your reviewer:

可以使用以下插件仓库中 Actions 构建的 JAR 测试:

- https://github.com/halo-sigs/plugin-links/actions
- https://github.com/halo-sigs/plugin-meilisearch/actions
- https://github.com/halo-sigs/plugin-template/actions

/cc @halo-dev/sig-halo-admin 
/cc @halo-dev/sig-halo 

#### Does this PR introduce a user-facing change?

<!--
如果当前 Pull Request 的修改不会造成用户侧的任何变更,在 `release-note` 代码块儿中填写 `NONE`。
否则请填写用户侧能够理解的 Release Note。如果当前 Pull Request 包含破坏性更新(Break Change),
Release Note 需要以 `action required` 开头。
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".
-->

```release-note
None
```
pull/591/head
Ryan Wang 2022-07-23 20:42:13 +08:00 committed by GitHub
parent 52bb68e565
commit d583e55390
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 108 additions and 13 deletions

View File

@ -35,7 +35,7 @@
"homepage": "https://github.com/halo-dev/halo-admin/tree/next/shared/components#readme", "homepage": "https://github.com/halo-dev/halo-admin/tree/next/shared/components#readme",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@halo-dev/api-client": "^0.0.3", "@halo-dev/api-client": "^0.0.4",
"@halo-dev/components": "workspace:*", "@halo-dev/components": "workspace:*",
"axios": "^0.27.2" "axios": "^0.27.2"
}, },

View File

@ -1,18 +1,17 @@
import { import {
PluginHaloRunV1alpha1PluginApi, ApiHaloRunV1alpha1PluginApi,
V1alpha1UserApi,
ApiHaloRunV1alpha1UserApi, ApiHaloRunV1alpha1UserApi,
PluginHaloRunV1alpha1PluginApi,
PluginHaloRunV1alpha1ReverseProxyApi,
V1alpha1ConfigMapApi, V1alpha1ConfigMapApi,
V1alpha1PersonalAccessTokenApi, V1alpha1PersonalAccessTokenApi,
V1alpha1RoleBindingApi,
V1alpha1RoleApi, V1alpha1RoleApi,
V1alpha1RoleBindingApi,
V1alpha1SettingApi, V1alpha1SettingApi,
PluginHaloRunV1alpha1ReverseProxyApi, V1alpha1UserApi,
CoreHaloRunV1alpha1LinkApi,
CoreHaloRunV1alpha1LinkGroupApi,
} from "@halo-dev/api-client"; } from "@halo-dev/api-client";
import axios from "axios";
import type { AxiosInstance } from "axios"; import type { AxiosInstance } from "axios";
import axios from "axios";
let apiUrl: string | undefined; let apiUrl: string | undefined;
const axiosInstance = axios.create({ const axiosInstance = axios.create({
@ -61,10 +60,11 @@ function setupApiClient(axios: AxiosInstance) {
user: new V1alpha1UserApi(undefined, apiUrl, axios), user: new V1alpha1UserApi(undefined, apiUrl, axios),
// TODO optional // TODO optional
link: new CoreHaloRunV1alpha1LinkApi(undefined, apiUrl, axios), // link: new CoreHaloRunV1alpha1LinkApi(undefined, apiUrl, axios),
linkGroup: new CoreHaloRunV1alpha1LinkGroupApi(undefined, apiUrl, axios), // linkGroup: new CoreHaloRunV1alpha1LinkGroupApi(undefined, apiUrl, axios),
}, },
user: new ApiHaloRunV1alpha1UserApi(undefined, apiUrl, axios), user: new ApiHaloRunV1alpha1UserApi(undefined, apiUrl, axios),
plugin: new ApiHaloRunV1alpha1PluginApi(undefined, apiUrl, axios),
}; };
} }

View File

@ -172,12 +172,12 @@ importers:
packages/shared: packages/shared:
specifiers: specifiers:
'@halo-dev/api-client': ^0.0.3 '@halo-dev/api-client': ^0.0.4
'@halo-dev/components': workspace:* '@halo-dev/components': workspace:*
axios: ^0.27.2 axios: ^0.27.2
vite-plugin-dts: ^1.3.1 vite-plugin-dts: ^1.3.1
dependencies: dependencies:
'@halo-dev/api-client': 0.0.3 '@halo-dev/api-client': 0.0.4
'@halo-dev/components': link:../components '@halo-dev/components': link:../components
axios: 0.27.2 axios: 0.27.2
devDependencies: devDependencies:
@ -1873,6 +1873,10 @@ packages:
resolution: {integrity: sha512-ybtVcjeeFqOldPIKRrm3socfGtbSR5GL+MXVmkSJwD2fCwT9OiXEvs3cgceWceIZI0Ug7AEKwxbcO+ZfOmDlYA==} resolution: {integrity: sha512-ybtVcjeeFqOldPIKRrm3socfGtbSR5GL+MXVmkSJwD2fCwT9OiXEvs3cgceWceIZI0Ug7AEKwxbcO+ZfOmDlYA==}
dev: false dev: false
/@halo-dev/api-client/0.0.4:
resolution: {integrity: sha512-rNkQJWBL+1du/wfe70cqc7YczcQMTlQBQb0xmBLXCvQvxIlANpEVo0YKlbMXBvznaec2MYUFu7aF0b8hi4GEDw==}
dev: false
/@halo-dev/logger/1.1.0: /@halo-dev/logger/1.1.0:
resolution: {integrity: sha512-y0jVivYwF8MCVi/OdW2D0LN+GTM5rzMsR/ZmQVfgmKQw7Q7Q+EXPijxON6iCMZnWANGa4NaAcOO9k3ggG8oRwg==} resolution: {integrity: sha512-y0jVivYwF8MCVi/OdW2D0LN+GTM5rzMsR/ZmQVfgmKQw7Q7Q+EXPijxON6iCMZnWANGa4NaAcOO9k3ggG8oRwg==}
engines: {node: '>=12.0.0'} engines: {node: '>=12.0.0'}

View File

@ -13,6 +13,7 @@ import {
VSwitch, VSwitch,
VTag, VTag,
} from "@halo-dev/components"; } from "@halo-dev/components";
import PluginInstallModal from "./components/PluginInstallModal.vue";
import { onMounted, ref } from "vue"; import { onMounted, ref } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { apiClient } from "@halo-dev/admin-shared"; import { apiClient } from "@halo-dev/admin-shared";
@ -20,6 +21,7 @@ import type { Plugin } from "@halo-dev/api-client";
import cloneDeep from "lodash.clonedeep"; import cloneDeep from "lodash.clonedeep";
const plugins = ref<Plugin[]>([] as Plugin[]); const plugins = ref<Plugin[]>([] as Plugin[]);
const pluginInstall = ref(false);
const router = useRouter(); const router = useRouter();
const dialog = useDialog(); const dialog = useDialog();
@ -69,12 +71,22 @@ const handleChangeStatus = (plugin: Plugin) => {
onMounted(handleFetchPlugins); onMounted(handleFetchPlugins);
</script> </script>
<template> <template>
<PluginInstallModal
v-model:visible="pluginInstall"
v-permission="['system:plugins:manage']"
@close="handleFetchPlugins"
/>
<VPageHeader title="插件"> <VPageHeader title="插件">
<template #icon> <template #icon>
<IconPlug class="mr-2 self-center" /> <IconPlug class="mr-2 self-center" />
</template> </template>
<template #actions> <template #actions>
<VButton v-permission="['system:plugins:manage']" type="secondary"> <VButton
v-permission="['system:plugins:manage']"
type="secondary"
@click="pluginInstall = true"
>
<template #icon> <template #icon>
<IconAddCircle class="h-full w-full" /> <IconAddCircle class="h-full w-full" />
</template> </template>

View File

@ -0,0 +1,79 @@
<script lang="ts" setup>
import { useDialog, VModal } from "@halo-dev/components";
import VueFilePond from "vue-filepond";
import "filepond/dist/filepond.min.css";
import { apiClient } from "@halo-dev/admin-shared";
import type { Plugin } from "@halo-dev/api-client";
const FilePond = VueFilePond();
defineProps({
visible: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["update:visible", "close"]);
const dialog = useDialog();
const handleVisibleChange = (visible: boolean) => {
emit("update:visible", visible);
if (!visible) {
emit("close");
}
};
const server = {
process: (fieldName, file, metadata, load) => {
const formData = new FormData();
formData.append(fieldName, file, file.name);
apiClient.plugin.installPlugin(file).then((response) => {
load(response);
const plugin: Plugin = response.data as unknown as Plugin;
handleVisibleChange(false);
dialog.success({
title: "上传成功",
description: "是否启动当前安装的插件?",
onConfirm: async () => {
plugin.spec.enabled = true;
try {
await apiClient.extension.plugin.updatepluginHaloRunV1alpha1Plugin(
plugin.metadata.name,
plugin
);
window.location.reload();
} catch (e) {
console.error(e);
}
},
});
});
return {};
},
};
</script>
<template>
<VModal
:visible="visible"
:width="500"
title="安装插件"
@update:visible="handleVisibleChange"
>
<file-pond
ref="pond"
:allow-multiple="false"
:server="server"
label-idle="Drop JAR file here..."
name="file"
/>
</VModal>
</template>