github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/static_source/admin/src/views/Backups/index.vue (about)

     1  <script setup lang="ts">
     2  import {useI18n} from '@/hooks/web/useI18n'
     3  import {Table} from '@/components/Table'
     4  import {h, onMounted, onUnmounted, reactive, ref} from 'vue'
     5  import {TableColumn} from '@/types/table'
     6  import api from "@/api/api";
     7  import {ElButton, ElCol, ElMessage, ElPopconfirm, ElRow, ElUpload, UploadProps} from 'element-plus'
     8  import {useForm} from "@/hooks/web/useForm";
     9  import {ContentWrap} from "@/components/ContentWrap";
    10  import {ApiBackup} from "@/api/stub";
    11  import {parseTime} from "@/utils";
    12  import {formatBytes} from "@/views/Dashboard/core";
    13  import {useCache} from "@/hooks/web/useCache";
    14  import {UUID} from "uuid-generator-ts";
    15  import stream from "@/api/stream";
    16  
    17  const {methods} = useForm()
    18  const {wsCache} = useCache()
    19  const {t} = useI18n()
    20  
    21  interface TableObject {
    22    tableList: ApiBackup[]
    23    loading: boolean
    24  }
    25  
    26  interface Params {
    27    page?: number;
    28    limit?: number;
    29    sort?: string;
    30  }
    31  
    32  const tableObject = reactive<TableObject>(
    33    {
    34      tableList: [],
    35      loading: false,
    36    }
    37  );
    38  
    39  const currentID = ref('')
    40  
    41  const columns: TableColumn[] = [
    42    {
    43      field: 'name',
    44      label: t('backup.name'),
    45      sortable: true,
    46    },
    47    {
    48      field: 'size',
    49      label: t('backup.size'),
    50      width: "100px",
    51      formatter: (row: ApiBackup) => {
    52        return h(
    53          'span',
    54          formatBytes(row.size.toString(), 2)
    55        )
    56      }
    57    },
    58    {
    59      field: 'operations',
    60      label: t('backup.operations'),
    61      width: "170px",
    62    },
    63    {
    64      field: 'modTime',
    65      label: t('main.createdAt'),
    66      type: 'time',
    67      sortable: true,
    68      width: "170px",
    69      formatter: (row: ApiBackup) => {
    70        return h(
    71          'span',
    72          parseTime(row.modTime)
    73        )
    74      }
    75    },
    76  ]
    77  
    78  const getList = async () => {
    79    tableObject.loading = true
    80    const res = await api.v1.backupServiceGetBackupList()
    81      .catch(() => {
    82      })
    83      .finally(() => {
    84        tableObject.loading = false
    85      })
    86    if (res) {
    87      const {items, meta} = res.data;
    88      tableObject.tableList = items;
    89    } else {
    90      tableObject.tableList = [];
    91    }
    92  }
    93  
    94  const addNew = async () => {
    95    const res = await api.v1.backupServiceNewBackup({})
    96      .catch(() => {
    97      })
    98      .finally(() => {
    99      })
   100    if (res.status == 200) {
   101      ElMessage({
   102        title: t('Success'),
   103        message: t('message.createdSuccessfully'),
   104        type: 'success',
   105        duration: 2000
   106      });
   107    }
   108  }
   109  
   110  const restore = async (backup: ApiBackup) => {
   111    const res = await api.v1.backupServiceRestoreBackup(backup.name)
   112      .catch(() => {
   113      })
   114      .finally(() => {
   115      })
   116  
   117    if (res.status == 200) {
   118      ElMessage({
   119        title: t('Success'),
   120        message: t('message.callSuccessful'),
   121        type: 'success',
   122        duration: 2000
   123      });
   124    }
   125  }
   126  
   127  const remove = async (backup: ApiBackup) => {
   128    const res = await api.v1.backupServiceDeleteBackup(backup.name)
   129      .catch(() => {
   130      })
   131      .finally(() => {
   132      })
   133  
   134    if (res.status == 200) {
   135      ElMessage({
   136        title: t('Success'),
   137        message: t('message.callSuccessful'),
   138        type: 'success',
   139        duration: 2000
   140      });
   141    }
   142  }
   143  
   144  const getUploadURL = () => {
   145    let uri = import.meta.env.VITE_API_BASEPATH as string || window.location.origin;
   146    const accessToken = wsCache.get("accessToken")
   147    uri += '/v1/backup/upload?access_token=' + accessToken;
   148    const serverId = wsCache.get('serverId')
   149    if (serverId) {
   150      uri += '&server_id=' + serverId;
   151    }
   152    return uri;
   153  }
   154  
   155  
   156  const getDownloadURL = (file: ApiBackup) => {
   157    let uri = import.meta.env.VITE_API_BASEPATH as string || window.location.origin;
   158    const accessToken = wsCache.get("accessToken")
   159    uri += '/snapshots/' + file.name + '?access_token=' + accessToken;
   160    const serverId = wsCache.get('serverId')
   161    if (serverId) {
   162      uri += '&server_id=' + serverId;
   163    }
   164    return uri;
   165  }
   166  
   167  const onSuccess: UploadProps['onSuccess'] = (file: ApiBackup, uploadFile) => {
   168    ElMessage({
   169      message: t('message.uploadSuccessfully'),
   170      type: 'success',
   171      duration: 2000
   172    })
   173  }
   174  
   175  const onError: UploadProps['onError'] = (error, uploadFile, uploadFiles) => {
   176    const body = JSON.parse(error.message)
   177    const {message, code} = body.error;
   178    ElMessage({
   179      message: message,
   180      type: 'error',
   181      duration: 0
   182    })
   183  }
   184  
   185  const forceFileDownload = (file: ApiBackup) => {
   186    const link = document.createElement('a')
   187    link.href = getDownloadURL(file)
   188    link.setAttribute('download', file.name)
   189    document.body.appendChild(link)
   190    link.click()
   191  }
   192  
   193  const onEventhandler = () => {
   194    getList()
   195  }
   196  
   197  const onEventStartedRestoreHandler = () => {
   198    ElMessage({
   199      message: t('message.startedRestoreProcess'),
   200      type: 'warning',
   201      duration: 0
   202    })
   203  }
   204  
   205  onMounted(() => {
   206    const uuid = new UUID()
   207    currentID.value = uuid.getDashFreeUUID()
   208  
   209    setTimeout(() => {
   210      stream.subscribe('event_created_backup', currentID.value, onEventhandler);
   211      stream.subscribe('event_removed_backup', currentID.value, onEventhandler);
   212      stream.subscribe('event_uploaded_backup', currentID.value, onEventhandler);
   213      stream.subscribe('event_started_restore', currentID.value, onEventStartedRestoreHandler);
   214    }, 1000)
   215  })
   216  
   217  onUnmounted(() => {
   218    stream.unsubscribe('event_created_backup', currentID.value);
   219    stream.unsubscribe('event_removed_backup', currentID.value);
   220    stream.unsubscribe('event_uploaded_backup', currentID.value);
   221    stream.unsubscribe('event_started_restore', currentID.value);
   222  })
   223  
   224  getList()
   225  
   226  </script>
   227  
   228  <template>
   229    <ContentWrap>
   230      <ElRow class="file-manager-body mb-20px">
   231        <ElCol>
   232          <ElButton class="flex mb-20px items-left" type="primary" @click="addNew()" plain>
   233            <Icon icon="iconoir:database-restore" class="mr-5px"/>
   234            {{ t('backup.addNew') }}
   235          </ElButton>
   236  
   237          <!--        <ElButton class="flex mb-20px items-left" type="primary" @click="addNew()" plain>-->
   238          <!--          {{ t('backup.apply') }}-->
   239          <!--        </ElButton>-->
   240  
   241          <!--        <ElButton class="flex mb-20px items-left" type="primary" @click="addNew()" plain>-->
   242          <!--          {{ t('backup.rollback') }}-->
   243          <!--        </ElButton>-->
   244  
   245  
   246          <ElUpload
   247            class="upload-demo"
   248            :action="getUploadURL()"
   249            :multiple="true"
   250            :on-success="onSuccess"
   251            :on-error="onError"
   252            :auto-upload="true"
   253          >
   254            <ElButton type="primary" plain>
   255              <Icon icon="material-symbols:upload" class="mr-5px"/>
   256              {{ $t('backup.uploadDump') }}
   257            </ElButton>
   258          </ElUpload>
   259        </ElCol>
   260      </ElRow>
   261  
   262  
   263      <Table
   264        :selection="false"
   265        :columns="columns"
   266        :data="tableObject.tableList"
   267        :loading="tableObject.loading"
   268        style="width: 100%"
   269      >
   270        <template #operations="{ row }">
   271          <ElPopconfirm
   272            :confirm-button-text="$t('main.ok')"
   273            :cancel-button-text="$t('main.no')"
   274            width="auto"
   275            style="margin-left: 10px;"
   276            :title="$t('backup.restoreSnapshot')"
   277            @confirm="restore(row)"
   278          >
   279            <template #reference>
   280              <ElButton class="flex items-right" type="danger" link>
   281                <Icon icon="ic:baseline-restore" class="mr-5px"/>
   282              </ElButton>
   283            </template>
   284          </ElPopconfirm>
   285  
   286          <ElButton class="flex items-right" link @click="forceFileDownload(row)">
   287            <Icon icon="material-symbols:download" class="mr-5px"/>
   288          </ElButton>
   289  
   290          <ElPopconfirm
   291            :confirm-button-text="$t('main.ok')"
   292            :cancel-button-text="$t('main.no')"
   293            width="auto"
   294            style="margin-left: 10px;"
   295            :title="$t('backup.removeSnapshot')"
   296            @confirm="remove(row)"
   297          >
   298            <template #reference>
   299              <ElButton class="flex items-right" link>
   300                <Icon icon="mdi:remove" class="mr-5px"/>
   301              </ElButton>
   302            </template>
   303          </ElPopconfirm>
   304  
   305        </template>
   306      </Table>
   307    </ContentWrap>
   308  
   309  </template>
   310  
   311  <style lang="less">
   312  
   313  .el-table__row {
   314    cursor: pointer;
   315  }
   316  </style>