PanelUpdateModal.vue 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. <script setup>
  2. import { useI18n } from 'vue-i18n';
  3. import { Modal, message } from 'ant-design-vue';
  4. import { CloudDownloadOutlined } from '@ant-design/icons-vue';
  5. import { HttpUtil, PromiseUtil } from '@/utils';
  6. import axios from 'axios';
  7. const { t } = useI18n();
  8. const props = defineProps({
  9. open: { type: Boolean, default: false },
  10. info: {
  11. type: Object,
  12. default: () => ({ currentVersion: '', latestVersion: '', updateAvailable: false }),
  13. },
  14. });
  15. const emit = defineEmits(['update:open', 'busy']);
  16. function close() {
  17. emit('update:open', false);
  18. }
  19. function updatePanel() {
  20. Modal.confirm({
  21. title: t('pages.index.panelUpdateDialog'),
  22. content: t('pages.index.panelUpdateDialogDesc').replace('#version#', props.info.latestVersion || ''),
  23. okText: t('confirm'),
  24. cancelText: t('cancel'),
  25. onOk: async () => {
  26. const baseTip = t('pages.index.dontRefresh');
  27. const tip = props.info.latestVersion ? `${baseTip} (${props.info.latestVersion})` : baseTip;
  28. close();
  29. emit('busy', { busy: true, tip });
  30. const msg = await HttpUtil.post('/panel/api/server/updatePanel');
  31. if (!msg?.success) {
  32. emit('busy', { busy: false });
  33. return;
  34. }
  35. // Wait for the running process to exit, then poll the new panel
  36. // until it answers (up to ~90s). Reload as soon as it's back.
  37. await PromiseUtil.sleep(5000);
  38. const deadline = Date.now() + 90_000;
  39. let back = false;
  40. while (Date.now() < deadline) {
  41. try {
  42. const r = await axios.get('/panel/api/server/status', { timeout: 2000 });
  43. if (r?.data?.success) { back = true; break; }
  44. } catch (_) { /* still restarting */ }
  45. await PromiseUtil.sleep(2000);
  46. }
  47. if (back) {
  48. message.success(t('pages.index.panelUpdateStartedPopover'));
  49. await PromiseUtil.sleep(800);
  50. }
  51. window.location.reload();
  52. },
  53. });
  54. }
  55. </script>
  56. <template>
  57. <a-modal :open="open" :title="t('pages.index.updatePanel')" :closable="true" :footer="null" @cancel="close">
  58. <a-alert v-if="info.updateAvailable" type="warning" class="mb-12" :message="t('pages.index.panelUpdateDesc')"
  59. show-icon />
  60. <a-list bordered class="version-list">
  61. <a-list-item class="version-list-item">
  62. <span>{{ t('pages.index.currentPanelVersion') }}</span>
  63. <a-tag color="green">v{{ info.currentVersion || '?' }}</a-tag>
  64. </a-list-item>
  65. <a-list-item v-if="info.updateAvailable" class="version-list-item">
  66. <span>{{ t('pages.index.latestPanelVersion') }}</span>
  67. <a-tag color="purple">{{ info.latestVersion || '-' }}</a-tag>
  68. </a-list-item>
  69. <a-list-item v-else class="version-list-item">
  70. <span>{{ t('pages.index.panelUpToDate') }}</span>
  71. <a-tag color="green">{{ t('pages.index.panelUpToDate') }}</a-tag>
  72. </a-list-item>
  73. </a-list>
  74. <div class="actions-row">
  75. <a-button type="primary" :disabled="!info.updateAvailable" @click="updatePanel">
  76. <template #icon>
  77. <CloudDownloadOutlined />
  78. </template>
  79. {{ t('pages.index.updatePanel') }}
  80. </a-button>
  81. </div>
  82. </a-modal>
  83. </template>
  84. <style scoped>
  85. .mb-12 {
  86. margin-bottom: 12px;
  87. }
  88. .version-list {
  89. width: 100%;
  90. }
  91. .version-list-item {
  92. display: flex;
  93. justify-content: space-between;
  94. }
  95. .actions-row {
  96. display: flex;
  97. justify-content: flex-end;
  98. margin-top: 12px;
  99. }
  100. </style>