option backup restore #238
This commit is contained in:
@@ -2,12 +2,14 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"s-ui/database"
|
||||||
"s-ui/logger"
|
"s-ui/logger"
|
||||||
"s-ui/service"
|
"s-ui/service"
|
||||||
"s-ui/util"
|
"s-ui/util"
|
||||||
"s-ui/util/common"
|
"s-ui/util/common"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@@ -109,6 +111,15 @@ func (a *APIHandler) postHandler(c *gin.Context) {
|
|||||||
link := c.Request.FormValue("link")
|
link := c.Request.FormValue("link")
|
||||||
result, _, err := util.GetOutbound(link, 0)
|
result, _, err := util.GetOutbound(link, 0)
|
||||||
jsonObj(c, result, err)
|
jsonObj(c, result, err)
|
||||||
|
case "importdb":
|
||||||
|
file, _, err := c.Request.FormFile("db")
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, "", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
err = database.ImportDB(file)
|
||||||
|
jsonMsg(c, "", err)
|
||||||
default:
|
default:
|
||||||
jsonMsg(c, "failed", common.NewError("unknown action: ", action))
|
jsonMsg(c, "failed", common.NewError("unknown action: ", action))
|
||||||
}
|
}
|
||||||
@@ -188,6 +199,16 @@ func (a *APIHandler) getHandler(c *gin.Context) {
|
|||||||
options := c.Query("o")
|
options := c.Query("o")
|
||||||
keypair := a.ServerService.GenKeypair(kType, options)
|
keypair := a.ServerService.GenKeypair(kType, options)
|
||||||
jsonObj(c, keypair, nil)
|
jsonObj(c, keypair, nil)
|
||||||
|
case "getdb":
|
||||||
|
exclude := c.Query("exclude")
|
||||||
|
db, err := database.GetDb(exclude)
|
||||||
|
if err != nil {
|
||||||
|
jsonMsg(c, "", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Header("Content-Type", "application/octet-stream")
|
||||||
|
c.Header("Content-Disposition", "attachment; filename=s-ui_"+time.Now().Format("20060102-150405")+".db")
|
||||||
|
c.Writer.Write(db)
|
||||||
default:
|
default:
|
||||||
jsonMsg(c, "failed", common.NewError("unknown action: ", action))
|
jsonMsg(c, "failed", common.NewError("unknown action: ", action))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,271 @@
|
|||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"s-ui/cmd/migration"
|
||||||
|
"s-ui/config"
|
||||||
|
"s-ui/database/model"
|
||||||
|
"s-ui/logger"
|
||||||
|
"s-ui/util/common"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/driver/sqlite"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetDb(exclude string) ([]byte, error) {
|
||||||
|
exclude_changes, exclude_stats := false, false
|
||||||
|
for _, table := range strings.Split(exclude, ",") {
|
||||||
|
if table == "changes" {
|
||||||
|
exclude_changes = true
|
||||||
|
} else if table == "stats" {
|
||||||
|
exclude_stats = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dbPath := dir + config.GetName() + "_" + time.Now().Format("20060102-200203") + ".db"
|
||||||
|
|
||||||
|
backupDb, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = backupDb.AutoMigrate(
|
||||||
|
&model.Setting{},
|
||||||
|
&model.Tls{},
|
||||||
|
&model.Inbound{},
|
||||||
|
&model.Outbound{},
|
||||||
|
&model.Endpoint{},
|
||||||
|
&model.User{},
|
||||||
|
&model.Stats{},
|
||||||
|
&model.Client{},
|
||||||
|
&model.Changes{},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var settings []model.Setting
|
||||||
|
var tls []model.Tls
|
||||||
|
var inbound []model.Inbound
|
||||||
|
var outbound []model.Outbound
|
||||||
|
var endpoint []model.Endpoint
|
||||||
|
var users []model.User
|
||||||
|
var clients []model.Client
|
||||||
|
var stats []model.Stats
|
||||||
|
var changes []model.Changes
|
||||||
|
|
||||||
|
// Perform scans and handle errors
|
||||||
|
if err := db.Model(&model.Setting{}).Scan(&settings).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := db.Model(&model.Tls{}).Scan(&tls).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := db.Model(&model.Inbound{}).Scan(&inbound).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := db.Model(&model.Outbound{}).Scan(&outbound).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := db.Model(&model.Endpoint{}).Scan(&endpoint).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := db.Model(&model.User{}).Scan(&users).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := db.Model(&model.Client{}).Scan(&clients).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save each model
|
||||||
|
for _, mdl := range []interface{}{settings, tls, inbound, outbound, endpoint, users, clients} {
|
||||||
|
if err := backupDb.Save(mdl).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !exclude_stats {
|
||||||
|
if err := db.Model(&model.Stats{}).Scan(&stats).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := backupDb.Save(stats).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !exclude_changes {
|
||||||
|
if err := db.Model(&model.Changes{}).Scan(&changes).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := backupDb.Save(changes).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update WAL
|
||||||
|
err = backupDb.Exec("PRAGMA wal_checkpoint;").Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bdb, _ := backupDb.DB()
|
||||||
|
bdb.Close()
|
||||||
|
|
||||||
|
// Open the file for reading
|
||||||
|
file, err := os.Open(dbPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
defer os.Remove(dbPath)
|
||||||
|
|
||||||
|
// Read the file contents
|
||||||
|
fileContents, err := io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileContents, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ImportDB(file multipart.File) error {
|
||||||
|
// Check if the file is a SQLite database
|
||||||
|
isValidDb, err := IsSQLiteDB(file)
|
||||||
|
if err != nil {
|
||||||
|
return common.NewErrorf("Error checking db file format: %v", err)
|
||||||
|
}
|
||||||
|
if !isValidDb {
|
||||||
|
return common.NewError("Invalid db file format")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the file reader to the beginning
|
||||||
|
_, err = file.Seek(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return common.NewErrorf("Error resetting file reader: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the file as temporary file
|
||||||
|
tempPath := fmt.Sprintf("%s.temp", config.GetDBPath())
|
||||||
|
// Remove the existing fallback file (if any) before creating one
|
||||||
|
_, err = os.Stat(tempPath)
|
||||||
|
if err == nil {
|
||||||
|
errRemove := os.Remove(tempPath)
|
||||||
|
if errRemove != nil {
|
||||||
|
return common.NewErrorf("Error removing existing temporary db file: %v", errRemove)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create the temporary file
|
||||||
|
tempFile, err := os.Create(tempPath)
|
||||||
|
if err != nil {
|
||||||
|
return common.NewErrorf("Error creating temporary db file: %v", err)
|
||||||
|
}
|
||||||
|
defer tempFile.Close()
|
||||||
|
|
||||||
|
// Remove temp file before returning
|
||||||
|
defer os.Remove(tempPath)
|
||||||
|
|
||||||
|
// Close old DB
|
||||||
|
old_db, _ := db.DB()
|
||||||
|
old_db.Close()
|
||||||
|
|
||||||
|
// Save uploaded file to temporary file
|
||||||
|
_, err = io.Copy(tempFile, file)
|
||||||
|
if err != nil {
|
||||||
|
return common.NewErrorf("Error saving db: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we can init db or not
|
||||||
|
newDb, err := gorm.Open(sqlite.Open(tempPath), &gorm.Config{})
|
||||||
|
if err != nil {
|
||||||
|
return common.NewErrorf("Error checking db: %v", err)
|
||||||
|
}
|
||||||
|
newDb_db, _ := newDb.DB()
|
||||||
|
newDb_db.Close()
|
||||||
|
|
||||||
|
// Backup the current database for fallback
|
||||||
|
fallbackPath := fmt.Sprintf("%s.backup", config.GetDBPath())
|
||||||
|
// Remove the existing fallback file (if any)
|
||||||
|
_, err = os.Stat(fallbackPath)
|
||||||
|
if err == nil {
|
||||||
|
errRemove := os.Remove(fallbackPath)
|
||||||
|
if errRemove != nil {
|
||||||
|
return common.NewErrorf("Error removing existing fallback db file: %v", errRemove)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Move the current database to the fallback location
|
||||||
|
err = os.Rename(config.GetDBPath(), fallbackPath)
|
||||||
|
if err != nil {
|
||||||
|
return common.NewErrorf("Error backing up temporary db file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the temporary file before returning
|
||||||
|
defer os.Remove(fallbackPath)
|
||||||
|
|
||||||
|
// Move temp to DB path
|
||||||
|
err = os.Rename(tempPath, config.GetDBPath())
|
||||||
|
if err != nil {
|
||||||
|
errRename := os.Rename(fallbackPath, config.GetDBPath())
|
||||||
|
if errRename != nil {
|
||||||
|
return common.NewErrorf("Error moving db file and restoring fallback: %v", errRename)
|
||||||
|
}
|
||||||
|
return common.NewErrorf("Error moving db file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migrate DB
|
||||||
|
migration.MigrateDb()
|
||||||
|
err = InitDB(config.GetDBPath())
|
||||||
|
if err != nil {
|
||||||
|
errRename := os.Rename(fallbackPath, config.GetDBPath())
|
||||||
|
if errRename != nil {
|
||||||
|
return common.NewErrorf("Error migrating db and restoring fallback: %v", errRename)
|
||||||
|
}
|
||||||
|
return common.NewErrorf("Error migrating db: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restart app
|
||||||
|
err = SendSighup()
|
||||||
|
if err != nil {
|
||||||
|
return common.NewErrorf("Error restarting app: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsSQLiteDB(file io.Reader) (bool, error) {
|
||||||
|
signature := []byte("SQLite format 3\x00")
|
||||||
|
buf := make([]byte, len(signature))
|
||||||
|
_, err := file.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return bytes.Equal(buf, signature), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendSighup() error {
|
||||||
|
// Get the current process
|
||||||
|
process, err := os.FindProcess(os.Getpid())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send SIGHUP to the current process
|
||||||
|
go func() {
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
err := process.Signal(syscall.SIGHUP)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("send signal SIGHUP failed:", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<LogVue
|
<LogVue v-model="logModal.visible" :control="logModal" :visible="logModal.visible" />
|
||||||
v-model="logModal.visible"
|
<Backup v-model="backupModal.visible" :control="backupModal" :visible="backupModal.visible" />
|
||||||
:visible="logModal.visible"
|
|
||||||
@close="closeLogs"
|
|
||||||
/>
|
|
||||||
<v-container class="fill-height" :loading="loading">
|
<v-container class="fill-height" :loading="loading">
|
||||||
<v-responsive :class="reloadItems.length>0 ? 'fill-height text-center' : 'align-center'" >
|
<v-responsive :class="reloadItems.length>0 ? 'fill-height text-center' : 'align-center'" >
|
||||||
<v-row class="d-flex align-center justify-center">
|
<v-row class="d-flex align-center justify-center">
|
||||||
@@ -46,6 +43,8 @@
|
|||||||
</v-row>
|
</v-row>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
|
<v-btn variant="tonal" hide-details style="margin-inline-start: 10px;" @click="backupModal.visible = true">{{ $t('main.backup.title') }} <v-icon icon="mdi-backup-restore" /></v-btn>
|
||||||
|
<v-btn variant="tonal" hide-details style="margin-inline-start: 10px;" @click="logModal.visible = true">{{ $t('basic.log.title') }} <v-icon icon="mdi-list-box-outline" /></v-btn>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
<v-row>
|
<v-row>
|
||||||
@@ -86,18 +85,8 @@
|
|||||||
<v-col cols="3">S-UI</v-col>
|
<v-col cols="3">S-UI</v-col>
|
||||||
<v-col cols="9">
|
<v-col cols="9">
|
||||||
<v-chip density="compact" color="blue">
|
<v-chip density="compact" color="blue">
|
||||||
<v-tooltip activator="parent" location="top">
|
|
||||||
{{ $t('main.info.threads') }}: {{ tilesData.sys?.appThreads }}<br />
|
|
||||||
{{ $t('main.info.memory') }}: {{ HumanReadable.sizeFormat(tilesData.sys?.appMem) }}
|
|
||||||
</v-tooltip>
|
|
||||||
v{{ tilesData.sys?.appVersion }}
|
v{{ tilesData.sys?.appVersion }}
|
||||||
</v-chip>
|
</v-chip>
|
||||||
<v-chip density="compact" color="transparent" style="cursor: pointer;" @click="openLogs()">
|
|
||||||
<v-tooltip activator="parent" location="top">
|
|
||||||
{{ $t('basic.log.title') + " - S-UI" }}
|
|
||||||
</v-tooltip>
|
|
||||||
<v-icon icon="mdi-list-box-outline" color="blue" />
|
|
||||||
</v-chip>
|
|
||||||
</v-col>
|
</v-col>
|
||||||
<v-col cols="3">{{ $t('main.info.uptime') }}</v-col>
|
<v-col cols="3">{{ $t('main.info.uptime') }}</v-col>
|
||||||
<v-col cols="9">{{ HumanReadable.formatSecond(tilesData.uptime) }}</v-col>
|
<v-col cols="9">{{ HumanReadable.formatSecond(tilesData.uptime) }}</v-col>
|
||||||
@@ -166,6 +155,7 @@ import History from '@/components/tiles/History.vue'
|
|||||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||||
import { i18n } from '@/locales'
|
import { i18n } from '@/locales'
|
||||||
import LogVue from '@/layouts/modals/Logs.vue'
|
import LogVue from '@/layouts/modals/Logs.vue'
|
||||||
|
import Backup from '@/layouts/modals/Backup.vue'
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const menu = ref(false)
|
const menu = ref(false)
|
||||||
@@ -235,17 +225,9 @@ onBeforeUnmount(() => {
|
|||||||
stopTimer()
|
stopTimer()
|
||||||
})
|
})
|
||||||
|
|
||||||
const logModal = ref({
|
const logModal = ref({ visible: false })
|
||||||
visible: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
const openLogs = () => {
|
const backupModal = ref({ visible: false })
|
||||||
logModal.value.visible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const closeLogs = () => {
|
|
||||||
logModal.value.visible = false
|
|
||||||
}
|
|
||||||
|
|
||||||
const restartSingbox = async () => {
|
const restartSingbox = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<template>
|
||||||
|
<v-dialog transition="dialog-bottom-transition" width="90%" max-width="500">
|
||||||
|
<v-card class="rounded-lg">
|
||||||
|
<v-card-title>
|
||||||
|
<v-row>
|
||||||
|
<v-col>{{ $t('main.backup.title') }}</v-col>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-col cols="auto">
|
||||||
|
<v-icon icon="mdi-close" @click="control.visible = false" />
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-title>
|
||||||
|
<v-divider></v-divider>
|
||||||
|
<v-card-text>
|
||||||
|
<v-row>
|
||||||
|
<v-col cols="auto">
|
||||||
|
<v-checkbox v-model="exclude" :label="$t('main.backup.exclStats')" value="stats" hide-details></v-checkbox>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="auto">
|
||||||
|
<v-checkbox v-model="exclude" :label="$t('main.backup.exclChanges')" value="changes" hide-details></v-checkbox>
|
||||||
|
</v-col>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-col cols="auto" align-self="center">
|
||||||
|
<v-btn color="primary" @click="backup()" hide-details>{{ $t('main.backup.backup') }}</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
<v-row>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-col cols="auto" align-self="center">
|
||||||
|
<v-btn color="primary" @click="restore()" hide-details>{{ $t('main.backup.restore') }}</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import HttpUtils from '@/plugins/httputil'
|
||||||
|
export default {
|
||||||
|
props: ['control', 'visible'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
exclude: ["stats", "changes"],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
backup() {
|
||||||
|
const excludeOption = this.exclude.length>0 ? '?exclude=' +this.exclude.join(',') : ''
|
||||||
|
window.location.href = 'api/getdb' + excludeOption
|
||||||
|
},
|
||||||
|
restore() {
|
||||||
|
const fileInput = document.createElement('input')
|
||||||
|
fileInput.type = 'file'
|
||||||
|
fileInput.accept = '.db'
|
||||||
|
|
||||||
|
fileInput.addEventListener('change', async (event: Event) => {
|
||||||
|
const inputElement = event.target as HTMLInputElement
|
||||||
|
const dbFile = inputElement.files ? inputElement.files[0] : null
|
||||||
|
|
||||||
|
if (dbFile) {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('db', dbFile)
|
||||||
|
|
||||||
|
this.control.visible = false
|
||||||
|
|
||||||
|
const uploadMsg = await HttpUtils.post('api/importdb', formData, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (uploadMsg.success) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
location.reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
fileInput.click()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
visible(v) {
|
||||||
|
if (v) {
|
||||||
|
this.exclude = ["stats", "changes"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
<v-col>{{ $t('basic.log.title') }}</v-col>
|
<v-col>{{ $t('basic.log.title') }}</v-col>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
<v-col cols="auto">
|
<v-col cols="auto">
|
||||||
<v-icon icon="mdi-close" @click="$emit('close')" />
|
<v-icon icon="mdi-close" @click="control.visible = false" />
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-title>
|
</v-card-title>
|
||||||
@@ -48,10 +48,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import HttpUtils from '@/plugins/httputil';
|
import HttpUtils from '@/plugins/httputil'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['visible'],
|
props: ['control', 'visible'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -77,11 +77,11 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
visible(newValue) {
|
visible(v) {
|
||||||
this.lines = []
|
this.lines = []
|
||||||
this.logLevel = 'info'
|
this.logLevel = 'info'
|
||||||
this.logCount = 10
|
this.logCount = 10
|
||||||
if (newValue) {
|
if (v) {
|
||||||
this.loadData()
|
this.loadData()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -72,6 +72,13 @@ export default {
|
|||||||
threads: "Threads",
|
threads: "Threads",
|
||||||
memory: "Memory",
|
memory: "Memory",
|
||||||
running: "Running"
|
running: "Running"
|
||||||
|
},
|
||||||
|
backup: {
|
||||||
|
title: "Backup & Restore",
|
||||||
|
backup: "Download Backup",
|
||||||
|
restore: "Restore",
|
||||||
|
exclStats: "Exclude graphs",
|
||||||
|
exclChanges: "Exclude changes",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
objects: {
|
objects: {
|
||||||
|
|||||||
@@ -72,7 +72,14 @@ export default {
|
|||||||
threads: "نخها",
|
threads: "نخها",
|
||||||
memory: "حافظه",
|
memory: "حافظه",
|
||||||
running: "اجرا"
|
running: "اجرا"
|
||||||
}
|
},
|
||||||
|
backup: {
|
||||||
|
title: "پشتیبانگیری و بازیابی",
|
||||||
|
backup: "دریافت پشتیبان",
|
||||||
|
restore: "بازیابی",
|
||||||
|
exclStats: "بدون گرافها",
|
||||||
|
exclChanges: "بدون تغییرات",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
objects: {
|
objects: {
|
||||||
inbound: "ورودی",
|
inbound: "ورودی",
|
||||||
|
|||||||
@@ -72,6 +72,13 @@ export default {
|
|||||||
threads: "Потоки",
|
threads: "Потоки",
|
||||||
memory: "Память",
|
memory: "Память",
|
||||||
running: "Работает"
|
running: "Работает"
|
||||||
|
},
|
||||||
|
backup: {
|
||||||
|
title: "Резервное копирование и восстановление",
|
||||||
|
backup: "Скачать резервную копию",
|
||||||
|
restore: "Восстановить",
|
||||||
|
exclStats: "Исключить графики",
|
||||||
|
exclChanges: "Исключить изменения",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
objects: {
|
objects: {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { title } from "process";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
message: "Chào mừng OHB",
|
message: "Chào mừng OHB",
|
||||||
success: "Thành công",
|
success: "Thành công",
|
||||||
@@ -70,6 +72,13 @@ export default {
|
|||||||
threads: "Luồng",
|
threads: "Luồng",
|
||||||
memory: "Bộ nhớ",
|
memory: "Bộ nhớ",
|
||||||
running: "Đang chạy"
|
running: "Đang chạy"
|
||||||
|
},
|
||||||
|
backup: {
|
||||||
|
title: "Sao lưu và khôi phục",
|
||||||
|
backup: "Tải xuống bản sao lưu",
|
||||||
|
restore: "Khôi phục",
|
||||||
|
exclStats: "Loại trừ các biểu đồ",
|
||||||
|
exclChanges: "Loại trừ các thay đổi",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
objects: {
|
objects: {
|
||||||
|
|||||||
@@ -70,7 +70,14 @@ export default {
|
|||||||
threads: "线程",
|
threads: "线程",
|
||||||
memory: "内存",
|
memory: "内存",
|
||||||
running: "运行状态"
|
running: "运行状态"
|
||||||
}
|
},
|
||||||
|
backup: {
|
||||||
|
title: "备份与恢复",
|
||||||
|
backup: "下载备份",
|
||||||
|
restore: "恢复",
|
||||||
|
exclStats: "排除图表数据",
|
||||||
|
exclChanges: "排除变更数据",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
objects: {
|
objects: {
|
||||||
inbound: "入站",
|
inbound: "入站",
|
||||||
|
|||||||
@@ -71,7 +71,14 @@ export default {
|
|||||||
threads: "線程",
|
threads: "線程",
|
||||||
memory: "內存",
|
memory: "內存",
|
||||||
running: "運行狀態"
|
running: "運行狀態"
|
||||||
}
|
},
|
||||||
|
backup: {
|
||||||
|
title: "備份與恢復",
|
||||||
|
backup: "下載備份",
|
||||||
|
restore: "恢復",
|
||||||
|
exclStats: "排除圖表記錄",
|
||||||
|
exclChanges: "排除更改記錄",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
objects: {
|
objects: {
|
||||||
inbound: "入站",
|
inbound: "入站",
|
||||||
|
|||||||
Reference in New Issue
Block a user