Fix nonblocking preview actions (#1188)
This commit is contained in:
@@ -28,12 +28,20 @@ export interface PreviewStatus {
|
||||
webui_home: string
|
||||
action_log_path: string
|
||||
dev_log_path: string
|
||||
active_action: string | null
|
||||
active_action_started_at: string | null
|
||||
last_action: string | null
|
||||
last_action_completed_at: string | null
|
||||
last_action_success: boolean | null
|
||||
last_action_message: string
|
||||
last_action_code: string
|
||||
action_log: string
|
||||
dev_log: string
|
||||
}
|
||||
|
||||
export interface PreviewActionResponse extends PreviewStatus {
|
||||
success: boolean
|
||||
accepted?: boolean
|
||||
message?: string
|
||||
code?: string
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import { NAlert, NButton, NDescriptions, NDescriptionsItem, NSelect, NSpace, NTag, useMessage } from 'naive-ui'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import {
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
preparePreview,
|
||||
startPreview,
|
||||
stopPreview,
|
||||
type PreviewActionResponse,
|
||||
type PreviewStatus,
|
||||
type PreviewTag,
|
||||
} from '@/api/hermes/system'
|
||||
@@ -22,6 +23,9 @@ const actionLoading = ref('')
|
||||
const tags = ref<PreviewTag[]>([])
|
||||
const selectedTag = ref('')
|
||||
const status = ref<PreviewStatus | null>(null)
|
||||
const lastHandledCompletion = ref('')
|
||||
const completionNotificationsReady = ref(false)
|
||||
let pollTimer: number | null = null
|
||||
|
||||
const tagOptions = computed(() => tags.value.map(tag => ({
|
||||
label: tag.name,
|
||||
@@ -29,6 +33,14 @@ const tagOptions = computed(() => tags.value.map(tag => ({
|
||||
})))
|
||||
const actionLog = computed(() => status.value?.action_log || '')
|
||||
const devLog = computed(() => status.value?.dev_log || '')
|
||||
const activeAction = computed(() => actionLoading.value || status.value?.active_action || '')
|
||||
const hasActiveAction = computed(() => Boolean(activeAction.value))
|
||||
const actionSuccessKeys: Record<string, string> = {
|
||||
prepare: 'githubPreview.prepareSuccess',
|
||||
install: 'githubPreview.installSuccess',
|
||||
start: 'githubPreview.startSuccess',
|
||||
stop: 'githubPreview.stopSuccess',
|
||||
}
|
||||
|
||||
function applyErrorStatus(err: any) {
|
||||
const messageText = String(err?.message || '')
|
||||
@@ -88,7 +100,7 @@ async function handleRefresh() {
|
||||
}
|
||||
}
|
||||
|
||||
async function runAction(action: string, fn: () => Promise<PreviewStatus & { success?: boolean; message?: string; code?: string }>, successKey: string) {
|
||||
async function runAction(action: string, fn: () => Promise<PreviewActionResponse>, successKey: string) {
|
||||
actionLoading.value = action
|
||||
try {
|
||||
const res = await fn()
|
||||
@@ -97,7 +109,9 @@ async function runAction(action: string, fn: () => Promise<PreviewStatus & { suc
|
||||
message.warning(errorCodeMessage(res.code, res.message))
|
||||
return
|
||||
}
|
||||
message.success(t(successKey))
|
||||
if (!res.accepted && !res.active_action) {
|
||||
message.success(t(successKey))
|
||||
}
|
||||
} catch (err: any) {
|
||||
applyErrorStatus(err)
|
||||
const payload = parseErrorPayload(err)
|
||||
@@ -107,6 +121,25 @@ async function runAction(action: string, fn: () => Promise<PreviewStatus & { suc
|
||||
}
|
||||
}
|
||||
|
||||
async function pollStatus() {
|
||||
try {
|
||||
await loadStatus()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function startPolling() {
|
||||
if (pollTimer) return
|
||||
pollTimer = window.setInterval(() => {
|
||||
void pollStatus()
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
function stopPolling() {
|
||||
if (!pollTimer) return
|
||||
window.clearInterval(pollTimer)
|
||||
pollTimer = null
|
||||
}
|
||||
|
||||
function requireTag(): string | null {
|
||||
if (!selectedTag.value) {
|
||||
message.warning(t('githubPreview.selectTag'))
|
||||
@@ -124,7 +157,7 @@ async function handlePrepare() {
|
||||
async function handleInstall() {
|
||||
await runAction('install', async () => {
|
||||
const res = await installPreview()
|
||||
if (res.success !== false && !res.installed) {
|
||||
if (res.success !== false && !res.accepted && !res.active_action && !res.installed) {
|
||||
return {
|
||||
...res,
|
||||
success: false,
|
||||
@@ -145,7 +178,41 @@ async function handleStop() {
|
||||
|
||||
onMounted(async () => {
|
||||
await handleRefresh()
|
||||
lastHandledCompletion.value = status.value?.last_action_completed_at || ''
|
||||
completionNotificationsReady.value = true
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
stopPolling()
|
||||
})
|
||||
|
||||
watch(
|
||||
() => status.value?.active_action || '',
|
||||
(action) => {
|
||||
if (action) startPolling()
|
||||
else stopPolling()
|
||||
},
|
||||
)
|
||||
|
||||
watch(
|
||||
() => status.value?.last_action_completed_at || '',
|
||||
(completedAt) => {
|
||||
if (!completedAt) return
|
||||
if (!completionNotificationsReady.value) {
|
||||
lastHandledCompletion.value = completedAt
|
||||
return
|
||||
}
|
||||
if (completedAt === lastHandledCompletion.value || actionLoading.value) return
|
||||
lastHandledCompletion.value = completedAt
|
||||
const completedAction = status.value?.last_action || ''
|
||||
if (status.value?.last_action_success === false) {
|
||||
message.error(errorCodeMessage(status.value.last_action_code, status.value.last_action_message))
|
||||
return
|
||||
}
|
||||
const successKey = actionSuccessKeys[completedAction]
|
||||
if (successKey) message.success(t(successKey))
|
||||
},
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -161,16 +228,16 @@ onMounted(async () => {
|
||||
:placeholder="t('githubPreview.selectTag')"
|
||||
/>
|
||||
<NSpace>
|
||||
<NButton type="primary" :loading="actionLoading === 'prepare'" :disabled="!selectedTag" @click="handlePrepare">
|
||||
<NButton type="primary" :loading="activeAction === 'prepare'" :disabled="hasActiveAction || !selectedTag" @click="handlePrepare">
|
||||
{{ t('githubPreview.prepare') }}
|
||||
</NButton>
|
||||
<NButton :loading="actionLoading === 'install'" :disabled="!status?.has_package" @click="handleInstall">
|
||||
<NButton :loading="activeAction === 'install'" :disabled="hasActiveAction || !status?.has_package" @click="handleInstall">
|
||||
{{ t('githubPreview.install') }}
|
||||
</NButton>
|
||||
<NButton type="success" :loading="actionLoading === 'start'" :disabled="!status?.installed" @click="handleStart">
|
||||
<NButton type="success" :loading="activeAction === 'start'" :disabled="hasActiveAction || !status?.installed" @click="handleStart">
|
||||
{{ t('githubPreview.start') }}
|
||||
</NButton>
|
||||
<NButton :loading="actionLoading === 'stop'" :disabled="!status?.running" @click="handleStop">
|
||||
<NButton :loading="activeAction === 'stop'" :disabled="hasActiveAction || !status?.running" @click="handleStop">
|
||||
{{ t('githubPreview.stop') }}
|
||||
</NButton>
|
||||
<NButton :loading="loading || tagsLoading" @click="handleRefresh">
|
||||
|
||||
@@ -1036,7 +1036,7 @@ jobTriggered: 'Job ausgelost',
|
||||
nodeEnvironmentMissing: "Node/npm wurde nicht erkannt. Bitte installiere Node.js und versuche es erneut.",
|
||||
prepareSuccess: "Vorschaucode ist bereit",
|
||||
installSuccess: "Abhängigkeiten installiert",
|
||||
startSuccess: "Vorschau gestartet",
|
||||
startSuccess: "Vorschau abgeschlossen",
|
||||
stopSuccess: "Vorschau gestoppt",
|
||||
},
|
||||
|
||||
|
||||
@@ -1138,7 +1138,7 @@ export default {
|
||||
nodeEnvironmentMissing: "Node/npm was not detected. Please install Node.js and try again.",
|
||||
prepareSuccess: "Preview code is ready",
|
||||
installSuccess: "Dependencies installed",
|
||||
startSuccess: "Preview started",
|
||||
startSuccess: "Preview completed",
|
||||
stopSuccess: "Preview stopped",
|
||||
},
|
||||
|
||||
|
||||
@@ -1036,7 +1036,7 @@ jobTriggered: 'Job ejecutado',
|
||||
nodeEnvironmentMissing: "No se detectó Node/npm. Instala Node.js y vuelve a intentarlo.",
|
||||
prepareSuccess: "Código de vista previa listo",
|
||||
installSuccess: "Dependencias instaladas",
|
||||
startSuccess: "Vista previa iniciada",
|
||||
startSuccess: "Vista previa completada",
|
||||
stopSuccess: "Vista previa detenida",
|
||||
},
|
||||
|
||||
|
||||
@@ -1036,7 +1036,7 @@ jobTriggered: 'Job declenche',
|
||||
nodeEnvironmentMissing: "Node/npm n’a pas été détecté. Installez Node.js puis réessayez.",
|
||||
prepareSuccess: "Code de prévisualisation prêt",
|
||||
installSuccess: "Dépendances installées",
|
||||
startSuccess: "Prévisualisation démarrée",
|
||||
startSuccess: "Prévisualisation terminée",
|
||||
stopSuccess: "Prévisualisation arrêtée",
|
||||
},
|
||||
|
||||
|
||||
@@ -1036,7 +1036,7 @@ export default {
|
||||
nodeEnvironmentMissing: "Node/npm が検出されませんでした。Node.js をインストールしてから再試行してください。",
|
||||
prepareSuccess: "プレビューコードの準備が完了しました",
|
||||
installSuccess: "依存関係をインストールしました",
|
||||
startSuccess: "プレビューを起動しました",
|
||||
startSuccess: "プレビューが完了しました",
|
||||
stopSuccess: "プレビューを停止しました",
|
||||
},
|
||||
|
||||
|
||||
@@ -1036,7 +1036,7 @@ export default {
|
||||
nodeEnvironmentMissing: "Node/npm 환경을 찾을 수 없습니다. Node.js를 설치한 뒤 다시 시도하세요.",
|
||||
prepareSuccess: "미리보기 코드가 준비되었습니다",
|
||||
installSuccess: "의존성이 설치되었습니다",
|
||||
startSuccess: "미리보기가 시작되었습니다",
|
||||
startSuccess: "미리보기가 완료되었습니다",
|
||||
stopSuccess: "미리보기가 중지되었습니다",
|
||||
},
|
||||
|
||||
|
||||
@@ -1036,7 +1036,7 @@ jobTriggered: 'Job acionado',
|
||||
nodeEnvironmentMissing: "Node/npm não foi detectado. Instale o Node.js e tente novamente.",
|
||||
prepareSuccess: "Código de prévia pronto",
|
||||
installSuccess: "Dependências instaladas",
|
||||
startSuccess: "Prévia iniciada",
|
||||
startSuccess: "Prévia concluída",
|
||||
stopSuccess: "Prévia parada",
|
||||
},
|
||||
|
||||
|
||||
@@ -1130,7 +1130,7 @@ export default {
|
||||
nodeEnvironmentMissing: "未偵測到可用的 Node/npm 環境,請先安裝 Node.js 後重試。",
|
||||
prepareSuccess: "預覽程式碼已準備好",
|
||||
installSuccess: "依賴安裝完成",
|
||||
startSuccess: "預覽已啟動",
|
||||
startSuccess: "預覽已完成",
|
||||
stopSuccess: "預覽已停止",
|
||||
},
|
||||
|
||||
|
||||
@@ -1130,7 +1130,7 @@ export default {
|
||||
nodeEnvironmentMissing: "未检测到可用的 Node/npm 环境,请先安装 Node.js 后重试。",
|
||||
prepareSuccess: "预览代码已准备好",
|
||||
installSuccess: "依赖安装完成",
|
||||
startSuccess: "预览已启动",
|
||||
startSuccess: "预览已完成",
|
||||
stopSuccess: "预览已停止",
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user