feat: add cleanup feature for post snapshots (#5822)

#### What type of PR is this?

/area ui
/kind feature
/milestone 2.15.x

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

支持清理所有无用的文章历史快照,仅保留已发布、基础版本、正在编辑的版本。

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

Fixes https://github.com/halo-dev/halo/issues/5817

#### Special notes for your reviewer:

需要在历史版本页面测试清理功能是否符合预期。

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

```release-note
None
```
pull/5838/head
Ryan Wang 2024-04-29 16:41:08 +08:00 committed by GitHub
parent d1d4705705
commit 966558d1ce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 156 additions and 8 deletions

View File

@ -1,12 +1,15 @@
<script setup lang="ts">
import {
Dialog,
IconHistoryLine,
Toast,
VButton,
VCard,
VLoading,
VPageHeader,
VSpace,
} from "@halo-dev/components";
import { useQuery } from "@tanstack/vue-query";
import { useQuery, useQueryClient } from "@tanstack/vue-query";
import { useRoute } from "vue-router";
import { apiClient } from "@/utils/api-client";
import { computed, watch } from "vue";
@ -14,8 +17,11 @@ import { OverlayScrollbarsComponent } from "overlayscrollbars-vue";
import { useRouteQuery } from "@vueuse/router";
import SnapshotContent from "./components/SnapshotContent.vue";
import SnapshotListItem from "./components/SnapshotListItem.vue";
import { useI18n } from "vue-i18n";
const queryClient = useQueryClient();
const route = useRoute();
const { t } = useI18n();
const singlePageName = computed(() => route.query.name as string);
@ -70,6 +76,43 @@ watch(
immediate: true,
}
);
function handleCleanup() {
Dialog.warning({
title: t("core.page_snapshots.operations.cleanup.title"),
description: t("core.page_snapshots.operations.cleanup.description"),
confirmText: t("core.common.buttons.confirm"),
cancelText: t("core.common.buttons.cancel"),
async onConfirm() {
const { releaseSnapshot, baseSnapshot, headSnapshot } =
singlePage.value?.spec || {};
const snapshotsToDelete = snapshots.value?.filter((snapshot) => {
const { name } = snapshot.metadata;
return ![releaseSnapshot, baseSnapshot, headSnapshot]
.filter(Boolean)
.includes(name);
});
if (!snapshotsToDelete?.length) {
Toast.info(t("core.page_snapshots.operations.cleanup.toast_empty"));
return;
}
for (let i = 0; i < snapshotsToDelete?.length; i++) {
await apiClient.singlePage.deleteSinglePageContent({
name: singlePageName.value,
snapshotName: snapshotsToDelete[i].metadata.name,
});
}
await queryClient.invalidateQueries({
queryKey: ["singlePage-snapshots-by-singlePage-name", singlePageName],
});
Toast.success(t("core.page_snapshots.operations.cleanup.toast_success"));
},
});
}
</script>
<template>
@ -78,9 +121,14 @@ watch(
<IconHistoryLine class="mr-2 self-center" />
</template>
<template #actions>
<VButton size="sm" @click="$router.back()">
{{ $t("core.common.buttons.back") }}
</VButton>
<VSpace>
<VButton size="sm" @click="$router.back()">
{{ $t("core.common.buttons.back") }}
</VButton>
<VButton size="sm" type="danger" @click="handleCleanup">
{{ $t("core.page_snapshots.operations.cleanup.button") }}
</VButton>
</VSpace>
</template>
</VPageHeader>

View File

@ -109,6 +109,9 @@ const isBase = computed(() => {
<VTag v-if="isHead">
{{ $t("core.page_snapshots.status.draft") }}
</VTag>
<VTag v-if="isBase">
{{ $t("core.page_snapshots.status.base") }}
</VTag>
<VStatusDot
v-if="snapshot.metadata.deletionTimestamp"
v-tooltip="$t('core.common.status.deleting')"

View File

@ -1,12 +1,15 @@
<script setup lang="ts">
import {
Dialog,
IconHistoryLine,
Toast,
VButton,
VCard,
VLoading,
VPageHeader,
VSpace,
} from "@halo-dev/components";
import { useQuery } from "@tanstack/vue-query";
import { useQuery, useQueryClient } from "@tanstack/vue-query";
import { useRoute } from "vue-router";
import { apiClient } from "@/utils/api-client";
import { computed, watch } from "vue";
@ -14,8 +17,11 @@ import { OverlayScrollbarsComponent } from "overlayscrollbars-vue";
import { useRouteQuery } from "@vueuse/router";
import SnapshotContent from "@console/modules/contents/posts/components/SnapshotContent.vue";
import SnapshotListItem from "@console/modules/contents/posts/components/SnapshotListItem.vue";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
const route = useRoute();
const queryClient = useQueryClient();
const postName = computed(() => route.query.name as string);
@ -70,6 +76,43 @@ watch(
immediate: true,
}
);
function handleCleanup() {
Dialog.warning({
title: t("core.post_snapshots.operations.cleanup.title"),
description: t("core.post_snapshots.operations.cleanup.description"),
confirmText: t("core.common.buttons.confirm"),
cancelText: t("core.common.buttons.cancel"),
async onConfirm() {
const { releaseSnapshot, baseSnapshot, headSnapshot } =
post.value?.spec || {};
const snapshotsToDelete = snapshots.value?.filter((snapshot) => {
const { name } = snapshot.metadata;
return ![releaseSnapshot, baseSnapshot, headSnapshot]
.filter(Boolean)
.includes(name);
});
if (!snapshotsToDelete?.length) {
Toast.info(t("core.post_snapshots.operations.cleanup.toast_empty"));
return;
}
for (let i = 0; i < snapshotsToDelete?.length; i++) {
await apiClient.post.deletePostContent({
name: postName.value,
snapshotName: snapshotsToDelete[i].metadata.name,
});
}
await queryClient.invalidateQueries({
queryKey: ["post-snapshots-by-post-name", postName],
});
Toast.success(t("core.post_snapshots.operations.cleanup.toast_success"));
},
});
}
</script>
<template>
@ -78,9 +121,14 @@ watch(
<IconHistoryLine class="mr-2 self-center" />
</template>
<template #actions>
<VButton size="sm" @click="$router.back()">
{{ $t("core.common.buttons.back") }}
</VButton>
<VSpace>
<VButton size="sm" @click="$router.back()">
{{ $t("core.common.buttons.back") }}
</VButton>
<VButton size="sm" type="danger" @click="handleCleanup">
{{ $t("core.post_snapshots.operations.cleanup.button") }}
</VButton>
</VSpace>
</template>
</VPageHeader>

View File

@ -107,6 +107,9 @@ const isBase = computed(() => {
<VTag v-if="isHead">
{{ $t("core.post_snapshots.status.draft") }}
</VTag>
<VTag v-if="isBase">
{{ $t("core.post_snapshots.status.base") }}
</VTag>
<VStatusDot
v-if="snapshot.metadata.deletionTimestamp"
v-tooltip="$t('core.common.status.deleting')"

View File

@ -1692,9 +1692,18 @@ core:
description: >-
Are you sure you want to delete this snapshot? This operation is
irreversible.
cleanup:
button: Cleanup
title: Cleanup snapshots
description: >-
Are you sure you want to delete all unused snapshots? Only published,
base version, and draft versions will be retained.
toast_empty: There are no snapshots to be cleaned up
toast_success: Cleanup completed
status:
released: Released
draft: Draft
base: Base
title: Post snapshots
page_snapshots:
operations:
@ -1710,7 +1719,16 @@ core:
description: >-
Are you sure you want to delete this snapshot? This operation is
irreversible.
cleanup:
button: Cleanup
title: Cleanup snapshots
description: >-
Are you sure you want to delete all unused snapshots? Only published,
base version, and draft versions will be retained.
toast_empty: There are no snapshots to be cleaned up
toast_success: Cleanup completed
status:
released: Released
draft: Draft
base: Base
title: Page snapshots

View File

@ -1592,9 +1592,16 @@ core:
delete:
title: 删除快照
description: 确定要删除该快照吗?此操作无法恢复。
cleanup:
button: 清理
title: 清理快照
description: 确定要清理所有不再使用的快照吗?只会保留已发布、基础和草稿版本。
toast_empty: 没有需要清理的快照
toast_success: 清理完成
status:
released: 已发布
draft: 草稿
base: 基础
title: 文章版本历史
page_snapshots:
operations:
@ -1606,7 +1613,14 @@ core:
delete:
title: 删除快照
description: 确定要删除该快照吗?此操作无法恢复。
cleanup:
button: 清理
title: 清理快照
description: 确定要清理所有不再使用的快照吗?只会保留已发布、基础和草稿版本。
toast_empty: 没有需要清理的快照
toast_success: 清理完成
status:
released: 已发布
draft: 草稿
base: 基础
title: 页面版本历史

View File

@ -1586,9 +1586,16 @@ core:
delete:
title: 刪除快照
description: 確定要刪除該快照嗎?此操作無法恢復。
cleanup:
button: 清理
title: 清理快照
description: 確定要清理所有不再使用的快照嗎?只會保留已發佈、基礎和草稿版本。
toast_empty: 沒有需要清理的快照
toast_success: 清理完成
status:
released: 已發布
draft: 草稿
base: 基礎
title: 文章版本歷史
page_snapshots:
operations:
@ -1600,7 +1607,14 @@ core:
delete:
title: 刪除快照
description: 確定要刪除該快照嗎?此操作無法恢復。
cleanup:
button: 清理
title: 清理快照
description: 確定要清理所有不再使用的快照嗎?只會保留已發佈、基礎和草稿版本。
toast_empty: 沒有需要清理的快照
toast_success: 清理完成
status:
released: 已發布
draft: 草稿
base: 基礎
title: 頁面版本歷史