mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 18:35:51 +02:00
280 lines
6.5 KiB
Vue
280 lines
6.5 KiB
Vue
<script setup lang="ts">
|
|
import type { Environment } from '@/api/environment'
|
|
import type { Ref } from 'vue'
|
|
import upgrade, { type RuntimeInfo } from '@/api/upgrade'
|
|
import websocket from '@/lib/websocket'
|
|
import _ from 'lodash'
|
|
import { marked } from 'marked'
|
|
import { useRoute } from 'vue-router'
|
|
|
|
const route = useRoute()
|
|
const visible = ref(false)
|
|
const nodeIds = ref<number[]>([])
|
|
const nodes = ref<Environment[]>([])
|
|
const channel = ref('stable')
|
|
const nodeNames = computed(() => nodes.value.map(v => v.name).join(', '))
|
|
const loading = ref(false)
|
|
|
|
const data = ref({
|
|
name: '',
|
|
}) as Ref<RuntimeInfo>
|
|
|
|
const modalVisible = ref(false)
|
|
const modalClosable = ref(false)
|
|
const getReleaseError = ref(false)
|
|
const progressPercent = ref(0)
|
|
const progressStatus = ref('active') as Ref<'normal' | 'active' | 'success' | 'exception'>
|
|
const showLogContainer = ref(false)
|
|
|
|
const progressStrokeColor = {
|
|
from: '#108ee9',
|
|
to: '#87d068',
|
|
}
|
|
|
|
const logContainer = useTemplateRef('logContainer')
|
|
function log(msg: string) {
|
|
const para = document.createElement('p')
|
|
|
|
para.appendChild(document.createTextNode($gettext(msg)))
|
|
|
|
logContainer.value!.appendChild(para)
|
|
|
|
nextTick(() => {
|
|
logContainer.value!.scroll({ top: logContainer.value!.scrollHeight, left: 0, behavior: 'smooth' })
|
|
})
|
|
}
|
|
|
|
const progressPercentComputed = computed(() => {
|
|
return Number.parseFloat(progressPercent.value.toFixed(1))
|
|
})
|
|
|
|
function getLatestRelease() {
|
|
loading.value = true
|
|
data.value.body = ''
|
|
upgrade.get_latest_release(channel.value).then(r => {
|
|
data.value = r
|
|
}).catch(e => {
|
|
getReleaseError.value = e?.message
|
|
}).finally(() => {
|
|
loading.value = false
|
|
})
|
|
}
|
|
|
|
function open(selectedNodeIds: Ref<number[]>, selectedNodes: Ref<Environment[]>) {
|
|
showLogContainer.value = false
|
|
visible.value = true
|
|
nodeIds.value = selectedNodeIds.value
|
|
nodes.value = _.cloneDeep(selectedNodes.value)
|
|
getLatestRelease()
|
|
}
|
|
|
|
watch(channel, getLatestRelease)
|
|
|
|
defineExpose({
|
|
open,
|
|
})
|
|
|
|
const dryRun = computed(() => {
|
|
return !!route.query.dry_run
|
|
})
|
|
|
|
async function performUpgrade() {
|
|
showLogContainer.value = true
|
|
progressStatus.value = 'active'
|
|
modalClosable.value = false
|
|
modalVisible.value = true
|
|
progressPercent.value = 0
|
|
logContainer.value!.innerHTML = ''
|
|
|
|
log($gettext('Upgrading Nginx UI, please wait...'))
|
|
|
|
const nodesNum = nodes.value.length
|
|
|
|
for (let i = 0; i < nodesNum; i++) {
|
|
await new Promise(resolve => {
|
|
const ws = websocket(`/api/upgrade/perform?x_node_id=${nodeIds.value[i]}`, false)
|
|
|
|
let last = 0
|
|
|
|
ws.onopen = () => {
|
|
ws.send(JSON.stringify({
|
|
dry_run: dryRun.value,
|
|
channel: channel.value,
|
|
}))
|
|
}
|
|
let isFailed = false
|
|
|
|
ws.onmessage = async m => {
|
|
const r = JSON.parse(m.data)
|
|
if (r.message)
|
|
log(r.message)
|
|
switch (r.status) {
|
|
case 'info':
|
|
progressPercent.value += (10 / nodesNum)
|
|
break
|
|
case 'progress':
|
|
progressPercent.value += ((r.progress - last) / 2) / nodesNum
|
|
last = r.progress
|
|
break
|
|
case 'error':
|
|
log('Upgraded successfully')
|
|
isFailed = true
|
|
break
|
|
default:
|
|
modalClosable.value = true
|
|
break
|
|
}
|
|
}
|
|
|
|
ws.onerror = () => {
|
|
resolve({})
|
|
}
|
|
|
|
ws.onclose = async () => {
|
|
resolve({})
|
|
|
|
progressPercent.value = 100 * ((i + 1) / nodesNum)
|
|
if (!isFailed)
|
|
log($gettext('Upgraded Nginx UI on %{node} successfully 🎉', { node: nodes.value[i].name }))
|
|
|
|
if (i + 1 === nodesNum) {
|
|
progressStatus.value = 'success'
|
|
modalClosable.value = true
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<AModal
|
|
v-model:open="visible"
|
|
:title="$gettext('Batch Upgrade')"
|
|
:footer="false"
|
|
:mask="false"
|
|
width="800px"
|
|
>
|
|
<AForm layout="vertical">
|
|
<AFormItem
|
|
:label="$gettext('Channel')"
|
|
class="max-w-40"
|
|
>
|
|
<ASelect v-model:value="channel">
|
|
<ASelectOption key="stable">
|
|
{{ $gettext('Stable') }}
|
|
</ASelectOption>
|
|
<ASelectOption key="prerelease">
|
|
{{ $gettext('Pre-release') }}
|
|
</ASelectOption>
|
|
</ASelect>
|
|
</AFormItem>
|
|
</AForm>
|
|
|
|
<ASpin :spinning="loading">
|
|
<AAlert
|
|
v-if="getReleaseError"
|
|
type="error"
|
|
:title="$gettext('Get release information error')"
|
|
:message="getReleaseError"
|
|
banner
|
|
/>
|
|
<template v-else>
|
|
<p>{{ $gettext('This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}.', { nodeNames, version: data.name }) }}</p>
|
|
|
|
<AAlert
|
|
v-if="dryRun"
|
|
type="info"
|
|
class="mb-4"
|
|
:message="$gettext('Dry run mode enabled')"
|
|
banner
|
|
/>
|
|
|
|
<div
|
|
v-show="showLogContainer"
|
|
class="mb-4"
|
|
>
|
|
<AProgress
|
|
:stroke-color="progressStrokeColor"
|
|
:percent="progressPercentComputed"
|
|
:status="progressStatus"
|
|
/>
|
|
|
|
<div
|
|
ref="logContainer"
|
|
class="core-upgrade-log-container"
|
|
/>
|
|
</div>
|
|
<div v-show="!showLogContainer && data.body">
|
|
<h1 class="latest-version">
|
|
{{ data.name }}
|
|
<ATag
|
|
v-if="channel === 'stable'"
|
|
color="green"
|
|
>
|
|
{{ $gettext('Stable') }}
|
|
</ATag>
|
|
<ATag
|
|
v-if="channel === 'prerelease'"
|
|
color="blue"
|
|
>
|
|
{{ $gettext('Pre-release') }}
|
|
</ATag>
|
|
</h1>
|
|
<div v-dompurify-html="marked.parse(data.body)" />
|
|
</div>
|
|
|
|
<div class="flex justify-end">
|
|
<AButton
|
|
v-if="!showLogContainer"
|
|
type="primary"
|
|
@click="performUpgrade"
|
|
>
|
|
{{ $gettext('Perform') }}
|
|
</AButton>
|
|
</div>
|
|
</template>
|
|
</ASpin>
|
|
</AModal>
|
|
</template>
|
|
|
|
<style lang="less">
|
|
.dark {
|
|
.core-upgrade-log-container {
|
|
background-color: rgba(0, 0, 0, 0.84) !important;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<style scoped lang="less">
|
|
:deep(.core-upgrade-log-container) {
|
|
height: 320px;
|
|
overflow-y: auto;
|
|
background-color: #f3f3f3;
|
|
border-radius: 4px;
|
|
margin-top: 15px;
|
|
padding: 10px;
|
|
|
|
p {
|
|
font-size: 12px;
|
|
line-height: 1.3;
|
|
}
|
|
}
|
|
|
|
.latest-version {
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
span.ant-tag {
|
|
margin-left: 10px;
|
|
}
|
|
}
|
|
|
|
:deep(h1) {
|
|
font-size: 20px;
|
|
}
|
|
|
|
:deep(h2) {
|
|
font-size: 18px;
|
|
}
|
|
</style>
|