Skip to content

Commit

Permalink
feat: add supports for setting pat to bind with halo app store (#27)
Browse files Browse the repository at this point in the history
支持在插件中设置 Halo 官网的 Personal Access Token 以建立绑定。

<img width="626" alt="image" src="https://github.com/halo-dev/plugin-app-store/assets/21301288/b9ce86f0-36ff-4c56-bf11-ccbd0092d05e">

/kind feature

```release-note
支持在插件中设置 Halo 官网的个人令牌以建立绑定。
```
  • Loading branch information
ruibaby authored Sep 21, 2023
1 parent c340509 commit 8928e90
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 0 deletions.
99 changes: 99 additions & 0 deletions console/src/components/plugin-tabs/AccountBindingTab.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<script lang="ts" setup>
import { Toast, VAlert, VButton } from "@halo-dev/components";
import { useMutation, useQuery, useQueryClient } from "@tanstack/vue-query";
import { ref } from "vue";
import { apiClient } from "@/utils/api-client";
import type { Secret } from "@halo-dev/api-client";
import { APP_STORE_PAT_CACHE_KEY, APP_STORE_PAT_SECRET_NAME } from "@/constant";
const queryClient = useQueryClient();
const pat = ref("");
const formState = ref<Secret>({
type: "Opaque",
stringData: {
token: "",
},
apiVersion: "v1alpha1",
kind: "Secret",
metadata: {
name: APP_STORE_PAT_SECRET_NAME,
},
});
useQuery<Secret>({
queryKey: ["app-store-pat-secret"],
queryFn: async () => {
const { data } = await apiClient.extension.secret.getv1alpha1Secret(
{
name: APP_STORE_PAT_SECRET_NAME,
},
{ mute: true }
);
return data;
},
onSuccess(data) {
if (data) {
formState.value = data;
pat.value = data.stringData?.token || "";
}
},
});
const { mutate, isLoading } = useMutation({
mutationKey: ["create-pat-secret"],
mutationFn: async () => {
formState.value.stringData = {
token: pat.value,
};
if (formState.value.metadata.creationTimestamp) {
return await apiClient.extension.secret.updatev1alpha1Secret({
name: APP_STORE_PAT_SECRET_NAME,
secret: formState.value,
});
} else {
return await apiClient.extension.secret.createv1alpha1Secret({
secret: formState.value,
});
}
},
onSuccess(data) {
formState.value = data.data;
localStorage.setItem(APP_STORE_PAT_CACHE_KEY, pat.value);
queryClient.invalidateQueries({ queryKey: ["store-apps"] });
Toast.success("保存成功");
},
});
</script>

<template>
<div class="bg-white p-4">
<FormKit id="account-binding-form" type="form" name="account-binding-form" @submit="mutate()">
<div class="formkit-outer formkit-disabled:opacity-50 py-4 first:pt-0 last:pb-0 transition-all">
<VAlert type="info" title="提示" class="sm:max-w-lg" :closable="false">
<template #description>
为了能够与 Halo 官方应用市场建立绑定关系以支持下载和更新付费应用,需要先访问
<a
class="as-text-gray-900 hover:as-text-gray-600"
target="_blank"
href="https://halo.run/console/users/-/detail"
>
https://halo.run/console/users/-/detail
</a>
创建个人令牌,然后设置到下方的输入框中。
</template>
</VAlert>
</div>
<FormKit v-model="pat" type="password" label="个人令牌" />
</FormKit>

<div class="pt-5">
<div class="flex justify-start">
<VButton type="secondary" :loading="isLoading" @click="$formkit.submit('account-binding-form')"> 保存 </VButton>
</div>
</div>
</div>
</template>
4 changes: 4 additions & 0 deletions console/src/constant/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,7 @@ export enum AppType {

export const PLUGIN_NAME = "app-store-integration";
export const CONFIG_MAP_NAME = "plugin-app-store-integration-configmap";

export const APP_STORE_PAT_SECRET_NAME = "halo-run-app-store-pat-secret";
export const APP_STORE_PAT_CACHE_KEY = "halo-run-app-store-pat-cache-key";
export const APP_STORE_PAT_CACHE_VALUE_NOT_SET_FLAG = "<NOT_SET>";
10 changes: 10 additions & 0 deletions console/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import PluginBindingCheckField from "./components/entity-fields/PluginBindingChe
import ThemeBindingCheckOperationItem from "./components/operation-items/ThemeBindingCheckOperationItem.vue";
import ViewAppStoreOperationItem from "./components/operation-items/ViewAppStoreOperationItem.vue";
import { STORE_APP_ID } from "./constant";
import AccountBindingTab from "./components/plugin-tabs/AccountBindingTab.vue";

export default definePlugin({
routes: [
Expand Down Expand Up @@ -134,5 +135,14 @@ export default definePlugin({
},
];
},
"plugin:self:tabs:create": () => {
return [
{
id: "token",
label: "账号绑定",
component: markRaw(AccountBindingTab),
},
];
},
},
});
2 changes: 2 additions & 0 deletions console/src/utils/api-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ApiConsoleHaloRunV1alpha1ThemeApi,
PluginHaloRunV1alpha1PluginApi,
ThemeHaloRunV1alpha1ThemeApi,
V1alpha1SecretApi,
} from "@halo-dev/api-client";
import { Toast } from "@halo-dev/components";
import type { AxiosError, AxiosInstance } from "axios";
Expand Down Expand Up @@ -75,6 +76,7 @@ function setupApiClient(axios: AxiosInstance) {
extension: {
plugin: new PluginHaloRunV1alpha1PluginApi(undefined, baseURL, axios),
theme: new ThemeHaloRunV1alpha1ThemeApi(undefined, baseURL, axios),
secret: new V1alpha1SecretApi(undefined, baseURL, axios),
},
plugin: new ApiConsoleHaloRunV1alpha1PluginApi(undefined, baseURL, axios),
theme: new ApiConsoleHaloRunV1alpha1ThemeApi(undefined, baseURL, axios),
Expand Down
32 changes: 32 additions & 0 deletions console/src/utils/store-api-client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import axios from "axios";
import qs from "qs";
import { apiClient } from "./api-client";
import { APP_STORE_PAT_CACHE_KEY, APP_STORE_PAT_CACHE_VALUE_NOT_SET_FLAG, APP_STORE_PAT_SECRET_NAME } from "@/constant";

const storeApiClient = axios.create({
baseURL: import.meta.env.VITE_APP_STORE_BACKEND,
Expand All @@ -8,4 +10,34 @@ const storeApiClient = axios.create({
},
});

storeApiClient.interceptors.request.use(async (config) => {
try {
let cachedToken = localStorage.getItem(APP_STORE_PAT_CACHE_KEY);

if (!cachedToken) {
const { data } = await apiClient.extension.secret.getv1alpha1Secret(
{
name: APP_STORE_PAT_SECRET_NAME,
},
{ mute: true }
);
if (data.stringData?.token) {
cachedToken = data.stringData.token;
localStorage.setItem(APP_STORE_PAT_CACHE_KEY, cachedToken);
} else {
localStorage.setItem(APP_STORE_PAT_CACHE_KEY, APP_STORE_PAT_CACHE_VALUE_NOT_SET_FLAG);
}
}

if (cachedToken && cachedToken !== APP_STORE_PAT_CACHE_VALUE_NOT_SET_FLAG) {
config.headers["Authorization"] = `Bearer ${cachedToken}`;
}

return config;
} catch {
localStorage.setItem(APP_STORE_PAT_CACHE_KEY, APP_STORE_PAT_CACHE_VALUE_NOT_SET_FLAG);
return config;
}
});

export default storeApiClient;

0 comments on commit 8928e90

Please sign in to comment.